mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-14 12:16:49 +00:00
[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).
This commit is contained in:
21 files changed
+546
-65
No files matched your search
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -96,6 +97,15 @@ public abstract class EclipseASTAdapter implements EclipseASTVisitor {
|
||||
/** {@inheritDoc} */
|
||||
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) {}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -106,6 +106,13 @@ public interface EclipseASTVisitor {
|
||||
void visitAnnotationOnLocal(LocalDeclaration local, EclipseNode annotationNode, Annotation annotation);
|
||||
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).
|
||||
*/
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -95,6 +95,15 @@ public class JavacASTAdapter implements JavacASTVisitor {
|
||||
/** {@inheritDoc} */
|
||||
@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) {}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -88,6 +88,13 @@ public interface JavacASTVisitor {
|
||||
void visitAnnotationOnLocal(JCVariableDecl local, JavacNode annotationNode, JCAnnotation annotation);
|
||||
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++;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user