Skip to content

Commit e8d7dc2

Browse files
committed
Revert null-safe behavior to error at runtime instead of compiletime (#65099)
This reverts a change where null-safe was enhanced to cause a compile-time error instead of a run- time error when the target value was a primitive type. The reason for the reversion is consistency across def/non-def types and versions. I've added a follow up issue to fix this behavior in general (#65098).
1 parent 7caf8cf commit e8d7dc2

File tree

2 files changed

+16
-4
lines changed

2 files changed

+16
-4
lines changed

modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/DefaultSemanticAnalysisPhase.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -2485,11 +2485,14 @@ public void visitDot(EDot userDotNode, SemanticScope semanticScope) {
24852485
"Field [" + index + "] does not exist for type [" + prefixValueType.getValueCanonicalTypeName() + "]."));
24862486
}
24872487
} else if (prefixValueType != null && prefixValueType.getValueType() == def.class) {
2488-
TargetType targetType = semanticScope.getDecoration(userDotNode, TargetType.class);
2488+
TargetType targetType = userDotNode.isNullSafe() ? null : semanticScope.getDecoration(userDotNode, TargetType.class);
24892489
// TODO: remove ZonedDateTime exception when JodaCompatibleDateTime is removed
24902490
valueType = targetType == null || targetType.getTargetType() == ZonedDateTime.class ||
24912491
semanticScope.getCondition(userDotNode, Explicit.class) ? def.class : targetType.getTargetType();
2492-
semanticScope.setCondition(userDotNode, DefOptimized.class);
2492+
2493+
if (write) {
2494+
semanticScope.setCondition(userDotNode, DefOptimized.class);
2495+
}
24932496
} else {
24942497
Class<?> prefixType;
24952498
String prefixCanonicalTypeName;
@@ -2708,7 +2711,10 @@ public void visitBrace(EBrace userBraceNode, SemanticScope semanticScope) {
27082711
// TODO: remove ZonedDateTime exception when JodaCompatibleDateTime is removed
27092712
valueType = targetType == null || targetType.getTargetType() == ZonedDateTime.class ||
27102713
semanticScope.getCondition(userBraceNode, Explicit.class) ? def.class : targetType.getTargetType();
2711-
semanticScope.setCondition(userBraceNode, DefOptimized.class);
2714+
2715+
if (write) {
2716+
semanticScope.setCondition(userBraceNode, DefOptimized.class);
2717+
}
27122718
} else if (Map.class.isAssignableFrom(prefixValueType)) {
27132719
String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(prefixValueType);
27142720

@@ -2854,7 +2860,7 @@ public void visitCall(ECall userCallNode, SemanticScope semanticScope) {
28542860
}
28552861
}
28562862

2857-
TargetType targetType = semanticScope.getDecoration(userCallNode, TargetType.class);
2863+
TargetType targetType = userCallNode.isNullSafe() ? null : semanticScope.getDecoration(userCallNode, TargetType.class);
28582864
// TODO: remove ZonedDateTime exception when JodaCompatibleDateTime is removed
28592865
valueType = targetType == null || targetType.getTargetType() == ZonedDateTime.class ||
28602866
semanticScope.getCondition(userCallNode, Explicit.class) ? def.class : targetType.getTargetType();

modules/lang-painless/src/test/java/org/elasticsearch/painless/WhenThingsGoWrongTests.java

+6
Original file line numberDiff line numberDiff line change
@@ -840,4 +840,10 @@ public void testInvalidFullyQualifiedStaticReferenceType() {
840840
iae = expectScriptThrows(IllegalArgumentException.class, () -> exec("while (java.util.List) {java.util.List x = 1;}"));
841841
assertEquals(iae.getMessage(), "value required: instead found unexpected type [java.util.List]");
842842
}
843+
844+
public void testInvalidNullSafeBehavior() {
845+
expectScriptThrows(ClassCastException.class, () ->
846+
exec("def test = ['hostname': 'somehostname']; test?.hostname && params.host.hostname != ''"));
847+
expectScriptThrows(NullPointerException.class, () -> exec("params?.host?.hostname && params.host?.hostname != ''"));
848+
}
843849
}

0 commit comments

Comments
 (0)