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 | |
| 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).
21 files changed, 546 insertions, 65 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 []} |
