diff options
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) { + } +} |