mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-14 10:18:46 +00:00
[Fixes #1656] Lombok would silently do the wrong thing when using references to public static final String fields, instead of actual string literals, there where you can specify strings in lombok annotation parameters, such as @ToString(of = MyClass.CONSTANT_FIELD). We can’t really fix it, but at least now lombok will error when you do that and describe in detail what’s going wrong.
This commit is contained in:
1 parent
7e81ac6238
commit
f540335ef9
9 files changed
+87
-40
No files matched your search
@@ -43,6 +43,30 @@ public class AnnotationValues<A extends Annotation> {
|
|||||||
private final Map<String, AnnotationValue> values;
|
private final Map<String, AnnotationValue> values;
|
||||||
private final LombokNode<?, ?, ?> ast;
|
private final LombokNode<?, ?, ?> ast;
|
||||||
|
|
||||||
|
public static class ClassLiteral {
|
||||||
|
private final String className;
|
||||||
|
|
||||||
|
public ClassLiteral(String className) {
|
||||||
|
this.className = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FieldSelect {
|
||||||
|
private final String finalPart;
|
||||||
|
|
||||||
|
public FieldSelect(String finalPart) {
|
||||||
|
this.finalPart = finalPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFinalPart() {
|
||||||
|
return finalPart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a single method on the annotation class. For example, the value() method on the Getter annotation.
|
* Represents a single method on the annotation class. For example, the value() method on the Getter annotation.
|
||||||
*/
|
*/
|
||||||
@@ -50,8 +74,7 @@ public class AnnotationValues<A extends Annotation> {
|
|||||||
/** A list of the raw expressions. List is size 1 unless an array is provided. */
|
/** A list of the raw expressions. List is size 1 unless an array is provided. */
|
||||||
public final List<String> raws;
|
public final List<String> raws;
|
||||||
|
|
||||||
/** Guesses for each raw expression. If the raw expression is a literal expression, the guess will
|
/** Guesses for each raw expression. It's 'primitive' (String or primitive), an AV.ClassLiteral, an AV.FieldSelect, or an array of one of those. */
|
||||||
* likely be right. If not, it'll be wrong. */
|
|
||||||
public final List<Object> valueGuesses;
|
public final List<Object> valueGuesses;
|
||||||
|
|
||||||
/** A list of the actual expressions. List is size 1 unless an array is provided. */
|
/** A list of the actual expressions. List is size 1 unless an array is provided. */
|
||||||
@@ -190,7 +213,7 @@ public class AnnotationValues<A extends Annotation> {
|
|||||||
|
|
||||||
if (!isArray && v.valueGuesses.size() > 1) {
|
if (!isArray && v.valueGuesses.size() > 1) {
|
||||||
throw new AnnotationValueDecodeFail(v,
|
throw new AnnotationValueDecodeFail(v,
|
||||||
"Expected a single value, but " + method.getName() + " has an array of values", -1);
|
"Expected a single value, but " + method.getName() + " has an array of values", -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v.valueGuesses.size() == 0 && !isArray) {
|
if (v.valueGuesses.size() == 0 && !isArray) {
|
||||||
@@ -217,7 +240,7 @@ public class AnnotationValues<A extends Annotation> {
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
throw new AnnotationValueDecodeFail(v,
|
throw new AnnotationValueDecodeFail(v,
|
||||||
"I can't make sense of this annotation value. Try using a fully qualified literal.", idx);
|
"I can't make sense of this annotation value. Try using a fully qualified literal.", idx);
|
||||||
}
|
}
|
||||||
Array.set(array, idx++, result);
|
Array.set(array, idx++, result);
|
||||||
}
|
}
|
||||||
@@ -232,46 +255,46 @@ public class AnnotationValues<A extends Annotation> {
|
|||||||
private Object guessToType(Object guess, Class<?> expected, AnnotationValue v, int pos) {
|
private Object guessToType(Object guess, Class<?> expected, AnnotationValue v, int pos) {
|
||||||
if (expected == int.class) {
|
if (expected == int.class) {
|
||||||
if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
|
if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
|
||||||
return ((Number)guess).intValue();
|
return ((Number) guess).intValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expected == long.class) {
|
if (expected == long.class) {
|
||||||
if (guess instanceof Long || guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
|
if (guess instanceof Long || guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
|
||||||
return ((Number)guess).longValue();
|
return ((Number) guess).longValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expected == short.class) {
|
if (expected == short.class) {
|
||||||
if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
|
if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
|
||||||
int intVal = ((Number)guess).intValue();
|
int intVal = ((Number) guess).intValue();
|
||||||
int shortVal = ((Number)guess).shortValue();
|
int shortVal = ((Number) guess).shortValue();
|
||||||
if (shortVal == intVal) return shortVal;
|
if (shortVal == intVal) return shortVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expected == byte.class) {
|
if (expected == byte.class) {
|
||||||
if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
|
if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
|
||||||
int intVal = ((Number)guess).intValue();
|
int intVal = ((Number) guess).intValue();
|
||||||
int byteVal = ((Number)guess).byteValue();
|
int byteVal = ((Number) guess).byteValue();
|
||||||
if (byteVal == intVal) return byteVal;
|
if (byteVal == intVal) return byteVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expected == double.class) {
|
if (expected == double.class) {
|
||||||
if (guess instanceof Number) return ((Number)guess).doubleValue();
|
if (guess instanceof Number) return ((Number) guess).doubleValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expected == float.class) {
|
if (expected == float.class) {
|
||||||
if (guess instanceof Number) return ((Number)guess).floatValue();
|
if (guess instanceof Number) return ((Number) guess).floatValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expected == boolean.class) {
|
if (expected == boolean.class) {
|
||||||
if (guess instanceof Boolean) return ((Boolean)guess).booleanValue();
|
if (guess instanceof Boolean) return ((Boolean) guess).booleanValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expected == char.class) {
|
if (expected == char.class) {
|
||||||
if (guess instanceof Character) return ((Character)guess).charValue();
|
if (guess instanceof Character) return ((Character) guess).charValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expected == String.class) {
|
if (expected == String.class) {
|
||||||
@@ -279,31 +302,36 @@ public class AnnotationValues<A extends Annotation> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Enum.class.isAssignableFrom(expected) ) {
|
if (Enum.class.isAssignableFrom(expected) ) {
|
||||||
if (guess instanceof String) {
|
if (guess instanceof FieldSelect) {
|
||||||
|
String fieldSel = ((FieldSelect) guess).getFinalPart();
|
||||||
for (Object enumConstant : expected.getEnumConstants()) {
|
for (Object enumConstant : expected.getEnumConstants()) {
|
||||||
String target = ((Enum<?>)enumConstant).name();
|
String target = ((Enum<?>) enumConstant).name();
|
||||||
if (target.equals(guess)) return enumConstant;
|
if (target.equals(fieldSel)) return enumConstant;
|
||||||
}
|
}
|
||||||
throw new AnnotationValueDecodeFail(v,
|
throw new AnnotationValueDecodeFail(v,
|
||||||
"Can't translate " + guess + " to an enum of type " + expected, pos);
|
"Can't translate " + fieldSel + " to an enum of type " + expected, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Class.class == expected) {
|
if (Class.class == expected) {
|
||||||
if (guess instanceof String) try {
|
if (guess instanceof ClassLiteral) try {
|
||||||
return Class.forName(toFQ((String)guess));
|
String classLit = ((ClassLiteral) guess).getClassName();
|
||||||
|
return Class.forName(toFQ(classLit));
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
throw new AnnotationValueDecodeFail(v,
|
throw new AnnotationValueDecodeFail(v,
|
||||||
"Can't translate " + guess + " to a class object.", pos);
|
"Can't translate " + guess + " to a class object.", pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (guess instanceof AnnotationValues) {
|
if (guess instanceof AnnotationValues) {
|
||||||
return ((AnnotationValues) guess).getInstance();
|
return ((AnnotationValues<?>) guess).getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (guess instanceof FieldSelect) throw new AnnotationValueDecodeFail(v,
|
||||||
|
"You must use constant literals in lombok annotations; they cannot be references to (static) fields.", pos);
|
||||||
|
|
||||||
throw new AnnotationValueDecodeFail(v,
|
throw new AnnotationValueDecodeFail(v,
|
||||||
"Can't translate a " + guess.getClass() + " to the expected " + expected, pos);
|
"Can't translate a " + guess.getClass() + " to the expected " + expected, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -746,7 +746,7 @@ public class EclipseHandlerUtil {
|
|||||||
* Provides AnnotationValues with the data it needs to do its thing.
|
* Provides AnnotationValues with the data it needs to do its thing.
|
||||||
*/
|
*/
|
||||||
public static <A extends java.lang.annotation.Annotation> AnnotationValues<A>
|
public static <A extends java.lang.annotation.Annotation> AnnotationValues<A>
|
||||||
createAnnotation(Class<A> type, final EclipseNode annotationNode) {
|
createAnnotation(Class<A> type, final EclipseNode annotationNode) {
|
||||||
|
|
||||||
final Annotation annotation = (Annotation) annotationNode.get();
|
final Annotation annotation = (Annotation) annotationNode.get();
|
||||||
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
|
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
|
||||||
@@ -763,7 +763,7 @@ public class EclipseHandlerUtil {
|
|||||||
String mName = (n == null || n.length == 0) ? "value" : new String(pair.name);
|
String mName = (n == null || n.length == 0) ? "value" : new String(pair.name);
|
||||||
final Expression rhs = pair.value;
|
final Expression rhs = pair.value;
|
||||||
if (rhs instanceof ArrayInitializer) {
|
if (rhs instanceof ArrayInitializer) {
|
||||||
expressions = ((ArrayInitializer)rhs).expressions;
|
expressions = ((ArrayInitializer) rhs).expressions;
|
||||||
} else if (rhs != null) {
|
} else if (rhs != null) {
|
||||||
expressions = new Expression[] { rhs };
|
expressions = new Expression[] { rhs };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -396,7 +396,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
|
|||||||
oldSource = log.useSource(newSource);
|
oldSource = log.useSource(newSource);
|
||||||
if (pos == null) pos = astObject.pos();
|
if (pos == null) pos = astObject.pos();
|
||||||
}
|
}
|
||||||
if (pos != null && attemptToRemoveErrorsInRange) {
|
if (pos != null && node != null && attemptToRemoveErrorsInRange) {
|
||||||
removeFromDeferredDiagnostics(pos.getStartPosition(), node.getEndPosition(pos));
|
removeFromDeferredDiagnostics(pos.getStartPosition(), node.getEndPosition(pos));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -360,7 +360,10 @@ public class JavacHandlerUtil {
|
|||||||
expressions.add(inner);
|
expressions.add(inner);
|
||||||
if (inner instanceof JCAnnotation) {
|
if (inner instanceof JCAnnotation) {
|
||||||
try {
|
try {
|
||||||
guesses.add(createAnnotation((Class<A>) Class.forName(inner.type.toString()), (JCAnnotation) inner, node));
|
@SuppressWarnings("unchecked")
|
||||||
|
Class<A> innerClass = (Class<A>) Class.forName(inner.type.toString());
|
||||||
|
|
||||||
|
guesses.add(createAnnotation(innerClass, (JCAnnotation) inner, node));
|
||||||
} catch (ClassNotFoundException ex) {
|
} catch (ClassNotFoundException ex) {
|
||||||
throw new IllegalStateException(ex);
|
throw new IllegalStateException(ex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
|
|||||||
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
|
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
|
||||||
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
|
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
|
||||||
|
|
||||||
|
import lombok.core.AnnotationValues;
|
||||||
|
|
||||||
public class Eclipse {
|
public class Eclipse {
|
||||||
private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0];
|
private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0];
|
||||||
/**
|
/**
|
||||||
@@ -161,7 +163,7 @@ public class Eclipse {
|
|||||||
*/
|
*/
|
||||||
public static Object calculateValue(Expression e) {
|
public static Object calculateValue(Expression e) {
|
||||||
if (e instanceof Literal) {
|
if (e instanceof Literal) {
|
||||||
((Literal)e).computeConstant();
|
((Literal) e).computeConstant();
|
||||||
switch (e.constant.typeID()) {
|
switch (e.constant.typeID()) {
|
||||||
case TypeIds.T_int: return e.constant.intValue();
|
case TypeIds.T_int: return e.constant.intValue();
|
||||||
case TypeIds.T_byte: return e.constant.byteValue();
|
case TypeIds.T_byte: return e.constant.byteValue();
|
||||||
@@ -175,13 +177,13 @@ public class Eclipse {
|
|||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
} else if (e instanceof ClassLiteralAccess) {
|
} else if (e instanceof ClassLiteralAccess) {
|
||||||
return Eclipse.toQualifiedName(((ClassLiteralAccess)e).type.getTypeName());
|
return new AnnotationValues.ClassLiteral(Eclipse.toQualifiedName(((ClassLiteralAccess)e).type.getTypeName()));
|
||||||
} else if (e instanceof SingleNameReference) {
|
} else if (e instanceof SingleNameReference) {
|
||||||
return new String(((SingleNameReference)e).token);
|
return new AnnotationValues.FieldSelect(new String(((SingleNameReference)e).token));
|
||||||
} else if (e instanceof QualifiedNameReference) {
|
} else if (e instanceof QualifiedNameReference) {
|
||||||
String qName = Eclipse.toQualifiedName(((QualifiedNameReference)e).tokens);
|
String qName = Eclipse.toQualifiedName(((QualifiedNameReference)e).tokens);
|
||||||
int idx = qName.lastIndexOf('.');
|
int idx = qName.lastIndexOf('.');
|
||||||
return idx == -1 ? qName : qName.substring(idx+1);
|
return new AnnotationValues.FieldSelect(idx == -1 ? qName : qName.substring(idx+1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import javax.lang.model.type.NoType;
|
|||||||
import javax.lang.model.type.TypeKind;
|
import javax.lang.model.type.TypeKind;
|
||||||
import javax.lang.model.type.TypeVisitor;
|
import javax.lang.model.type.TypeVisitor;
|
||||||
|
|
||||||
|
import lombok.core.AnnotationValues;
|
||||||
import lombok.javac.JavacTreeMaker.TreeTag;
|
import lombok.javac.JavacTreeMaker.TreeTag;
|
||||||
import lombok.javac.JavacTreeMaker.TypeTag;
|
import lombok.javac.JavacTreeMaker.TypeTag;
|
||||||
|
|
||||||
@@ -143,16 +144,17 @@ public class Javac {
|
|||||||
return ((Number) lit.value).intValue() == 0 ? false : true;
|
return ((Number) lit.value).intValue() == 0 ? false : true;
|
||||||
}
|
}
|
||||||
return lit.value;
|
return lit.value;
|
||||||
} else if (expr instanceof JCIdent || expr instanceof JCFieldAccess) {
|
}
|
||||||
|
|
||||||
|
if (expr instanceof JCIdent || expr instanceof JCFieldAccess) {
|
||||||
String x = expr.toString();
|
String x = expr.toString();
|
||||||
if (x.endsWith(".class")) x = x.substring(0, x.length() - 6);
|
if (x.endsWith(".class")) return new AnnotationValues.ClassLiteral(x.substring(0, x.length() - 6));
|
||||||
else {
|
int idx = x.lastIndexOf('.');
|
||||||
int idx = x.lastIndexOf('.');
|
if (idx > -1) x = x.substring(idx + 1);
|
||||||
if (idx > -1) x = x.substring(idx + 1);
|
return new AnnotationValues.FieldSelect(x);
|
||||||
}
|
}
|
||||||
return x;
|
|
||||||
} else
|
return null;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final TypeTag CTC_BOOLEAN = typeTag("BOOLEAN");
|
public static final TypeTag CTC_BOOLEAN = typeTag("BOOLEAN");
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
//skip compare contents
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString(of = ToStringWithConstantRefInOf.FIELD_NAME)
|
||||||
|
public class ToStringWithConstantRefInOf {
|
||||||
|
static final String FIELD_NAME = "id";
|
||||||
|
private String id;
|
||||||
|
private int whatever;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
4 You must use constant literals in lombok annotations; they cannot be references to (static) fields.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
4 You must use constant literals in lombok annotations; they cannot be references to (static) fields.
|
||||||
Reference in new issue
Block a user