mirror of
https://github.com/tiennm99/lombok.git
synced 2026-09-04 08:19:37 +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:
@@ -43,6 +43,30 @@ public class AnnotationValues<A extends Annotation> {
|
||||
private final Map<String, AnnotationValue> values;
|
||||
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.
|
||||
*/
|
||||
@@ -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. */
|
||||
public final List<String> raws;
|
||||
|
||||
/** Guesses for each raw expression. If the raw expression is a literal expression, the guess will
|
||||
* likely be right. If not, it'll be wrong. */
|
||||
/** Guesses for each raw expression. It's 'primitive' (String or primitive), an AV.ClassLiteral, an AV.FieldSelect, or an array of one of those. */
|
||||
public final List<Object> valueGuesses;
|
||||
|
||||
/** 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) {
|
||||
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) {
|
||||
@@ -217,7 +240,7 @@ public class AnnotationValues<A extends Annotation> {
|
||||
return defaultValue;
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -232,46 +255,46 @@ public class AnnotationValues<A extends Annotation> {
|
||||
private Object guessToType(Object guess, Class<?> expected, AnnotationValue v, int pos) {
|
||||
if (expected == int.class) {
|
||||
if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
|
||||
return ((Number)guess).intValue();
|
||||
return ((Number) guess).intValue();
|
||||
}
|
||||
}
|
||||
|
||||
if (expected == long.class) {
|
||||
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 (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
|
||||
int intVal = ((Number)guess).intValue();
|
||||
int shortVal = ((Number)guess).shortValue();
|
||||
int intVal = ((Number) guess).intValue();
|
||||
int shortVal = ((Number) guess).shortValue();
|
||||
if (shortVal == intVal) return shortVal;
|
||||
}
|
||||
}
|
||||
|
||||
if (expected == byte.class) {
|
||||
if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
|
||||
int intVal = ((Number)guess).intValue();
|
||||
int byteVal = ((Number)guess).byteValue();
|
||||
int intVal = ((Number) guess).intValue();
|
||||
int byteVal = ((Number) guess).byteValue();
|
||||
if (byteVal == intVal) return byteVal;
|
||||
}
|
||||
}
|
||||
|
||||
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 (guess instanceof Number) return ((Number)guess).floatValue();
|
||||
if (guess instanceof Number) return ((Number) guess).floatValue();
|
||||
}
|
||||
|
||||
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 (guess instanceof Character) return ((Character)guess).charValue();
|
||||
if (guess instanceof Character) return ((Character) guess).charValue();
|
||||
}
|
||||
|
||||
if (expected == String.class) {
|
||||
@@ -279,31 +302,36 @@ public class AnnotationValues<A extends Annotation> {
|
||||
}
|
||||
|
||||
if (Enum.class.isAssignableFrom(expected) ) {
|
||||
if (guess instanceof String) {
|
||||
if (guess instanceof FieldSelect) {
|
||||
String fieldSel = ((FieldSelect) guess).getFinalPart();
|
||||
for (Object enumConstant : expected.getEnumConstants()) {
|
||||
String target = ((Enum<?>)enumConstant).name();
|
||||
if (target.equals(guess)) return enumConstant;
|
||||
String target = ((Enum<?>) enumConstant).name();
|
||||
if (target.equals(fieldSel)) return enumConstant;
|
||||
}
|
||||
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 (guess instanceof String) try {
|
||||
return Class.forName(toFQ((String)guess));
|
||||
if (guess instanceof ClassLiteral) try {
|
||||
String classLit = ((ClassLiteral) guess).getClassName();
|
||||
return Class.forName(toFQ(classLit));
|
||||
} catch (ClassNotFoundException e) {
|
||||
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) {
|
||||
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,
|
||||
"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.
|
||||
*/
|
||||
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();
|
||||
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);
|
||||
final Expression rhs = pair.value;
|
||||
if (rhs instanceof ArrayInitializer) {
|
||||
expressions = ((ArrayInitializer)rhs).expressions;
|
||||
expressions = ((ArrayInitializer) rhs).expressions;
|
||||
} else if (rhs != null) {
|
||||
expressions = new Expression[] { rhs };
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
|
||||
oldSource = log.useSource(newSource);
|
||||
if (pos == null) pos = astObject.pos();
|
||||
}
|
||||
if (pos != null && attemptToRemoveErrorsInRange) {
|
||||
if (pos != null && node != null && attemptToRemoveErrorsInRange) {
|
||||
removeFromDeferredDiagnostics(pos.getStartPosition(), node.getEndPosition(pos));
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -360,7 +360,10 @@ public class JavacHandlerUtil {
|
||||
expressions.add(inner);
|
||||
if (inner instanceof JCAnnotation) {
|
||||
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) {
|
||||
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.lookup.TypeIds;
|
||||
|
||||
import lombok.core.AnnotationValues;
|
||||
|
||||
public class Eclipse {
|
||||
private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0];
|
||||
/**
|
||||
@@ -161,7 +163,7 @@ public class Eclipse {
|
||||
*/
|
||||
public static Object calculateValue(Expression e) {
|
||||
if (e instanceof Literal) {
|
||||
((Literal)e).computeConstant();
|
||||
((Literal) e).computeConstant();
|
||||
switch (e.constant.typeID()) {
|
||||
case TypeIds.T_int: return e.constant.intValue();
|
||||
case TypeIds.T_byte: return e.constant.byteValue();
|
||||
@@ -175,13 +177,13 @@ public class Eclipse {
|
||||
default: return null;
|
||||
}
|
||||
} 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) {
|
||||
return new String(((SingleNameReference)e).token);
|
||||
return new AnnotationValues.FieldSelect(new String(((SingleNameReference)e).token));
|
||||
} else if (e instanceof QualifiedNameReference) {
|
||||
String qName = Eclipse.toQualifiedName(((QualifiedNameReference)e).tokens);
|
||||
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;
|
||||
|
||||
@@ -36,6 +36,7 @@ import javax.lang.model.type.NoType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeVisitor;
|
||||
|
||||
import lombok.core.AnnotationValues;
|
||||
import lombok.javac.JavacTreeMaker.TreeTag;
|
||||
import lombok.javac.JavacTreeMaker.TypeTag;
|
||||
|
||||
@@ -143,16 +144,17 @@ public class Javac {
|
||||
return ((Number) lit.value).intValue() == 0 ? false : true;
|
||||
}
|
||||
return lit.value;
|
||||
} else if (expr instanceof JCIdent || expr instanceof JCFieldAccess) {
|
||||
}
|
||||
|
||||
if (expr instanceof JCIdent || expr instanceof JCFieldAccess) {
|
||||
String x = expr.toString();
|
||||
if (x.endsWith(".class")) x = x.substring(0, x.length() - 6);
|
||||
else {
|
||||
int idx = x.lastIndexOf('.');
|
||||
if (idx > -1) x = x.substring(idx + 1);
|
||||
}
|
||||
return x;
|
||||
} else
|
||||
return null;
|
||||
if (x.endsWith(".class")) return new AnnotationValues.ClassLiteral(x.substring(0, x.length() - 6));
|
||||
int idx = x.lastIndexOf('.');
|
||||
if (idx > -1) x = x.substring(idx + 1);
|
||||
return new AnnotationValues.FieldSelect(x);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
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