diff options
author | Reinier Zwitserloot <reinier@zwitserloot.com> | 2019-01-22 04:28:18 +0100 |
---|---|---|
committer | Reinier Zwitserloot <reinier@zwitserloot.com> | 2019-01-22 04:30:02 +0100 |
commit | ccd802503d8aa578be3f1f956d97b06a803de0aa (patch) | |
tree | 53bee7018a6dd79664ee9b711b7ca36146acb8c1 /src/core/lombok | |
parent | ba4e69bf30bf1c761b84e78dbec1fa1e285b02b6 (diff) | |
download | lombok-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.java | 26 | ||||
-rw-r--r-- | src/core/lombok/eclipse/EclipseAST.java | 90 | ||||
-rw-r--r-- | src/core/lombok/eclipse/EclipseASTAdapter.java | 12 | ||||
-rw-r--r-- | src/core/lombok/eclipse/EclipseASTVisitor.java | 24 | ||||
-rw-r--r-- | src/core/lombok/eclipse/EclipseNode.java | 11 | ||||
-rw-r--r-- | src/core/lombok/eclipse/TransformEclipseAST.java | 8 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleNonNull.java | 39 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacAST.java | 75 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacASTAdapter.java | 11 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacASTVisitor.java | 24 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacNode.java | 10 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacTransformer.java | 6 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleNonNull.java | 24 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/JavacHandlerUtil.java | 2 |
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); } /** |