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 []}. + */ + 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); } /** diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java index 69249cc9..84c342f0 100644 --- a/src/delombok/lombok/delombok/PrettyPrinter.java +++ b/src/delombok/lombok/delombok/PrettyPrinter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Project Lombok Authors. + * Copyright (C) 2016-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 @@ -378,6 +378,9 @@ public class PrettyPrinter extends JCTree.Visitor { private int dims(JCExpression vartype) { if (vartype instanceof JCArrayTypeTree) { return 1 + dims(((JCArrayTypeTree) vartype).elemtype); + } else if (isJcAnnotatedType(vartype)) { + JCTree underlyingType = readObject(vartype, "underlyingType", (JCTree) null); + if (underlyingType instanceof JCArrayTypeTree) return 1 + dims (((JCArrayTypeTree) underlyingType).elemtype); } return 0; @@ -625,13 +628,29 @@ public class PrettyPrinter extends JCTree.Visitor { printVarDef0(tree); } + private boolean innermostArrayBracketsAreVarargs = false; private void printVarDef0(JCVariableDecl tree) { boolean varargs = (tree.mods.flags & VARARGS) != 0; - if (varargs && tree.vartype instanceof JCArrayTypeTree) { - print(((JCArrayTypeTree) tree.vartype).elemtype); - print("..."); - } else { + + /* story time! + + in 'new int[5][6];', the 5 is the outermost and the 6 is the innermost: That means: 5 int arrays, each capable of containing 6 elements. + But that's actually a crazy way to read it; you'd think that in FOO[], you should interpret that as 'an array of FOO', but that's not correct; + if FOO is for example 'int[]', it's: "Modify the component type of FOO to be an array of whatever it was before.. unless FOO isn't an array, in which case, + this is an array of FOO". Which is weird. + + This is particularly poignant with vargs. In: "int[]... x", the ... are actually the _INNER_ type even though varargs by definition is a modification of + how to interpret the outer. The JLS just sort of lets that be: To indicate varargs, replace the lexically last [] with dots even though that's the wrong + [] to modify! + + This becomes an utter shambles when annotations-on-arrays become involved. The annotation on the INNER most type is to be placed right before the ...; + and because of that, we have to do crazy stuff with this innermostArrayBracketsAreVarargs flag. + */ + try { + innermostArrayBracketsAreVarargs = varargs; print(tree.vartype); + } finally { + innermostArrayBracketsAreVarargs = false; } print(" "); print(tree.name); @@ -775,10 +794,7 @@ public class PrettyPrinter extends JCTree.Visitor { } @Override public void visitTypeArray(JCArrayTypeTree tree) { - JCTree elem = tree.elemtype; - while (elem instanceof JCWildcard) elem = ((JCWildcard) elem).inner; - print(elem); - print("[]"); + printTypeArray0(tree); } @Override public void visitNewArray(JCNewArray tree) { @@ -1457,6 +1473,21 @@ public class PrettyPrinter extends JCTree.Visitor { } } + private boolean jcAnnotatedTypeInit = false; + private Class<?> jcAnnotatedTypeClass = null; + + private boolean isJcAnnotatedType(Object o) { + if (o == null) return false; + if (jcAnnotatedTypeInit) return jcAnnotatedTypeClass == o.getClass(); + Class<?> c = o.getClass(); + if (c.getSimpleName().equals("JCAnnotatedType")) { + jcAnnotatedTypeClass = c; + jcAnnotatedTypeInit = true; + return true; + } + return false; + } + private void printMemberReference0(JCTree tree) { print(readObject(tree, "expr", (JCExpression) null)); print("::"); @@ -1515,10 +1546,57 @@ public class PrettyPrinter extends JCTree.Visitor { print(readObject(tree, "annotations", List.<JCExpression>nil()), " "); print(" "); print(((JCFieldAccess) underlyingType).name); + } else if (underlyingType instanceof JCArrayTypeTree) { + printTypeArray0(tree); } else { print(readObject(tree, "annotations", List.<JCExpression>nil()), " "); print(" "); print(underlyingType); } } + + private void printTypeArray0(JCTree tree) { + JCTree inner = tree; + int dimCount = 0; + + while (true) { + if (inner instanceof JCArrayTypeTree) { + inner = ((JCArrayTypeTree) inner).elemtype; + dimCount++; + continue; + } else if (isJcAnnotatedType(inner)) { + JCTree underlyingType = readObject(inner, "underlyingType", (JCTree) null); + if (underlyingType instanceof JCArrayTypeTree) { + inner = ((JCArrayTypeTree) underlyingType).elemtype; + dimCount++; + continue; + } + } + break; + } + + print(inner); + + inner = tree; + while (true) { + if (inner instanceof JCArrayTypeTree) { + dimCount--; + print((dimCount == 0 && innermostArrayBracketsAreVarargs) ? "..." : "[]"); + inner = ((JCArrayTypeTree) inner).elemtype; + continue; + } else if (isJcAnnotatedType(inner)) { + JCTree underlyingType = readObject(inner, "underlyingType", (JCTree) null); + if (underlyingType instanceof JCArrayTypeTree) { + dimCount--; + print(" "); + print(readObject(inner, "annotations", List.<JCExpression>nil()), " "); + print(" "); + print((dimCount == 0 && innermostArrayBracketsAreVarargs) ? "..." : "[]"); + inner = ((JCArrayTypeTree) underlyingType).elemtype; + continue; + } + } + break; + } + } } diff --git a/src/utils/lombok/permit/Permit.java b/src/utils/lombok/permit/Permit.java index 00b8274c..9f0434b8 100644 --- a/src/utils/lombok/permit/Permit.java +++ b/src/utils/lombok/permit/Permit.java @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2018-20199 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 + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.permit; import java.lang.reflect.AccessibleObject; @@ -84,6 +105,22 @@ public class Permit { return setAccessible(f); } + public static Field permissiveGetField(Class<?> c, String fName) { + try { + return getField(c, fName); + } catch (Exception ignore) { + return null; + } + } + + public static <T> T permissiveReadField(Class<T> type, Field f, Object instance) { + try { + return type.cast(f.get(instance)); + } catch (Exception ignore) { + return null; + } + } + public static <T> Constructor<T> getConstructor(Class<T> c, Class<?>... parameterTypes) throws NoSuchMethodException { return setAccessible(c.getDeclaredConstructor(parameterTypes)); } diff --git a/test/pretty/resource/after/TypeAnnotations.java b/test/pretty/resource/after/TypeAnnotations.java index 760b5ef8..aa93f173 100644 --- a/test/pretty/resource/after/TypeAnnotations.java +++ b/test/pretty/resource/after/TypeAnnotations.java @@ -9,6 +9,12 @@ public class TypeAnnotations { @Target(ElementType.TYPE_USE) @interface Bar { } + @Target(ElementType.TYPE_USE) + @interface Baz { + } + @Target(ElementType.TYPE_USE) + @interface Bat { + } public List<@Foo String> test(@Foo String param) { @Bar String local = "bar"; @@ -19,4 +25,10 @@ public class TypeAnnotations { public <@Foo T extends java.lang.@Foo Number> T test2(@Bar String... varargs) { return null; } + public void test3(String[][] arg, String[]... arg2) { + @Baz + String @Bar [] @Foo [] x; + } + public void test4(@Foo String @Bar [] @Baz [] @Bat ... y) { + } } diff --git a/test/pretty/resource/before/TypeAnnotations.java b/test/pretty/resource/before/TypeAnnotations.java index a39337da..25982a7a 100644 --- a/test/pretty/resource/before/TypeAnnotations.java +++ b/test/pretty/resource/before/TypeAnnotations.java @@ -11,6 +11,12 @@ public class TypeAnnotations { @Target(ElementType.TYPE_USE) @interface Bar {} + @Target(ElementType.TYPE_USE) + @interface Baz {} + + @Target(ElementType.TYPE_USE) + @interface Bat {} + public List<@Foo String> test(@Foo String param) { @Bar String local = "bar"; @Foo java.io.@Foo File[] array = {}; @@ -20,4 +26,11 @@ public class TypeAnnotations { public <@Foo T extends java.lang.@Foo Number> T test2(@Bar String... varargs) { return null; } + + public void test3(String[][] arg, String[]... arg2) { + @Baz String @Bar [] @Foo [] x; + } + + public void test4(@Foo String @Bar [] @Baz [] @Bat ... y) { + } } diff --git a/test/transform/resource/after-delombok/NonNullTypeUse.java b/test/transform/resource/after-delombok/NonNullTypeUse.java new file mode 100644 index 00000000..27719480 --- /dev/null +++ b/test/transform/resource/after-delombok/NonNullTypeUse.java @@ -0,0 +1,32 @@ +import lombok.NonNull; +class NonNullTypeUse { + void test1(@NonNull String[][][] args) { + if (args == null) { + throw new java.lang.NullPointerException("args is marked @NonNull but is null"); + } + } + void test2(String @NonNull [][][] args) { + if (args == null) { + throw new java.lang.NullPointerException("args is marked @NonNull but is null"); + } + } + void test3(String[] @NonNull [][] args) { + } + void test4(String[][] @NonNull [] args) { + } + void test5(@NonNull String simple) { + if (simple == null) { + throw new java.lang.NullPointerException("simple is marked @NonNull but is null"); + } + } + void test6(java.lang.@NonNull String weird) { + if (weird == null) { + throw new java.lang.NullPointerException("weird is marked @NonNull but is null"); + } + } + void test7(java.lang.String @NonNull [][] weird) { + if (weird == null) { + throw new java.lang.NullPointerException("weird is marked @NonNull but is null"); + } + } +} diff --git a/test/transform/resource/after-ecj/NonNullTypeUse.java b/test/transform/resource/after-ecj/NonNullTypeUse.java new file mode 100644 index 00000000..4cf1aa5a --- /dev/null +++ b/test/transform/resource/after-ecj/NonNullTypeUse.java @@ -0,0 +1,40 @@ +import lombok.NonNull; +class NonNullTypeUse { + NonNullTypeUse() { + super(); + } + void test1(@NonNull String[][][] args) { + if ((args == null)) + { + throw new java.lang.NullPointerException("args is marked @NonNull but is null"); + } + } + void test2(String @NonNull [][][] args) { + if ((args == null)) + { + throw new java.lang.NullPointerException("args is marked @NonNull but is null"); + } + } + void test3(String[] @NonNull [][] args) { + } + void test4(String[][] @NonNull [] args) { + } + void test5(@NonNull String simple) { + if ((simple == null)) + { + throw new java.lang.NullPointerException("simple is marked @NonNull but is null"); + } + } + void test6(java.lang.@NonNull String weird) { + if ((weird == null)) + { + throw new java.lang.NullPointerException("weird is marked @NonNull but is null"); + } + } + void test7(java.lang.String @NonNull [][] weird) { + if ((weird == null)) + { + throw new java.lang.NullPointerException("weird is marked @NonNull but is null"); + } + } +}
\ No newline at end of file diff --git a/test/transform/resource/before/NonNullTypeUse.java b/test/transform/resource/before/NonNullTypeUse.java new file mode 100644 index 00000000..32179351 --- /dev/null +++ b/test/transform/resource/before/NonNullTypeUse.java @@ -0,0 +1,19 @@ +//version 8: +import lombok.NonNull; + +class NonNullTypeUse { + void test1(@NonNull String[][][] args) { + } + void test2(String @NonNull [][][] args) { + } + void test3(String [] @NonNull [][] args) { + } + void test4(String [][] @NonNull [] args) { + } + void test5(@NonNull String simple) { + } + void test6(java.lang.@NonNull String weird) { + } + void test7(java.lang.String @NonNull [][] weird) { + } +} |