aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok
diff options
context:
space:
mode:
authorReinier Zwitserloot <reinier@zwitserloot.com>2019-01-22 04:28:18 +0100
committerReinier Zwitserloot <reinier@zwitserloot.com>2019-01-22 04:30:02 +0100
commitccd802503d8aa578be3f1f956d97b06a803de0aa (patch)
tree53bee7018a6dd79664ee9b711b7ca36146acb8c1 /src/core/lombok
parentba4e69bf30bf1c761b84e78dbec1fa1e285b02b6 (diff)
downloadlombok-ccd802503d8aa578be3f1f956d97b06a803de0aa.tar.gz
lombok-ccd802503d8aa578be3f1f956d97b06a803de0aa.tar.bz2
lombok-ccd802503d8aa578be3f1f956d97b06a803de0aa.zip
[fixes #2019] Lombok now properly deals with `@NonNull` specifically on the ‘type use’ of a parameter (and, in case of arrays, on the outermost dimension which is actually the first one listed. Weird corner case of the JLS).
Diffstat (limited to 'src/core/lombok')
-rw-r--r--src/core/lombok/core/AST.java26
-rw-r--r--src/core/lombok/eclipse/EclipseAST.java90
-rw-r--r--src/core/lombok/eclipse/EclipseASTAdapter.java12
-rw-r--r--src/core/lombok/eclipse/EclipseASTVisitor.java24
-rw-r--r--src/core/lombok/eclipse/EclipseNode.java11
-rw-r--r--src/core/lombok/eclipse/TransformEclipseAST.java8
-rw-r--r--src/core/lombok/eclipse/handlers/HandleNonNull.java39
-rw-r--r--src/core/lombok/javac/JavacAST.java75
-rw-r--r--src/core/lombok/javac/JavacASTAdapter.java11
-rw-r--r--src/core/lombok/javac/JavacASTVisitor.java24
-rw-r--r--src/core/lombok/javac/JavacNode.java10
-rw-r--r--src/core/lombok/javac/JavacTransformer.java6
-rw-r--r--src/core/lombok/javac/handlers/HandleNonNull.java24
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java2
14 files changed, 306 insertions, 56 deletions
diff --git a/src/core/lombok/core/AST.java b/src/core/lombok/core/AST.java
index afbba1e8..77443ba1 100644
--- a/src/core/lombok/core/AST.java
+++ b/src/core/lombok/core/AST.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2016 The Project Lombok Authors.
+ * Copyright (C) 2009-2019 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -53,7 +53,7 @@ import lombok.permit.Permit;
public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> {
/** The kind of node represented by a given AST.Node object. */
public enum Kind {
- COMPILATION_UNIT, TYPE, FIELD, INITIALIZER, METHOD, ANNOTATION, ARGUMENT, LOCAL, STATEMENT;
+ COMPILATION_UNIT, TYPE, FIELD, INITIALIZER, METHOD, ANNOTATION, ARGUMENT, LOCAL, STATEMENT, TYPE_USE;
}
private L top;
@@ -263,8 +263,8 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
private Class<?> getComponentType(Type type) {
if (type instanceof ParameterizedType) {
- Type component = ((ParameterizedType)type).getActualTypeArguments()[0];
- return component instanceof Class<?> ? (Class<?>)component : Object.class;
+ Type component = ((ParameterizedType) type).getActualTypeArguments()[0];
+ return component instanceof Class<?> ? (Class<?>) component : Object.class;
}
return Object.class;
}
@@ -330,7 +330,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
idx++;
if (o == null) continue;
if (Collection.class.isInstance(o)) {
- Collection<?> newC = (Collection<?>)o;
+ Collection<?> newC = (Collection<?>) o;
List<Collection<?>> newChain = new ArrayList<Collection<?>>(chain);
newChain.add(newC);
if (replaceStatementInCollection(field, fieldRef, newChain, newC, oldN, newN)) return true;
@@ -356,7 +356,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
@SuppressWarnings({"rawtypes", "unchecked"})
protected void setElementInASTCollection(Field field, Object fieldRef, List<Collection<?>> chain, Collection<?> collection, int idx, N newN) throws IllegalAccessException {
if (collection instanceof List<?>) {
- ((List)collection).set(idx, newN);
+ ((List) collection).set(idx, newN);
}
}
@@ -384,7 +384,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
Object o = fa.field.get(child);
if (o == null) return;
if (fa.dim == 0) {
- L node = buildTree((N)o, Kind.STATEMENT);
+ L node = buildTree((N) o, Kind.STATEMENT);
if (node != null) list.add(nodeType.cast(node));
} else if (o.getClass().isArray()) {
buildWithArray(nodeType, o, list, fa.dim);
@@ -399,12 +399,12 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
@SuppressWarnings("unchecked")
private void buildWithArray(Class<L> nodeType, Object array, Collection<L> list, int dim) {
if (dim == 1) {
- for (Object v : (Object[])array) {
+ for (Object v : (Object[]) array) {
if (v == null) continue;
L node = buildTree((N)v, Kind.STATEMENT);
if (node != null) list.add(nodeType.cast(node));
}
- } else for (Object v : (Object[])array) {
+ } else for (Object v : (Object[]) array) {
if (v == null) return;
buildWithArray(nodeType, v, list, dim -1);
}
@@ -413,13 +413,13 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
@SuppressWarnings("unchecked")
private void buildWithCollection(Class<L> nodeType, Object collection, Collection<L> list, int dim) {
if (dim == 1) {
- for (Object v : (Collection<?>)collection) {
+ for (Object v : (Collection<?>) collection) {
if (v == null) continue;
- L node = buildTree((N)v, Kind.STATEMENT);
+ L node = buildTree((N) v, Kind.STATEMENT);
if (node != null) list.add(nodeType.cast(node));
}
- } else for (Object v : (Collection<?>)collection) {
- buildWithCollection(nodeType, v, list, dim-1);
+ } else for (Object v : (Collection<?>) collection) {
+ buildWithCollection(nodeType, v, list, dim - 1);
}
}
diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java
index 1ba26338..e724fb50 100644
--- a/src/core/lombok/eclipse/EclipseAST.java
+++ b/src/core/lombok/eclipse/EclipseAST.java
@@ -22,6 +22,7 @@
package lombok.eclipse;
import java.io.File;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
@@ -49,8 +50,12 @@ import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Wildcard;
/**
* Wraps around Eclipse's internal AST view to add useful features as well as the ability to visit parents from children,
@@ -292,7 +297,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
throw Lombok.sneakyThrow(e);
} catch (NullPointerException e) {
if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) {
- e.initCause(EcjReflectionCheck.problem);
+ e.initCause(EcjReflectionCheck.problemAddProblemToCompilationResult);
throw e;
}
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
@@ -300,6 +305,25 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
}
}
+ public static Annotation[] getTopLevelTypeReferenceAnnotations(TypeReference tr) {
+ Method m = EcjReflectionCheck.typeReferenceGetAnnotationsOnDimensions;
+ if (m == null) return null;
+ Annotation[][] annss = null;
+ try {
+ annss = (Annotation[][]) m.invoke(tr);
+ if (annss != null) return annss[0];
+ } catch (Throwable ignore) {}
+
+ try {
+ Field f = EcjReflectionCheck.typeReferenceAnnotations;
+ if (f == null) return null;
+ annss = (Annotation[][]) f.get(tr);
+ return annss[annss.length - 1];
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
private final CompilationUnitDeclaration compilationUnitDeclaration;
private boolean completeParse;
@@ -350,6 +374,8 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
return buildStatement((Statement) node);
case ANNOTATION:
return buildAnnotation((Annotation) node, false);
+ case TYPE_USE:
+ return buildTypeUse((TypeReference) node);
default:
throw new AssertionError("Did not expect to arrive here: " + kind);
}
@@ -397,6 +423,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
if (field instanceof Initializer) return buildInitializer((Initializer)field);
if (setAndGetAsHandled(field)) return null;
List<EclipseNode> childNodes = new ArrayList<EclipseNode>();
+ addIfNotNull(childNodes, buildTypeUse(field.type));
addIfNotNull(childNodes, buildStatement(field.initialization));
childNodes.addAll(buildAnnotations(field.annotations, true));
return putInMap(new EclipseNode(this, field, childNodes, Kind.FIELD));
@@ -438,11 +465,40 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
private EclipseNode buildLocal(LocalDeclaration local, Kind kind) {
if (setAndGetAsHandled(local)) return null;
List<EclipseNode> childNodes = new ArrayList<EclipseNode>();
+ addIfNotNull(childNodes, buildTypeUse(local.type));
addIfNotNull(childNodes, buildStatement(local.initialization));
childNodes.addAll(buildAnnotations(local.annotations, true));
return putInMap(new EclipseNode(this, local, childNodes, kind));
}
+ private EclipseNode buildTypeUse(TypeReference tr) {
+ if (setAndGetAsHandled(tr)) return null;
+ if (tr == null) return null;
+
+ List<EclipseNode> childNodes = new ArrayList<EclipseNode>();
+ Annotation[] anns = getTopLevelTypeReferenceAnnotations(tr);
+ if (anns != null) for (Annotation ann : anns) addIfNotNull(childNodes, buildAnnotation(ann, false));
+
+ if (tr instanceof ParameterizedQualifiedTypeReference) {
+ ParameterizedQualifiedTypeReference pqtr = (ParameterizedQualifiedTypeReference) tr;
+ int len = pqtr.tokens.length;
+ for (int i = 0; i < len; i++) {
+ TypeReference[] typeArgs = pqtr.typeArguments[i];
+ if (typeArgs != null) for (TypeReference tArg : typeArgs) addIfNotNull(childNodes, buildTypeUse(tArg));
+ }
+ } else if (tr instanceof ParameterizedSingleTypeReference) {
+ ParameterizedSingleTypeReference pstr = (ParameterizedSingleTypeReference) tr;
+ if (pstr.typeArguments != null) for (TypeReference tArg : pstr.typeArguments) {
+ addIfNotNull(childNodes, buildTypeUse(tArg));
+ }
+ } else if (tr instanceof Wildcard) {
+ TypeReference bound = ((Wildcard) tr).bound;
+ if (bound != null) addIfNotNull(childNodes, buildTypeUse(bound));
+ }
+
+ return putInMap(new EclipseNode(this, tr, childNodes, Kind.TYPE_USE));
+ }
+
private Collection<EclipseNode> buildAnnotations(Annotation[] annotations, boolean varDecl) {
List<EclipseNode> elements = new ArrayList<EclipseNode>();
if (annotations != null) for (Annotation an : annotations) addIfNotNull(elements, buildAnnotation(an, varDecl));
@@ -467,9 +523,9 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
private EclipseNode buildStatement(Statement child) {
if (child == null) return null;
- if (child instanceof TypeDeclaration) return buildType((TypeDeclaration)child);
+ if (child instanceof TypeDeclaration) return buildType((TypeDeclaration) child);
- if (child instanceof LocalDeclaration) return buildLocal((LocalDeclaration)child, Kind.LOCAL);
+ if (child instanceof LocalDeclaration) return buildLocal((LocalDeclaration) child, Kind.LOCAL);
if (setAndGetAsHandled(child)) return null;
@@ -491,21 +547,35 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
private static class EcjReflectionCheck {
private static final String COMPILATIONRESULT_TYPE = "org.eclipse.jdt.internal.compiler.CompilationResult";
- public static Method addProblemToCompilationResult;
- public static final Throwable problem;
-
+ public static final Method addProblemToCompilationResult;
+ public static final Throwable problemAddProblemToCompilationResult;
+ public static final Method typeReferenceGetAnnotationsOnDimensions;
+ public static final Field typeReferenceAnnotations;
static {
Throwable problem_ = null;
- Method m = null;
+ Method m1 = null, m2;
+ Field f;
try {
- m = Permit.getMethod(EclipseAstProblemView.class, "addProblemToCompilationResult", char[].class, Class.forName(COMPILATIONRESULT_TYPE), boolean.class, String.class, int.class, int.class);
+ m1 = Permit.getMethod(EclipseAstProblemView.class, "addProblemToCompilationResult", char[].class, Class.forName(COMPILATIONRESULT_TYPE), boolean.class, String.class, int.class, int.class);
} catch (Throwable t) {
// That's problematic, but as long as no local classes are used we don't actually need it.
// Better fail on local classes than crash altogether.
problem_ = t;
}
- addProblemToCompilationResult = m;
- problem = problem_;
+ try {
+ m2 = Permit.getMethod(TypeReference.class, "getAnnotationsOnDimensions");
+ } catch (Throwable t) {
+ m2 = null;
+ }
+ try {
+ f = Permit.getField(TypeReference.class, "annotations");
+ } catch (Throwable t) {
+ f = null;
+ }
+ addProblemToCompilationResult = m1;
+ problemAddProblemToCompilationResult = problem_;
+ typeReferenceGetAnnotationsOnDimensions = m2;
+ typeReferenceAnnotations = f;
}
}
}
diff --git a/src/core/lombok/eclipse/EclipseASTAdapter.java b/src/core/lombok/eclipse/EclipseASTAdapter.java
index 61807fff..c6ad059d 100644
--- a/src/core/lombok/eclipse/EclipseASTAdapter.java
+++ b/src/core/lombok/eclipse/EclipseASTAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2009-2019 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -30,6 +30,7 @@ import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
/**
* Standard adapter for the {@link EclipseASTVisitor} interface. Every method on that interface
@@ -97,6 +98,15 @@ public abstract class EclipseASTAdapter implements EclipseASTVisitor {
public void endVisitLocal(EclipseNode localNode, LocalDeclaration local) {}
/** {@inheritDoc} */
+ @Override public void visitTypeUse(EclipseNode typeUseNode, TypeReference typeUse) {}
+
+ /** {@inheritDoc} */
+ public void visitAnnotationOnTypeUse(TypeReference typeUse, EclipseNode annotationNode, Annotation annotation) {}
+
+ /** {@inheritDoc} */
+ @Override public void endVisitTypeUse(EclipseNode typeUseNode, TypeReference typeUse) {}
+
+ /** {@inheritDoc} */
public void visitStatement(EclipseNode statementNode, Statement statement) {}
/** {@inheritDoc} */
diff --git a/src/core/lombok/eclipse/EclipseASTVisitor.java b/src/core/lombok/eclipse/EclipseASTVisitor.java
index 37bda5e3..0bd668bc 100644
--- a/src/core/lombok/eclipse/EclipseASTVisitor.java
+++ b/src/core/lombok/eclipse/EclipseASTVisitor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2019 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -107,6 +107,13 @@ public interface EclipseASTVisitor {
void endVisitLocal(EclipseNode localNode, LocalDeclaration local);
/**
+ * Visits a node that represents a type reference. Anything from {@code int} to {@code T} to {@code foo,.pkg.Bar<T>.Baz<?> @Ann []}.
+ */
+ void visitTypeUse(EclipseNode typeUseNode, TypeReference typeUse);
+ void visitAnnotationOnTypeUse(TypeReference typeUse, EclipseNode annotationNode, Annotation annotation);
+ void endVisitTypeUse(EclipseNode typeUseNode, TypeReference typeUse);
+
+ /**
* Visits a statement that isn't any of the other visit methods (e.g. TypeDeclaration).
*/
void visitStatement(EclipseNode statementNode, Statement statement);
@@ -412,6 +419,21 @@ public interface EclipseASTVisitor {
print("</LOCAL %s %s>", str(local.type), str(local.name));
}
+ @Override public void visitTypeUse(EclipseNode typeUseNode, TypeReference typeUse) {
+ print("<TYPE %s>", typeUse.getClass());
+ indent++;
+ print("%s", typeUse);
+ }
+
+ @Override public void visitAnnotationOnTypeUse(TypeReference typeUse, EclipseNode annotationNode, Annotation annotation) {
+ print("<ANNOTATION%s: %s />", isGenerated(annotation) ? " (GENERATED)" : "", annotation);
+ }
+
+ @Override public void endVisitTypeUse(EclipseNode typeUseNode, TypeReference typeUse) {
+ indent--;
+ print("</TYPE %s>", typeUse.getClass());
+ }
+
public void visitStatement(EclipseNode node, Statement statement) {
print("<%s%s%s>", statement.getClass(), isGenerated(statement) ? " (GENERATED)" : "", position(node));
if (statement instanceof AllocationExpression) {
diff --git a/src/core/lombok/eclipse/EclipseNode.java b/src/core/lombok/eclipse/EclipseNode.java
index a0580c51..4c7f4eac 100644
--- a/src/core/lombok/eclipse/EclipseNode.java
+++ b/src/core/lombok/eclipse/EclipseNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2018 The Project Lombok Authors.
+ * Copyright (C) 2009-2019 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -38,6 +38,7 @@ import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
/**
@@ -119,10 +120,18 @@ public class EclipseNode extends lombok.core.LombokNode<EclipseAST, EclipseNode,
case LOCAL:
visitor.visitAnnotationOnLocal((LocalDeclaration) parent.get(), this, (Annotation) get());
break;
+ case TYPE_USE:
+ visitor.visitAnnotationOnTypeUse((TypeReference) parent.get(), this, (Annotation) get());
+ break;
default:
throw new AssertionError("Annotation not expected as child of a " + up().getKind());
}
break;
+ case TYPE_USE:
+ visitor.visitTypeUse(this, (TypeReference) get());
+ ast.traverseChildren(visitor, this);
+ visitor.endVisitTypeUse(this, (TypeReference) get());
+ break;
case STATEMENT:
visitor.visitStatement(this, (Statement) get());
ast.traverseChildren(visitor, this);
diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java
index e5edba64..6fcde937 100644
--- a/src/core/lombok/eclipse/TransformEclipseAST.java
+++ b/src/core/lombok/eclipse/TransformEclipseAST.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2018 The Project Lombok Authors.
+ * Copyright (C) 2009-2019 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -39,6 +39,7 @@ import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.parser.Parser;
/**
@@ -236,5 +237,10 @@ public class TransformEclipseAST {
CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
nextPriority = Math.min(nextPriority, handlers.handleAnnotation(top, annotationNode, annotation, priority));
}
+
+ @Override public void visitAnnotationOnTypeUse(TypeReference typeUse, EclipseNode annotationNode, Annotation annotation) {
+ CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get();
+ nextPriority = Math.min(nextPriority, handlers.handleAnnotation(top, annotationNode, annotation, priority));
+ }
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleNonNull.java b/src/core/lombok/eclipse/handlers/HandleNonNull.java
index ebc62909..1672618d 100644
--- a/src/core/lombok/eclipse/handlers/HandleNonNull.java
+++ b/src/core/lombok/eclipse/handlers/HandleNonNull.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-2019 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -33,6 +33,7 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.DeferUntilPostDiet;
+import lombok.eclipse.EclipseAST;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
@@ -52,6 +53,7 @@ import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement;
import org.eclipse.jdt.internal.compiler.ast.ThrowStatement;
import org.eclipse.jdt.internal.compiler.ast.TryStatement;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.mangosdk.spi.ProviderFor;
@DeferUntilPostDiet
@@ -95,14 +97,33 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> {
return;
}
- if (annotationNode.up().getKind() != Kind.ARGUMENT) return;
-
- Argument arg;
+ Argument param;
+ EclipseNode paramNode;
AbstractMethodDeclaration declaration;
+ switch (annotationNode.up().getKind()) {
+ case ARGUMENT:
+ paramNode = annotationNode.up();
+ break;
+ case TYPE_USE:
+ EclipseNode typeNode = annotationNode.directUp();
+ boolean ok = false;
+ ASTNode astNode = typeNode.get();
+ if (astNode instanceof TypeReference) {
+ Annotation[] anns = EclipseAST.getTopLevelTypeReferenceAnnotations((TypeReference) astNode);
+ if (anns == null) return;
+ for (Annotation ann : anns) if (ast == ann) ok = true;
+ }
+ if (!ok) return;
+ paramNode = typeNode.directUp();
+ break;
+ default:
+ return;
+ }
+
try {
- arg = (Argument) annotationNode.up().get();
- declaration = (AbstractMethodDeclaration) annotationNode.up().up().get();
+ param = (Argument) paramNode.get();
+ declaration = (AbstractMethodDeclaration) paramNode.up().get();
} catch (Exception e) {
return;
}
@@ -118,7 +139,7 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> {
// and if they exist, create a new method in the class: 'private static <T> T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and
// wrap all references to it in the super/this to a call to this method.
- Statement nullCheck = generateNullCheck(arg, annotationNode);
+ Statement nullCheck = generateNullCheck(param, annotationNode);
if (nullCheck == null) {
// @NonNull applied to a primitive. Kinda pointless. Let's generate a warning.
@@ -129,7 +150,7 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> {
if (declaration.statements == null) {
declaration.statements = new Statement[] {nullCheck};
} else {
- char[] expectedName = arg.name;
+ char[] expectedName = param.name;
/* Abort if the null check is already there, delving into try and synchronized statements */ {
Statement[] stats = declaration.statements;
int idx = 0;
@@ -162,7 +183,7 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> {
newStatements[skipOver] = nullCheck;
declaration.statements = newStatements;
}
- annotationNode.up().up().rebuild();
+ paramNode.up().rebuild();
}
public boolean isNullCheck(Statement stat) {
diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java
index f2901038..f6cd5571 100644
--- a/src/core/lombok/javac/JavacAST.java
+++ b/src/core/lombok/javac/JavacAST.java
@@ -47,15 +47,19 @@ import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCCatch;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTry;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.JCWildcard;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
@@ -192,6 +196,8 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return buildStatementOrExpression(node);
case ANNOTATION:
return buildAnnotation((JCAnnotation) node, false);
+ case TYPE_USE:
+ return buildTypeUse(node);
default:
throw new AssertionError("Did not expect: " + kind);
}
@@ -233,6 +239,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
if (setAndGetAsHandled(field)) return null;
List<JavacNode> childNodes = new ArrayList<JavacNode>();
for (JCAnnotation annotation : field.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation, true));
+ addIfNotNull(childNodes, buildTypeUse(field.vartype));
addIfNotNull(childNodes, buildExpression(field.init));
return putInMap(new JavacNode(this, field, childNodes, Kind.FIELD));
}
@@ -241,23 +248,62 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
if (setAndGetAsHandled(local)) return null;
List<JavacNode> childNodes = new ArrayList<JavacNode>();
for (JCAnnotation annotation : local.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation, true));
+ addIfNotNull(childNodes, buildTypeUse(local.vartype));
addIfNotNull(childNodes, buildExpression(local.init));
return putInMap(new JavacNode(this, local, childNodes, kind));
}
- private static boolean JCTRY_RESOURCES_FIELD_INITIALIZED;
+ private JavacNode buildTypeUse(JCTree typeUse) {
+ if (setAndGetAsHandled(typeUse)) return null;
+
+ if (typeUse == null) return null;
+
+ if (typeUse.getClass().getSimpleName().equals("JCAnnotatedType")) {
+ initJcAnnotatedType(typeUse.getClass());
+ Collection<?> anns = Permit.permissiveReadField(Collection.class, JCANNOTATEDTYPE_ANNOTATIONS, typeUse);
+ JCExpression underlying = Permit.permissiveReadField(JCExpression.class, JCANNOTATEDTYPE_UNDERLYINGTYPE, typeUse);
+
+ List<JavacNode> childNodes = new ArrayList<JavacNode>();
+ if (anns != null) for (Object annotation : anns) if (annotation instanceof JCAnnotation) addIfNotNull(childNodes, buildAnnotation((JCAnnotation) annotation, true));
+ addIfNotNull(childNodes, buildTypeUse(underlying));
+ return putInMap(new JavacNode(this, typeUse, childNodes, Kind.TYPE_USE));
+ }
+
+ if (typeUse instanceof JCWildcard) {
+ JCTree inner = ((JCWildcard) typeUse).inner;
+ List<JavacNode> childNodes = inner == null ? Collections.<JavacNode>emptyList() : new ArrayList<JavacNode>();
+ if (inner != null) addIfNotNull(childNodes, buildTypeUse(inner));
+ return putInMap(new JavacNode(this, typeUse, childNodes, Kind.TYPE_USE));
+ }
+
+ if (typeUse instanceof JCArrayTypeTree) {
+ JCTree inner = ((JCArrayTypeTree) typeUse).elemtype;
+ List<JavacNode> childNodes = inner == null ? Collections.<JavacNode>emptyList() : new ArrayList<JavacNode>();
+ if (inner != null) addIfNotNull(childNodes, buildTypeUse(inner));
+ return putInMap(new JavacNode(this, typeUse, childNodes, Kind.TYPE_USE));
+ }
+
+ if (typeUse instanceof JCFieldAccess) {
+ JCTree inner = ((JCFieldAccess) typeUse).selected;
+ List<JavacNode> childNodes = inner == null ? Collections.<JavacNode>emptyList() : new ArrayList<JavacNode>();
+ if (inner != null) addIfNotNull(childNodes, buildTypeUse(inner));
+ return putInMap(new JavacNode(this, typeUse, childNodes, Kind.TYPE_USE));
+ }
+
+ if (typeUse instanceof JCIdent) {
+ return putInMap(new JavacNode(this, typeUse, Collections.<JavacNode>emptyList(), Kind.TYPE_USE));
+ }
+
+ return null;
+ }
+
+ private static boolean JCTRY_RESOURCES_FIELD_INITIALIZED = false;
private static Field JCTRY_RESOURCES_FIELD;
@SuppressWarnings("unchecked")
private static List<JCTree> getResourcesForTryNode(JCTry tryNode) {
if (!JCTRY_RESOURCES_FIELD_INITIALIZED) {
- try {
- JCTRY_RESOURCES_FIELD = Permit.getField(JCTry.class, "resources");
- } catch (NoSuchFieldException ignore) {
- // Java 1.6 or lower won't have this at all.
- } catch (Exception ignore) {
- // Shouldn't happen. Best thing we can do is just carry on and break on try/catch.
- }
+ JCTRY_RESOURCES_FIELD = Permit.permissiveGetField(JCTry.class, "resources");
JCTRY_RESOURCES_FIELD_INITIALIZED = true;
}
@@ -271,6 +317,15 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return Collections.emptyList();
}
+ private static boolean JCANNOTATEDTYPE_FIELDS_INITIALIZED = false;
+ private static Field JCANNOTATEDTYPE_ANNOTATIONS, JCANNOTATEDTYPE_UNDERLYINGTYPE;
+ private static void initJcAnnotatedType(Class<?> context) {
+ if (JCANNOTATEDTYPE_FIELDS_INITIALIZED) return;
+ JCANNOTATEDTYPE_ANNOTATIONS = Permit.permissiveGetField(context, "annotations");
+ JCANNOTATEDTYPE_UNDERLYINGTYPE = Permit.permissiveGetField(context, "underlyingType");
+ JCANNOTATEDTYPE_FIELDS_INITIALIZED = true;
+ }
+
private JavacNode buildTry(JCTry tryNode) {
if (setAndGetAsHandled(tryNode)) return null;
List<JavacNode> childNodes = new ArrayList<JavacNode>();
@@ -323,8 +378,8 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
private JavacNode buildStatementOrExpression(JCTree statement) {
if (statement == null) return null;
if (statement instanceof JCAnnotation) return null;
- if (statement instanceof JCClassDecl) return buildType((JCClassDecl)statement);
- if (statement instanceof JCVariableDecl) return buildLocalVar((JCVariableDecl)statement, Kind.LOCAL);
+ if (statement instanceof JCClassDecl) return buildType((JCClassDecl) statement);
+ if (statement instanceof JCVariableDecl) return buildLocalVar((JCVariableDecl) statement, Kind.LOCAL);
if (statement instanceof JCTry) return buildTry((JCTry) statement);
if (statement.getClass().getSimpleName().equals("JCLambda")) return buildLambda(statement);
if (setAndGetAsHandled(statement)) return null;
diff --git a/src/core/lombok/javac/JavacASTAdapter.java b/src/core/lombok/javac/JavacASTAdapter.java
index 6af53e3d..4c1912d8 100644
--- a/src/core/lombok/javac/JavacASTAdapter.java
+++ b/src/core/lombok/javac/JavacASTAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2019 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -96,6 +96,15 @@ public class JavacASTAdapter implements JavacASTVisitor {
@Override public void endVisitLocal(JavacNode localNode, JCVariableDecl local) {}
/** {@inheritDoc} */
+ @Override public void visitTypeUse(JavacNode typeUseNode, JCTree typeUse) {}
+
+ /** {@inheritDoc} */
+ @Override public void visitAnnotationOnTypeUse(JCTree typeUse, JavacNode annotationNode, JCAnnotation annotation) {}
+
+ /** {@inheritDoc} */
+ @Override public void endVisitTypeUse(JavacNode typeUseNode, JCTree typeUse) {}
+
+ /** {@inheritDoc} */
@Override public void visitStatement(JavacNode statementNode, JCTree statement) {}
/** {@inheritDoc} */
diff --git a/src/core/lombok/javac/JavacASTVisitor.java b/src/core/lombok/javac/JavacASTVisitor.java
index d4f8f731..9b67dda3 100644
--- a/src/core/lombok/javac/JavacASTVisitor.java
+++ b/src/core/lombok/javac/JavacASTVisitor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2019 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -89,6 +89,13 @@ public interface JavacASTVisitor {
void endVisitLocal(JavacNode localNode, JCVariableDecl local);
/**
+ * Visits a node that represents a type reference. Anything from {@code int} to {@code T} to {@code foo.pkg.Bar<T>.Baz<?> @Ann []}.
+ */
+ void visitTypeUse(JavacNode typeUseNode, JCTree typeUse);
+ void visitAnnotationOnTypeUse(JCTree typeUse, JavacNode annotationNode, JCAnnotation annotation);
+ void endVisitTypeUse(JavacNode typeUseNode, JCTree typeUse);
+
+ /**
* Visits a statement that isn't any of the other visit methods (e.g. JCClassDecl).
* The statement object is guaranteed to be either a JCStatement or a JCExpression.
*/
@@ -261,6 +268,21 @@ public interface JavacASTVisitor {
print("</LOCAL %s %s>", local.vartype, local.name);
}
+ @Override public void visitTypeUse(JavacNode node, JCTree typeUse) {
+ print("<TYPE %s>", typeUse.getClass());
+ indent++;
+ print("%s", typeUse);
+ }
+
+ @Override public void visitAnnotationOnTypeUse(JCTree typeUse, JavacNode node, JCAnnotation annotation) {
+ print("<ANNOTATION: %s />", annotation);
+ }
+
+ @Override public void endVisitTypeUse(JavacNode node, JCTree typeUse) {
+ indent--;
+ print("</TYPE %s>", typeUse.getClass());
+ }
+
@Override public void visitStatement(JavacNode node, JCTree statement) {
print("<%s>", statement.getClass());
indent++;
diff --git a/src/core/lombok/javac/JavacNode.java b/src/core/lombok/javac/JavacNode.java
index f119f1f9..191ab3c0 100644
--- a/src/core/lombok/javac/JavacNode.java
+++ b/src/core/lombok/javac/JavacNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2018 The Project Lombok Authors.
+ * Copyright (C) 2009-2019 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -145,10 +145,18 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre
case LOCAL:
visitor.visitAnnotationOnLocal((JCVariableDecl) up().get(), this, (JCAnnotation) get());
break;
+ case TYPE_USE:
+ visitor.visitAnnotationOnTypeUse(up().get(), this, (JCAnnotation) get());
+ break;
default:
throw new AssertionError("Annotion not expected as child of a " + up().getKind());
}
break;
+ case TYPE_USE:
+ visitor.visitTypeUse(this, get());
+ ast.traverseChildren(visitor, this);
+ visitor.endVisitTypeUse(this, get());
+ break;
default:
throw new AssertionError("Unexpected kind during node traversal: " + getKind());
}
diff --git a/src/core/lombok/javac/JavacTransformer.java b/src/core/lombok/javac/JavacTransformer.java
index 0a4f1f73..625fb283 100644
--- a/src/core/lombok/javac/JavacTransformer.java
+++ b/src/core/lombok/javac/JavacTransformer.java
@@ -27,6 +27,7 @@ import java.util.SortedSet;
import javax.annotation.processing.Messager;
import com.sun.source.util.Trees;
+import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
@@ -114,5 +115,10 @@ public class JavacTransformer {
JCCompilationUnit top = (JCCompilationUnit) annotationNode.top().get();
handlers.handleAnnotation(top, annotationNode, annotation, priority);
}
+
+ @Override public void visitAnnotationOnTypeUse(JCTree typeUse, JavacNode annotationNode, JCAnnotation annotation) {
+ JCCompilationUnit top = (JCCompilationUnit) annotationNode.top().get();
+ handlers.handleAnnotation(top, annotationNode, annotation, priority);
+ }
}
}
diff --git a/src/core/lombok/javac/handlers/HandleNonNull.java b/src/core/lombok/javac/handlers/HandleNonNull.java
index 81aa1525..9a81ffff 100644
--- a/src/core/lombok/javac/handlers/HandleNonNull.java
+++ b/src/core/lombok/javac/handlers/HandleNonNull.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-2019 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -74,12 +74,24 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> {
return;
}
- if (annotationNode.up().getKind() != Kind.ARGUMENT) return;
-
JCMethodDecl declaration;
+ JavacNode paramNode;
+
+ switch (annotationNode.up().getKind()) {
+ case ARGUMENT:
+ paramNode = annotationNode.up();
+ break;
+ case TYPE_USE:
+ JavacNode typeNode = annotationNode.directUp();
+ paramNode = typeNode.directUp();
+ break;
+ default:
+ return;
+ }
+ if (paramNode.getKind() != Kind.ARGUMENT) return;
try {
- declaration = (JCMethodDecl) annotationNode.up().up().get();
+ declaration = (JCMethodDecl) paramNode.up().get();
} catch (Exception e) {
return;
}
@@ -93,7 +105,7 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> {
// and if they exist, create a new method in the class: 'private static <T> T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and
// wrap all references to it in the super/this to a call to this method.
- JCStatement nullCheck = recursiveSetGeneratedBy(generateNullCheck(annotationNode.getTreeMaker(), annotationNode.up(), annotationNode), ast, annotationNode.getContext());
+ JCStatement nullCheck = recursiveSetGeneratedBy(generateNullCheck(annotationNode.getTreeMaker(), paramNode, annotationNode), ast, annotationNode.getContext());
if (nullCheck == null) {
// @NonNull applied to a primitive. Kinda pointless. Let's generate a warning.
@@ -103,7 +115,7 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> {
List<JCStatement> statements = declaration.body.stats;
- String expectedName = annotationNode.up().getName();
+ String expectedName = paramNode.getName();
/* Abort if the null check is already there, delving into try and synchronized statements */ {
List<JCStatement> stats = statements;
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 5fd17388..f08098d2 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -1462,7 +1462,7 @@ public class JavacHandlerUtil {
* variable name as message.
*/
public static JCStatement generateNullCheck(JavacTreeMaker maker, JavacNode variable, JavacNode source) {
- return generateNullCheck(maker, variable, (JCVariableDecl)variable.get(), source);
+ return generateNullCheck(maker, variable, (JCVariableDecl) variable.get(), source);
}
/**