Skip to content

Commit

Permalink
Fix for #959: check aliased annotations for attribute methods
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Dec 2, 2019
1 parent 8590c03 commit 1780c16
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -52,6 +51,7 @@
import org.codehaus.groovy.ast.tools.GeneralUtils;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.transform.ASTTransformation;
import org.codehaus.groovy.transform.AnnotationCollectorTransform;
import org.codehaus.groovy.transform.GroovyASTTransformation;
import org.codehaus.groovy.transform.trait.Traits;
import org.codehaus.jdt.groovy.internal.compiler.ast.JDTClassNode;
Expand Down Expand Up @@ -283,6 +283,24 @@ public static List<ImportNode> getAllImportNodes(ModuleNode moduleNode) {
return importNodes;
}

public static MethodNode getAnnotationMethod(ClassNode annotationType, String methodName) {
MethodNode meth = annotationType.getMethod(methodName, Parameter.EMPTY_ARRAY);
if (meth != null) {
return meth;
}

if (getAnnotations(annotationType.redirect(), "groovy.transform.AnnotationCollector").findFirst().isPresent()) {
for (AnnotationNode aliasedNode : AnnotationCollectorTransform.getMeta(annotationType.redirect())) {
meth = getAnnotationMethod(aliasedNode.getClassNode(), methodName);
if (meth != null) {
return meth;
}
}
}

return null;
}

public static MethodNode getMethod(ClassNode declaringType, String methodName, Parameter... parameters) {
MethodNode meth = declaringType.getMethod(methodName, parameters);
if (meth != null) {
Expand All @@ -291,7 +309,7 @@ public static MethodNode getMethod(ClassNode declaringType, String methodName, P
// concrete types (without mixins/traits) return all methods from getMethod(String, Parameter[])
if (declaringType.isAbstract() || declaringType.isInterface() || implementsTrait(declaringType)) {
Set<ClassNode> done = new HashSet<>(Collections.singleton(declaringType));
Queue<ClassNode> todo = new LinkedList<>();
LinkedList<ClassNode> todo = new LinkedList<>();
ClassNode type = declaringType;
do {
ClassNode supa = type.getSuperClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -674,12 +674,12 @@ private void visitAnnotationKeys(AnnotationNode node) {
ClassNode type = node.getClassNode();
VariableScope scope = scopes.getLast();
for (String name : node.getMembers().keySet()) {
MethodNode meth = type.getMethod(name, NO_PARAMETERS);
TypeLookupResult noLookup;
ASTNode attr;
TypeLookupResult noLookup;
MethodNode meth = GroovyUtils.getAnnotationMethod(type, name);
if (meth != null) {
attr = meth; // no Groovy AST node exists for name
noLookup = new TypeLookupResult(meth.getReturnType(), type, meth, TypeConfidence.EXACT, scope);
noLookup = new TypeLookupResult(meth.getReturnType(), meth.getDeclaringClass(), meth, TypeConfidence.EXACT, scope);
} else {
attr = new ConstantExpression(name);
// this is very rough; it only works for an attribute that directly follows '('
Expand All @@ -688,7 +688,8 @@ private void visitAnnotationKeys(AnnotationNode node) {
noLookup = new TypeLookupResult(VariableScope.VOID_CLASS_NODE, type, null, TypeConfidence.UNKNOWN, scope);
}
noLookup.enclosingAnnotation = node; // set context for requestor
if (notifyRequestor(attr, requestor, noLookup) != VisitStatus.CONTINUE) break;
VisitStatus status = notifyRequestor(attr, requestor, noLookup);
if (status != VisitStatus.CONTINUE) break;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ final class CodeSelectAttributesTests extends BrowsingTestSuite {
addJavaSource '''\
|import java.lang.annotation.*;
|@Target(ElementType.TYPE)
|@interface Anno {
|@interface A {
| String one();
| String two();
|}
|'''.stripMargin(), 'Anno'
|'''.stripMargin(), 'A'

String source = '''\
|@Anno(one='1')
|@A(one='1')
|class C {
|}
|'''.stripMargin()
Expand All @@ -55,6 +55,28 @@ final class CodeSelectAttributesTests extends BrowsingTestSuite {
assert elem.inferredElement instanceof MethodNode
}

@Test // https://github.com/groovy/groovy-eclipse/issues/959
void testCodeSelectOnAttributeName3() {
addGroovySource '''\
|@groovy.transform.EqualsAndHashCode
|@groovy.transform.AnnotationCollector
|@interface A {
|}
|'''.stripMargin(), 'A'

buildProject()

String source = '''\
|@A(excludes='temporary')
|class C {
| def temporary
|}
|'''.stripMargin()

def elem = assertCodeSelect([source], 'excludes')
assert elem.inferredElement instanceof MethodNode
}

@Test
void testCodeSelectOnAttributeValue1() {
String source = '''\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,50 @@ final class SemanticHighlightingTests extends GroovyEclipseTestSuite {
new HighlightedTypedPosition(contents.indexOf('method'), 6, METHOD))
}

@Test // https://github.com/groovy/groovy-eclipse/issues/959
void testAnnoElems7() {
String contents = '''\
|@groovy.transform.EqualsAndHashCode
|@groovy.transform.AnnotationCollector
|@interface A {
|}
|@A(excludes = 'temporary')
|class C {
| def temporary
|}
|'''.stripMargin()

assertHighlighting(contents,
new HighlightedTypedPosition(contents.indexOf('A '), 1, ANNOTATION),
new HighlightedTypedPosition(contents.indexOf('excludes'), 8, TAG_KEY),
new HighlightedTypedPosition(contents.indexOf('C '), 1, CLASS),
new HighlightedTypedPosition(contents.lastIndexOf('temporary'), 9, FIELD))
}

@Test // https://github.com/groovy/groovy-eclipse/issues/959
void testAnnoElems8() {
addGroovySource '''\
|@groovy.transform.EqualsAndHashCode
|@groovy.transform.AnnotationCollector
|@interface A {
|}
|'''.stripMargin(), 'A'

buildProject()

String contents = '''\
|@A(excludes = 'temporary')
|class C {
| def temporary
|}
|'''.stripMargin()

assertHighlighting(contents,
new HighlightedTypedPosition(contents.indexOf('excludes'), 8, TAG_KEY),
new HighlightedTypedPosition(contents.indexOf('C'), 1, CLASS),
new HighlightedTypedPosition(contents.lastIndexOf('temporary'), 9, FIELD))
}

@Test
void testGString1() {
String contents = '''\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ protected void visitAnnotation(AnnotationNode annotation) {
for (Map.Entry<String, Expression> pair : annotation.getMembers().entrySet()) {
String name = pair.getKey();
Expression expr = pair.getValue();
check(annotation.getClassNode().getMethod(name, Parameter.EMPTY_ARRAY),
check(GroovyUtils.getAnnotationMethod(annotation.getClassNode(), name),
start/*expr.getStart() - name.length() - 1*/, expr.getStart() - 1);
/*expr.visit(this);*/
start = expr.getEnd() + 1;
Expand Down

0 comments on commit 1780c16

Please sign in to comment.