[groovy] support constant values via field references (IDEA-186646)

This commit is contained in:
Daniil Ovchinnikov
2018-02-14 17:26:29 +03:00
parent 380cad9239
commit f71925909e
5 changed files with 109 additions and 76 deletions
@@ -1,21 +1,7 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.impl;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -27,6 +13,9 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrRefere
import java.util.Collections;
import java.util.List;
import static org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtilKt.findDeclaredDetachedValue;
import static org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtilKt.getArrayValue;
/**
* @author Max Medvedev
*/
@@ -146,20 +135,18 @@ public class GrAnnotationUtil {
}
public static List<String> getStringArrayValue(@NotNull PsiAnnotation annotation, @NotNull String attributeName, boolean declared) {
PsiAnnotationMemberValue value =
declared ? annotation.findDeclaredAttributeValue(attributeName) : annotation.findAttributeValue(attributeName);
if (value instanceof PsiArrayInitializerMemberValue) {
return ContainerUtil.mapNotNull(((PsiArrayInitializerMemberValue)value).getInitializers(), memberValue -> {
String string = getString(memberValue);
return StringUtil.isEmpty(string) ? null : string;
});
}
else {
String string = getString(value);
if (!StringUtil.isEmpty(string)) {
return Collections.singletonList(string);
}
}
return Collections.emptyList();
PsiAnnotationMemberValue value = findDetachedAttributeValue(annotation, attributeName, declared);
if (value == null) return Collections.emptyList();
return getArrayValue(value, AnnotationUtil::getStringAttributeValue);
}
@Nullable
private static PsiAnnotationMemberValue findDetachedAttributeValue(@NotNull PsiAnnotation annotation,
@Nullable String attributeName,
boolean declared) {
PsiAnnotationMemberValue declaredValue = findDeclaredDetachedValue(annotation, attributeName);
if (declaredValue != null) return declaredValue;
if (declared) return null;
return annotation.findAttributeValue(attributeName);
}
}
@@ -1,23 +1,10 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.impl
import com.intellij.codeInsight.AnnotationUtil
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiAnnotationMemberValue
import com.intellij.psi.PsiArrayInitializerMemberValue
import com.intellij.psi.PsiLiteral
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList
@@ -25,6 +12,16 @@ fun PsiAnnotation.findDeclaredDetachedValue(attributeName: String?): PsiAnnotati
return AnnotationUtil.findDeclaredAttribute(this, attributeName)?.detachedValue
}
internal fun <T : Any> PsiAnnotationMemberValue.getArrayValue(computeValue: (PsiAnnotationMemberValue) -> T?): List<T> {
if (this is PsiArrayInitializerMemberValue) {
return initializers.mapNotNull(computeValue)
}
else {
val value = computeValue(this) ?: return emptyList()
return listOf(value)
}
}
fun PsiAnnotationMemberValue?.booleanValue() = (this as? PsiLiteral)?.value as? Boolean
fun PsiAnnotationMemberValue?.stringValue() = (this as? PsiLiteral)?.value as? String
@@ -1,26 +1,14 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.util;
import com.intellij.psi.PsiConstantEvaluationHelper;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.impl.ConstantExpressionEvaluator;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrParenthesizedExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
/**
@@ -36,6 +24,12 @@ public class GroovyConstantExpressionEvaluator implements ConstantExpressionEval
if (expression instanceof GrLiteral) {
return ((GrLiteral)expression).getValue();
}
if (expression instanceof GrReferenceExpression) {
PsiElement resolved = ((GrReferenceExpression)expression).resolve();
if (resolved instanceof PsiField) {
return ((PsiField)resolved).computeConstantValue();
}
}
return null;
}
@@ -0,0 +1,61 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.resolve
import com.intellij.testFramework.LightProjectDescriptor
import groovy.transform.CompileStatic
import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor
import org.jetbrains.plugins.groovy.LightGroovyTestCase
import static org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil.getStringArrayValue
import static org.jetbrains.plugins.groovy.util.TestUtils.disableAstLoading
@CompileStatic
class ConstantExpressionTest extends LightGroovyTestCase {
final LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST
private void addJavaAnnotation() {
fixture.addClass '''\
package com.foo;
public @interface MyJavaAnnotation {
String[] stringArrayValue() default {};
String stringValue() default "default string";
}
'''
}
private void addJavaConstants() {
fixture.addClass '''\
package com.foo;
public interface Constants {
String HELLO = "java hello";
String WORLD = "java world";
String COMPOUND = HELLO + " " + WORLD;
}
'''
}
void 'test annotation value from java'() {
addJavaAnnotation()
addJavaConstants()
fixture.addFileToProject '_.groovy', '''\
import com.foo.Constants
import com.foo.MyJavaAnnotation
@MyJavaAnnotation(stringArrayValue = [
Constants.HELLO,
Constants.WORLD,
Constants.COMPOUND,
"literal"
])
class GroovyClass {}
'''
disableAstLoading project, testRootDisposable
def clazz = fixture.findClass 'GroovyClass'
def annotation = clazz.modifierList.findAnnotation 'com.foo.MyJavaAnnotation'
def values = getStringArrayValue annotation, 'stringArrayValue', false
assertOrderedEquals(["java hello", "java world", "java hello java world", "literal"], values)
}
}
@@ -1,36 +1,26 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.util;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFileFilter;
import com.intellij.pom.PomDeclarationSearcher;
import com.intellij.pom.PomTarget;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture;
import com.intellij.util.CollectConsumer;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
@@ -281,4 +271,8 @@ public abstract class TestUtils {
//workaround for IDEA-148973: Groovy static compilation fails to compile calls of overriding methods with covariant type in interfaces
return aClass.getMethods();
}
public static void disableAstLoading(@NotNull Project project, @NotNull Disposable parent) {
PsiManagerEx.getInstanceEx(project).setAssertOnFileLoadingFilter(VirtualFileFilter.ALL, parent);
}
}