Skip to content

Commit ec16696

Browse files
committed
Fix type detection of nested annotations
This commit fixes type detection of java annotations that are nested within the annotated class. A simple example of this can be seen in the test class `SimpleNestedAnnotation.java`.
1 parent 8bea251 commit ec16696

File tree

29 files changed

+846
-7
lines changed

29 files changed

+846
-7
lines changed

src/core/lombok/core/TypeResolver.java

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
*/
2222
package lombok.core;
2323

24+
import java.util.ArrayList;
25+
import java.util.Arrays;
2426
import java.util.List;
2527

2628
import lombok.core.AST.Kind;
@@ -32,7 +34,7 @@
3234
* and this importer also can't find inner types from superclasses/interfaces.
3335
*/
3436
public class TypeResolver {
35-
private ImportList imports;
37+
private final ImportList imports;
3638

3739
/**
3840
* Creates a new TypeResolver that can be used to resolve types in a source file with the given package and import statements.
@@ -53,6 +55,14 @@ public String typeRefToFullyQualifiedName(LombokNode<?, ?, ?> context, TypeLibra
5355
// When asking if 'lombok.Getter' could possibly be referring to 'lombok.Getter', the answer is obviously yes.
5456
if (qualifieds.contains(typeRef)) return LombokInternalAliasing.processAliases(typeRef);
5557

58+
// Types defined on the containing type, or on any of its parents in the source file, take precedence over any imports
59+
String nestedTypeFqn = new NestedTypeFinder(typeRef).findNestedType(context);
60+
if (nestedTypeFqn != null) {
61+
// we found a nestedType - check edge case where nestedType is in type library
62+
qualifieds = library.toQualifieds(nestedTypeFqn);
63+
return qualifieds == null || !qualifieds.contains(nestedTypeFqn) ? null : nestedTypeFqn;
64+
}
65+
5666
// When asking if 'Getter' could possibly be referring to 'lombok.Getter' if 'import lombok.Getter;' is in the source file, the answer is yes.
5767
int firstDot = typeRef.indexOf('.');
5868
if (firstDot == -1) firstDot = typeRef.length();
@@ -96,12 +106,7 @@ public String typeRefToFullyQualifiedName(LombokNode<?, ?, ?> context, TypeLibra
96106
continue mainLoop;
97107
}
98108

99-
if (n.getKind() == Kind.TYPE || n.getKind() == Kind.COMPILATION_UNIT) {
100-
for (LombokNode<?, ?, ?> child : n.down()) {
101-
// Inner class that's visible to us has 'typeRef' as name, so that's the one being referred to, not one of our type library classes.
102-
if (child.getKind() == Kind.TYPE && firstTypeRef.equals(child.getName())) return null;
103-
}
104-
}
109+
// don't need to check for inner class shadowing, we already do that in NestedTypeFinder
105110

106111
n = n.directUp();
107112
}
@@ -113,4 +118,90 @@ public String typeRefToFullyQualifiedName(LombokNode<?, ?, ?> context, TypeLibra
113118
// No star import matches either.
114119
return null;
115120
}
121+
122+
/**
123+
* Traverse up the containing types until we find a match, or hit the package. At each level,
124+
* we check for a type with matching name (including traversing into child types if typeRef is
125+
* not a simple name).
126+
*/
127+
private static class NestedTypeFinder {
128+
129+
private final String typeRef;
130+
private final List<String> typeRefElements;
131+
132+
public NestedTypeFinder(String typeRef) {
133+
this.typeRef = typeRef;
134+
this.typeRefElements = Arrays.asList(typeRef.split("\\.", -1));
135+
}
136+
137+
/** Finds a matching nestedType and returns its FQN, or {@code null} if no match found. */
138+
public String findNestedType(LombokNode<?, ?, ?> context) {
139+
LombokNode<?, ?, ?> nearestType = traverseUpToNearestType(context);
140+
if (nearestType == null) {
141+
return null;
142+
}
143+
144+
boolean found = findTypeRef(nearestType, 0);
145+
if (found) {
146+
// return FQN
147+
return getFoundFqn(nearestType);
148+
}
149+
150+
return findNestedType(nearestType.up());
151+
}
152+
153+
/** Traverse up to the nearest type or package (including {@code node} if it is a type). */
154+
private LombokNode<?, ?, ?> traverseUpToNearestType(LombokNode<?, ?, ?> node) {
155+
if (node == null) {
156+
return null; // parent is null once we hit the package
157+
}
158+
if (node.getKind() == Kind.COMPILATION_UNIT || node.getKind() == Kind.TYPE) {
159+
return node;
160+
}
161+
return traverseUpToNearestType(node.up());
162+
}
163+
164+
/** Check whether {@code typeRef[nameIndex]} exists as a child of {@code typeNode}. */
165+
private boolean findTypeRef(LombokNode<?, ?, ?> typeNode, int nameIndex) {
166+
for (LombokNode<?, ?, ?> child : typeNode.down()) {
167+
if (child.getKind() == Kind.TYPE) {
168+
// check if this node matches the first element
169+
if (child.getName().equals(typeRefElements.get(nameIndex))) {
170+
if (nameIndex == typeRefElements.size() - 1) {
171+
// we've found a match as we've matched all elements of typeRef
172+
return true;
173+
}
174+
// otherwise, check match of remaining typeRef elements
175+
boolean found = findTypeRef(child, nameIndex + 1);
176+
if (found) {
177+
return true;
178+
}
179+
}
180+
}
181+
}
182+
return false;
183+
}
184+
185+
private String getFoundFqn(LombokNode<?, ?, ?> typeNode) {
186+
List<String> elements = new ArrayList<String>();
187+
while (typeNode.getKind() != Kind.COMPILATION_UNIT) {
188+
elements.add(typeNode.getName());
189+
typeNode = traverseUpToNearestType(typeNode.up());
190+
}
191+
192+
String pkg = typeNode.getPackageDeclaration();
193+
StringBuilder fqn;
194+
if (pkg == null) { // pkg can be null e.g. if top-level type is in default package
195+
fqn = new StringBuilder(elements.size() * 10);
196+
} else {
197+
fqn = new StringBuilder(pkg.length() + elements.size() * 10);
198+
fqn.append(pkg).append('.');
199+
}
200+
for (int i = elements.size() - 1; i >= 0; i--) {
201+
fqn.append(elements.get(i)).append('.');
202+
}
203+
fqn.append(typeRef);
204+
return fqn.toString();
205+
}
206+
}
116207
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package test.lombok.other;
2+
3+
import java.lang.annotation.Retention;
4+
import java.lang.annotation.RetentionPolicy;
5+
6+
public class Other {
7+
8+
@Retention(RetentionPolicy.RUNTIME)
9+
public @interface TA {
10+
}
11+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package test.lombok;
2+
3+
import java.lang.annotation.Retention;
4+
import java.lang.annotation.RetentionPolicy;
5+
import test.lombok.NestedClassAndAnnotationNestedInImportedSibling.Other.OtherNested.TA;
6+
7+
public class NestedClassAndAnnotationNestedInImportedSibling {
8+
9+
public static class Inner {
10+
11+
@TA
12+
private final int someVal;
13+
14+
@java.lang.SuppressWarnings("all")
15+
public Inner(@TA final int someVal) {
16+
this.someVal = someVal;
17+
}
18+
19+
@TA
20+
@java.lang.SuppressWarnings("all")
21+
public int getSomeVal() {
22+
return this.someVal;
23+
}
24+
}
25+
26+
public static class Other {
27+
28+
public static class OtherNested {
29+
30+
@Retention(RetentionPolicy.RUNTIME)
31+
public @interface TA {
32+
}
33+
}
34+
}
35+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package test.lombok;
2+
3+
import java.lang.annotation.Retention;
4+
import java.lang.annotation.RetentionPolicy;
5+
6+
public class NestedClassAndAnnotationNestedInNonImportedSibling {
7+
8+
public static class Inner {
9+
@Other.OtherNested.TA
10+
private final int someVal;
11+
12+
@java.lang.SuppressWarnings("all")
13+
public Inner(@Other.OtherNested.TA final int someVal) {
14+
this.someVal = someVal;
15+
}
16+
17+
@Other.OtherNested.TA
18+
@java.lang.SuppressWarnings("all")
19+
public int getSomeVal() {
20+
return this.someVal;
21+
}
22+
}
23+
24+
public static class Other {
25+
26+
public static class OtherNested {
27+
@Retention(RetentionPolicy.RUNTIME)
28+
public @interface TA {
29+
}
30+
}
31+
}
32+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package test.lombok;
2+
3+
import java.lang.annotation.Retention;
4+
import java.lang.annotation.RetentionPolicy;
5+
6+
public class NestedClassAndNestedAnnotation {
7+
8+
public static class Inner {
9+
@TA
10+
private final int someVal;
11+
12+
@java.lang.SuppressWarnings("all")
13+
public Inner(@TA final int someVal) {
14+
this.someVal = someVal;
15+
}
16+
17+
@TA
18+
@java.lang.SuppressWarnings("all")
19+
public int getSomeVal() {
20+
return this.someVal;
21+
}
22+
}
23+
24+
@Retention(RetentionPolicy.RUNTIME)
25+
public @interface TA {
26+
}
27+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package test.lombok;
2+
3+
import java.lang.annotation.Retention;
4+
import java.lang.annotation.RetentionPolicy;
5+
import test.lombok.NestedClassAndNestedAnnotationImported.Other.OtherNested.TA;
6+
7+
public class NestedClassAndNestedAnnotationImported {
8+
9+
public static class Inner {
10+
@TA
11+
private final int someVal;
12+
13+
@java.lang.SuppressWarnings("all")
14+
public Inner(@TA final int someVal) {
15+
this.someVal = someVal;
16+
}
17+
18+
@TA
19+
@java.lang.SuppressWarnings("all")
20+
public int getSomeVal() {
21+
return this.someVal;
22+
}
23+
}
24+
25+
public static class Other {
26+
27+
public static class OtherNested {
28+
@Retention(RetentionPolicy.RUNTIME)
29+
public @interface TA {
30+
}
31+
}
32+
}
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package test.lombok;
2+
3+
import java.lang.annotation.Retention;
4+
import java.lang.annotation.RetentionPolicy;
5+
import test.lombok.other.Other;
6+
7+
public class NestedClassAndNestedAnnotationWithConflictingNestedImport {
8+
9+
public static class Inner {
10+
@Other.TA
11+
private final int someVal;
12+
13+
@java.lang.SuppressWarnings("all")
14+
public Inner(@Other.TA final int someVal) {
15+
this.someVal = someVal;
16+
}
17+
18+
@Other.TA
19+
@java.lang.SuppressWarnings("all")
20+
public int getSomeVal() {
21+
return this.someVal;
22+
}
23+
}
24+
25+
public static class Other {
26+
27+
@Retention(RetentionPolicy.RUNTIME)
28+
public @interface TA {
29+
}
30+
}
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package test.lombok;
2+
3+
import java.lang.annotation.Retention;
4+
import java.lang.annotation.RetentionPolicy;
5+
import test.lombok.other.Other.TA;
6+
7+
public class NestedClassAndNestedAnnotationWithOverridingNestedAnnotationImport {
8+
9+
public static class Inner {
10+
@TA
11+
private final int someVal;
12+
13+
@java.lang.SuppressWarnings("all")
14+
public Inner(@TA final int someVal) {
15+
this.someVal = someVal;
16+
}
17+
18+
@TA
19+
@java.lang.SuppressWarnings("all")
20+
public int getSomeVal() {
21+
return this.someVal;
22+
}
23+
}
24+
25+
public static class Other {
26+
27+
@Retention(RetentionPolicy.RUNTIME)
28+
public @interface TA {
29+
}
30+
}
31+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package test.lombok;
2+
3+
import java.lang.annotation.Retention;
4+
import java.lang.annotation.RetentionPolicy;
5+
6+
public class SimpleNestedAnnotation {
7+
8+
@TA
9+
private final int someVal;
10+
11+
@Retention(RetentionPolicy.RUNTIME)
12+
public @interface TA {
13+
}
14+
15+
@java.lang.SuppressWarnings("all")
16+
public SimpleNestedAnnotation(@TA final int someVal) {
17+
this.someVal = someVal;
18+
}
19+
20+
@TA
21+
@java.lang.SuppressWarnings("all")
22+
public int getSomeVal() {
23+
return this.someVal;
24+
}
25+
}

0 commit comments

Comments
 (0)