Perform NullAway build-time checks in spring-expression
See gh-32475
This commit is contained in:
parent
1ccd5512c5
commit
f648fd7c3b
|
|
@ -117,7 +117,7 @@ tasks.withType(JavaCompile).configureEach {
|
||||||
options.errorprone {
|
options.errorprone {
|
||||||
disableAllChecks = true
|
disableAllChecks = true
|
||||||
option("NullAway:CustomContractAnnotations", "org.springframework.lang.Contract")
|
option("NullAway:CustomContractAnnotations", "org.springframework.lang.Contract")
|
||||||
option("NullAway:AnnotatedPackages", "org.springframework.core")
|
option("NullAway:AnnotatedPackages", "org.springframework.core,org.springframework.expression")
|
||||||
option("NullAway:UnannotatedSubPackages", "org.springframework.instrument,org.springframework.context.index," +
|
option("NullAway:UnannotatedSubPackages", "org.springframework.instrument,org.springframework.context.index," +
|
||||||
"org.springframework.asm,org.springframework.cglib,org.springframework.objenesis," +
|
"org.springframework.asm,org.springframework.cglib,org.springframework.objenesis," +
|
||||||
"org.springframework.javapoet,org.springframework.aot.nativex.substitution")
|
"org.springframework.javapoet,org.springframework.aot.nativex.substitution")
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import java.util.stream.Stream;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
|
import org.springframework.lang.Contract;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
@ -562,6 +563,7 @@ public class TypeDescriptor implements Serializable {
|
||||||
* @return the type descriptor
|
* @return the type descriptor
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@Contract("!null -> !null; null -> null")
|
||||||
public static TypeDescriptor forObject(@Nullable Object source) {
|
public static TypeDescriptor forObject(@Nullable Object source) {
|
||||||
return (source != null ? valueOf(source.getClass()) : null);
|
return (source != null ? valueOf(source.getClass()) : null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -312,6 +312,7 @@ public abstract class Assert {
|
||||||
* @param message the exception message to use if the assertion fails
|
* @param message the exception message to use if the assertion fails
|
||||||
* @throws IllegalArgumentException if the object array is {@code null} or contains no elements
|
* @throws IllegalArgumentException if the object array is {@code null} or contains no elements
|
||||||
*/
|
*/
|
||||||
|
@Contract("null, _ -> fail")
|
||||||
public static void notEmpty(@Nullable Object[] array, String message) {
|
public static void notEmpty(@Nullable Object[] array, String message) {
|
||||||
if (ObjectUtils.isEmpty(array)) {
|
if (ObjectUtils.isEmpty(array)) {
|
||||||
throw new IllegalArgumentException(message);
|
throw new IllegalArgumentException(message);
|
||||||
|
|
@ -330,6 +331,7 @@ public abstract class Assert {
|
||||||
* @throws IllegalArgumentException if the object array is {@code null} or contains no elements
|
* @throws IllegalArgumentException if the object array is {@code null} or contains no elements
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
|
@Contract("null, _ -> fail")
|
||||||
public static void notEmpty(@Nullable Object[] array, Supplier<String> messageSupplier) {
|
public static void notEmpty(@Nullable Object[] array, Supplier<String> messageSupplier) {
|
||||||
if (ObjectUtils.isEmpty(array)) {
|
if (ObjectUtils.isEmpty(array)) {
|
||||||
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
|
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import java.util.Optional;
|
||||||
import java.util.StringJoiner;
|
import java.util.StringJoiner;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.springframework.lang.Contract;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -110,6 +111,7 @@ public abstract class ObjectUtils {
|
||||||
* @param array the array to check
|
* @param array the array to check
|
||||||
* @see #isEmpty(Object)
|
* @see #isEmpty(Object)
|
||||||
*/
|
*/
|
||||||
|
@Contract("null -> true")
|
||||||
public static boolean isEmpty(@Nullable Object[] array) {
|
public static boolean isEmpty(@Nullable Object[] array) {
|
||||||
return (array == null || array.length == 0);
|
return (array == null || array.length == 0);
|
||||||
}
|
}
|
||||||
|
|
@ -331,6 +333,7 @@ public abstract class ObjectUtils {
|
||||||
* @see Object#equals(Object)
|
* @see Object#equals(Object)
|
||||||
* @see java.util.Arrays#equals
|
* @see java.util.Arrays#equals
|
||||||
*/
|
*/
|
||||||
|
@Contract("null, null -> true; null, _ -> false; _, null -> false")
|
||||||
public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
|
public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
|
||||||
if (o1 == o2) {
|
if (o1 == o2) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import java.util.Map;
|
||||||
import org.springframework.asm.ClassWriter;
|
import org.springframework.asm.ClassWriter;
|
||||||
import org.springframework.asm.MethodVisitor;
|
import org.springframework.asm.MethodVisitor;
|
||||||
import org.springframework.asm.Opcodes;
|
import org.springframework.asm.Opcodes;
|
||||||
|
import org.springframework.lang.Contract;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
@ -583,6 +584,7 @@ public class CodeFlow implements Opcodes {
|
||||||
* @param descriptor type descriptor
|
* @param descriptor type descriptor
|
||||||
* @return {@code true} if the descriptor is boolean compatible
|
* @return {@code true} if the descriptor is boolean compatible
|
||||||
*/
|
*/
|
||||||
|
@Contract("null -> false")
|
||||||
public static boolean isBooleanCompatible(@Nullable String descriptor) {
|
public static boolean isBooleanCompatible(@Nullable String descriptor) {
|
||||||
return (descriptor != null && (descriptor.equals("Z") || descriptor.equals("Ljava/lang/Boolean")));
|
return (descriptor != null && (descriptor.equals("Z") || descriptor.equals("Ljava/lang/Boolean")));
|
||||||
}
|
}
|
||||||
|
|
@ -592,6 +594,7 @@ public class CodeFlow implements Opcodes {
|
||||||
* @param descriptor type descriptor
|
* @param descriptor type descriptor
|
||||||
* @return {@code true} if a primitive type or {@code void}
|
* @return {@code true} if a primitive type or {@code void}
|
||||||
*/
|
*/
|
||||||
|
@Contract("null -> false")
|
||||||
public static boolean isPrimitive(@Nullable String descriptor) {
|
public static boolean isPrimitive(@Nullable String descriptor) {
|
||||||
return (descriptor != null && descriptor.length() == 1);
|
return (descriptor != null && descriptor.length() == 1);
|
||||||
}
|
}
|
||||||
|
|
@ -601,6 +604,7 @@ public class CodeFlow implements Opcodes {
|
||||||
* @param descriptor the descriptor for a possible primitive array
|
* @param descriptor the descriptor for a possible primitive array
|
||||||
* @return {@code true} if the descriptor a primitive array
|
* @return {@code true} if the descriptor a primitive array
|
||||||
*/
|
*/
|
||||||
|
@Contract("null -> false")
|
||||||
public static boolean isPrimitiveArray(@Nullable String descriptor) {
|
public static boolean isPrimitiveArray(@Nullable String descriptor) {
|
||||||
if (descriptor == null) {
|
if (descriptor == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -653,6 +657,7 @@ public class CodeFlow implements Opcodes {
|
||||||
* @param descriptor the descriptor for a type
|
* @param descriptor the descriptor for a type
|
||||||
* @return {@code true} if the descriptor is for a supported numeric type or boolean
|
* @return {@code true} if the descriptor is for a supported numeric type or boolean
|
||||||
*/
|
*/
|
||||||
|
@Contract("null -> false")
|
||||||
public static boolean isPrimitiveOrUnboxableSupportedNumberOrBoolean(@Nullable String descriptor) {
|
public static boolean isPrimitiveOrUnboxableSupportedNumberOrBoolean(@Nullable String descriptor) {
|
||||||
if (descriptor == null) {
|
if (descriptor == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -670,6 +675,7 @@ public class CodeFlow implements Opcodes {
|
||||||
* @param descriptor the descriptor for a type
|
* @param descriptor the descriptor for a type
|
||||||
* @return {@code true} if the descriptor is for a supported numeric type
|
* @return {@code true} if the descriptor is for a supported numeric type
|
||||||
*/
|
*/
|
||||||
|
@Contract("null -> false")
|
||||||
public static boolean isPrimitiveOrUnboxableSupportedNumber(@Nullable String descriptor) {
|
public static boolean isPrimitiveOrUnboxableSupportedNumber(@Nullable String descriptor) {
|
||||||
if (descriptor == null) {
|
if (descriptor == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -690,6 +696,7 @@ public class CodeFlow implements Opcodes {
|
||||||
* @param number the number to check
|
* @param number the number to check
|
||||||
* @return {@code true} if it is an {@link Integer}, {@link Short} or {@link Byte}
|
* @return {@code true} if it is an {@link Integer}, {@link Short} or {@link Byte}
|
||||||
*/
|
*/
|
||||||
|
@Contract("null -> false")
|
||||||
public static boolean isIntegerForNumericOp(Number number) {
|
public static boolean isIntegerForNumericOp(Number number) {
|
||||||
return (number instanceof Integer || number instanceof Short || number instanceof Byte);
|
return (number instanceof Integer || number instanceof Short || number instanceof Byte);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -325,6 +325,7 @@ public class Indexer extends SpelNodeImpl {
|
||||||
CompilablePropertyAccessor compilablePropertyAccessor = (CompilablePropertyAccessor) this.cachedReadAccessor;
|
CompilablePropertyAccessor compilablePropertyAccessor = (CompilablePropertyAccessor) this.cachedReadAccessor;
|
||||||
Assert.state(compilablePropertyAccessor != null, "No cached read accessor");
|
Assert.state(compilablePropertyAccessor != null, "No cached read accessor");
|
||||||
String propertyName = (String) stringLiteral.getLiteralValue().getValue();
|
String propertyName = (String) stringLiteral.getLiteralValue().getValue();
|
||||||
|
Assert.state(propertyName != null, "No property name");
|
||||||
compilablePropertyAccessor.generateCode(propertyName, mv, cf);
|
compilablePropertyAccessor.generateCode(propertyName, mv, cf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -565,6 +566,7 @@ public class Indexer extends SpelNodeImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
public TypedValue getValue() {
|
public TypedValue getValue() {
|
||||||
Class<?> targetObjectRuntimeClass = getObjectClass(this.targetObject);
|
Class<?> targetObjectRuntimeClass = getObjectClass(this.targetObject);
|
||||||
try {
|
try {
|
||||||
|
|
@ -603,6 +605,7 @@ public class Indexer extends SpelNodeImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
public void setValue(@Nullable Object newValue) {
|
public void setValue(@Nullable Object newValue) {
|
||||||
Class<?> contextObjectClass = getObjectClass(this.targetObject);
|
Class<?> contextObjectClass = getObjectClass(this.targetObject);
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import org.springframework.expression.spel.ExpressionState;
|
||||||
import org.springframework.expression.spel.SpelEvaluationException;
|
import org.springframework.expression.spel.SpelEvaluationException;
|
||||||
import org.springframework.expression.spel.SpelMessage;
|
import org.springframework.expression.spel.SpelMessage;
|
||||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||||
|
import org.springframework.lang.Contract;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -64,6 +65,7 @@ public class OpAnd extends Operator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract("null -> fail")
|
||||||
private void assertValueNotNull(@Nullable Boolean value) {
|
private void assertValueNotNull(@Nullable Boolean value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
|
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
|
||||||
|
|
|
||||||
|
|
@ -68,19 +68,17 @@ public class OpEQ extends Operator {
|
||||||
cf.loadEvaluationContext(mv);
|
cf.loadEvaluationContext(mv);
|
||||||
String leftDesc = getLeftOperand().exitTypeDescriptor;
|
String leftDesc = getLeftOperand().exitTypeDescriptor;
|
||||||
String rightDesc = getRightOperand().exitTypeDescriptor;
|
String rightDesc = getRightOperand().exitTypeDescriptor;
|
||||||
boolean leftPrim = CodeFlow.isPrimitive(leftDesc);
|
|
||||||
boolean rightPrim = CodeFlow.isPrimitive(rightDesc);
|
|
||||||
|
|
||||||
cf.enterCompilationScope();
|
cf.enterCompilationScope();
|
||||||
getLeftOperand().generateCode(mv, cf);
|
getLeftOperand().generateCode(mv, cf);
|
||||||
cf.exitCompilationScope();
|
cf.exitCompilationScope();
|
||||||
if (leftPrim) {
|
if (CodeFlow.isPrimitive(leftDesc)) {
|
||||||
CodeFlow.insertBoxIfNecessary(mv, leftDesc.charAt(0));
|
CodeFlow.insertBoxIfNecessary(mv, leftDesc.charAt(0));
|
||||||
}
|
}
|
||||||
cf.enterCompilationScope();
|
cf.enterCompilationScope();
|
||||||
getRightOperand().generateCode(mv, cf);
|
getRightOperand().generateCode(mv, cf);
|
||||||
cf.exitCompilationScope();
|
cf.exitCompilationScope();
|
||||||
if (rightPrim) {
|
if (CodeFlow.isPrimitive(rightDesc)) {
|
||||||
CodeFlow.insertBoxIfNecessary(mv, rightDesc.charAt(0));
|
CodeFlow.insertBoxIfNecessary(mv, rightDesc.charAt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,19 +69,17 @@ public class OpNE extends Operator {
|
||||||
cf.loadEvaluationContext(mv);
|
cf.loadEvaluationContext(mv);
|
||||||
String leftDesc = getLeftOperand().exitTypeDescriptor;
|
String leftDesc = getLeftOperand().exitTypeDescriptor;
|
||||||
String rightDesc = getRightOperand().exitTypeDescriptor;
|
String rightDesc = getRightOperand().exitTypeDescriptor;
|
||||||
boolean leftPrim = CodeFlow.isPrimitive(leftDesc);
|
|
||||||
boolean rightPrim = CodeFlow.isPrimitive(rightDesc);
|
|
||||||
|
|
||||||
cf.enterCompilationScope();
|
cf.enterCompilationScope();
|
||||||
getLeftOperand().generateCode(mv, cf);
|
getLeftOperand().generateCode(mv, cf);
|
||||||
cf.exitCompilationScope();
|
cf.exitCompilationScope();
|
||||||
if (leftPrim) {
|
if (CodeFlow.isPrimitive(leftDesc)) {
|
||||||
CodeFlow.insertBoxIfNecessary(mv, leftDesc.charAt(0));
|
CodeFlow.insertBoxIfNecessary(mv, leftDesc.charAt(0));
|
||||||
}
|
}
|
||||||
cf.enterCompilationScope();
|
cf.enterCompilationScope();
|
||||||
getRightOperand().generateCode(mv, cf);
|
getRightOperand().generateCode(mv, cf);
|
||||||
cf.exitCompilationScope();
|
cf.exitCompilationScope();
|
||||||
if (rightPrim) {
|
if (CodeFlow.isPrimitive(rightDesc)) {
|
||||||
CodeFlow.insertBoxIfNecessary(mv, rightDesc.charAt(0));
|
CodeFlow.insertBoxIfNecessary(mv, rightDesc.charAt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import org.springframework.expression.spel.ExpressionState;
|
||||||
import org.springframework.expression.spel.SpelEvaluationException;
|
import org.springframework.expression.spel.SpelEvaluationException;
|
||||||
import org.springframework.expression.spel.SpelMessage;
|
import org.springframework.expression.spel.SpelMessage;
|
||||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||||
|
import org.springframework.lang.Contract;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -63,6 +64,7 @@ public class OpOr extends Operator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract("null -> fail")
|
||||||
private void assertValueNotNull(@Nullable Boolean value) {
|
private void assertValueNotNull(@Nullable Boolean value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
|
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
|
||||||
|
|
|
||||||
|
|
@ -352,6 +352,7 @@ public abstract class Operator extends SpelNodeImpl {
|
||||||
* @param rightActualDescriptor the dynamic/runtime right object descriptor
|
* @param rightActualDescriptor the dynamic/runtime right object descriptor
|
||||||
* @return a DescriptorComparison object indicating the type of compatibility, if any
|
* @return a DescriptorComparison object indicating the type of compatibility, if any
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
public static DescriptorComparison checkNumericCompatibility(
|
public static DescriptorComparison checkNumericCompatibility(
|
||||||
@Nullable String leftDeclaredDescriptor, @Nullable String rightDeclaredDescriptor,
|
@Nullable String leftDeclaredDescriptor, @Nullable String rightDeclaredDescriptor,
|
||||||
@Nullable String leftActualDescriptor, @Nullable String rightActualDescriptor) {
|
@Nullable String leftActualDescriptor, @Nullable String rightActualDescriptor) {
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||||
// 'simple' object
|
// 'simple' object
|
||||||
try {
|
try {
|
||||||
if (isWritableProperty(this.name,contextObject, evalContext)) {
|
if (isWritableProperty(this.name,contextObject, evalContext)) {
|
||||||
Class<?> clazz = result.getTypeDescriptor().getType();
|
Class<?> clazz = resultDescriptor.getType();
|
||||||
Object newObject = ReflectionUtils.accessibleConstructor(clazz).newInstance();
|
Object newObject = ReflectionUtils.accessibleConstructor(clazz).newInstance();
|
||||||
writeProperty(contextObject, evalContext, this.name, newObject);
|
writeProperty(contextObject, evalContext, this.name, newObject);
|
||||||
result = readProperty(contextObject, evalContext, this.name);
|
result = readProperty(contextObject, evalContext, this.name);
|
||||||
|
|
@ -142,11 +142,11 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||||
}
|
}
|
||||||
catch (InvocationTargetException ex) {
|
catch (InvocationTargetException ex) {
|
||||||
throw new SpelEvaluationException(getStartPosition(), ex.getTargetException(),
|
throw new SpelEvaluationException(getStartPosition(), ex.getTargetException(),
|
||||||
SpelMessage.UNABLE_TO_DYNAMICALLY_CREATE_OBJECT, result.getTypeDescriptor().getType());
|
SpelMessage.UNABLE_TO_DYNAMICALLY_CREATE_OBJECT, resultDescriptor.getType());
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
throw new SpelEvaluationException(getStartPosition(), ex,
|
throw new SpelEvaluationException(getStartPosition(), ex,
|
||||||
SpelMessage.UNABLE_TO_DYNAMICALLY_CREATE_OBJECT, result.getTypeDescriptor().getType());
|
SpelMessage.UNABLE_TO_DYNAMICALLY_CREATE_OBJECT, resultDescriptor.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ import org.springframework.expression.spel.ast.StringLiteral;
|
||||||
import org.springframework.expression.spel.ast.Ternary;
|
import org.springframework.expression.spel.ast.Ternary;
|
||||||
import org.springframework.expression.spel.ast.TypeReference;
|
import org.springframework.expression.spel.ast.TypeReference;
|
||||||
import org.springframework.expression.spel.ast.VariableReference;
|
import org.springframework.expression.spel.ast.VariableReference;
|
||||||
|
import org.springframework.lang.Contract;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
|
@ -164,6 +165,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
// | (QMARK^ expression COLON! expression)
|
// | (QMARK^ expression COLON! expression)
|
||||||
// | (ELVIS^ expression))?;
|
// | (ELVIS^ expression))?;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
private SpelNodeImpl eatExpression() {
|
private SpelNodeImpl eatExpression() {
|
||||||
SpelNodeImpl expr = eatLogicalOrExpression();
|
SpelNodeImpl expr = eatLogicalOrExpression();
|
||||||
Token t = peekToken();
|
Token t = peekToken();
|
||||||
|
|
@ -274,6 +276,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
|
|
||||||
//sumExpression: productExpression ( (PLUS^ | MINUS^) productExpression)*;
|
//sumExpression: productExpression ( (PLUS^ | MINUS^) productExpression)*;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
private SpelNodeImpl eatSumExpression() {
|
private SpelNodeImpl eatSumExpression() {
|
||||||
SpelNodeImpl expr = eatProductExpression();
|
SpelNodeImpl expr = eatProductExpression();
|
||||||
while (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) {
|
while (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) {
|
||||||
|
|
@ -313,6 +316,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
|
|
||||||
// powerExpr : unaryExpression (POWER^ unaryExpression)? (INC || DEC) ;
|
// powerExpr : unaryExpression (POWER^ unaryExpression)? (INC || DEC) ;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
private SpelNodeImpl eatPowerIncDecExpression() {
|
private SpelNodeImpl eatPowerIncDecExpression() {
|
||||||
SpelNodeImpl expr = eatUnaryExpression();
|
SpelNodeImpl expr = eatUnaryExpression();
|
||||||
if (peekToken(TokenKind.POWER)) {
|
if (peekToken(TokenKind.POWER)) {
|
||||||
|
|
@ -333,6 +337,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
|
|
||||||
// unaryExpression: (PLUS^ | MINUS^ | BANG^ | INC^ | DEC^) unaryExpression | primaryExpression ;
|
// unaryExpression: (PLUS^ | MINUS^ | BANG^ | INC^ | DEC^) unaryExpression | primaryExpression ;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
private SpelNodeImpl eatUnaryExpression() {
|
private SpelNodeImpl eatUnaryExpression() {
|
||||||
if (peekToken(TokenKind.NOT, TokenKind.PLUS, TokenKind.MINUS)) {
|
if (peekToken(TokenKind.NOT, TokenKind.PLUS, TokenKind.MINUS)) {
|
||||||
Token t = takeToken();
|
Token t = takeToken();
|
||||||
|
|
@ -755,6 +760,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
qualifiedIdPieces.getLast().getEndPosition(), qualifiedIdPieces.toArray(new SpelNodeImpl[0]));
|
qualifiedIdPieces.getLast().getEndPosition(), qualifiedIdPieces.toArray(new SpelNodeImpl[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract("null -> false")
|
||||||
private boolean isValidQualifiedId(@Nullable Token node) {
|
private boolean isValidQualifiedId(@Nullable Token node) {
|
||||||
if (node == null || node.kind == TokenKind.LITERAL_STRING) {
|
if (node == null || node.kind == TokenKind.LITERAL_STRING) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1040,17 +1046,20 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
return t.kind.toString().toLowerCase();
|
return t.kind.toString().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract("_, null, _ -> fail; _, _, null -> fail")
|
||||||
private void checkOperands(Token token, @Nullable SpelNodeImpl left, @Nullable SpelNodeImpl right) {
|
private void checkOperands(Token token, @Nullable SpelNodeImpl left, @Nullable SpelNodeImpl right) {
|
||||||
checkLeftOperand(token, left);
|
checkLeftOperand(token, left);
|
||||||
checkRightOperand(token, right);
|
checkRightOperand(token, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract("_, null -> fail")
|
||||||
private void checkLeftOperand(Token token, @Nullable SpelNodeImpl operandExpression) {
|
private void checkLeftOperand(Token token, @Nullable SpelNodeImpl operandExpression) {
|
||||||
if (operandExpression == null) {
|
if (operandExpression == null) {
|
||||||
throw internalException(token.startPos, SpelMessage.LEFT_OPERAND_PROBLEM);
|
throw internalException(token.startPos, SpelMessage.LEFT_OPERAND_PROBLEM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract("_, null -> fail")
|
||||||
private void checkRightOperand(Token token, @Nullable SpelNodeImpl operandExpression) {
|
private void checkRightOperand(Token token, @Nullable SpelNodeImpl operandExpression) {
|
||||||
if (operandExpression == null) {
|
if (operandExpression == null) {
|
||||||
throw internalException(token.startPos, SpelMessage.RIGHT_OPERAND_PROBLEM);
|
throw internalException(token.startPos, SpelMessage.RIGHT_OPERAND_PROBLEM);
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
public TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
|
public TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
|
||||||
Assert.state(target != null, "Target must not be null");
|
Assert.state(target != null, "Target must not be null");
|
||||||
Class<?> type = (target instanceof Class<?> clazz ? clazz : target.getClass());
|
Class<?> type = (target instanceof Class<?> clazz ? clazz : target.getClass());
|
||||||
|
|
@ -515,6 +516,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
* <p>Note: An optimized accessor is currently only usable for read attempts.
|
* <p>Note: An optimized accessor is currently only usable for read attempts.
|
||||||
* Do not call this method if you need a read-write accessor.
|
* Do not call this method if you need a read-write accessor.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
public PropertyAccessor createOptimalAccessor(EvaluationContext context, @Nullable Object target, String name) {
|
public PropertyAccessor createOptimalAccessor(EvaluationContext context, @Nullable Object target, String name) {
|
||||||
// Don't be clever for arrays or a null target...
|
// Don't be clever for arrays or a null target...
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue