This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_5_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_5_0_X by this push:
new 7629f4b757 GROOVY-11754: STC: prevent cast exception under imbalanced
`instanceof` (#2397)
7629f4b757 is described below
commit 7629f4b757cb1b962cb0ded717e2febbf28426ca
Author: Eric Milles <[email protected]>
AuthorDate: Thu Apr 2 09:33:04 2026 -0500
GROOVY-11754: STC: prevent cast exception under imbalanced `instanceof`
(#2397)
---
.../transform/stc/StaticTypeCheckingVisitor.java | 58 ++++++++++++++++++++--
.../transform/stc/TypeInferenceSTCTest.groovy | 2 +-
.../asm/sc/TypeInferenceStaticCompileTest.groovy | 6 ---
3 files changed, 55 insertions(+), 11 deletions(-)
diff --git
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 07bb03ac47..9cc82464c9 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -266,6 +266,7 @@ import static org.codehaus.groovy.syntax.Types.INTDIV;
import static org.codehaus.groovy.syntax.Types.INTDIV_EQUAL;
import static org.codehaus.groovy.syntax.Types.KEYWORD_IN;
import static org.codehaus.groovy.syntax.Types.KEYWORD_INSTANCEOF;
+import static org.codehaus.groovy.syntax.Types.LOGICAL_OR;
import static org.codehaus.groovy.syntax.Types.MINUS_MINUS;
import static org.codehaus.groovy.syntax.Types.MOD;
import static org.codehaus.groovy.syntax.Types.MOD_EQUAL;
@@ -848,10 +849,13 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
return;
}
+ // GROOVY-7971, GROOVY-8965, GROOVY-10702, GROOVY-11754, et al.
+ if (op == LOGICAL_OR) typeCheckingContext.pushTemporaryTypeInfo();
+
ClassNode lType;
leftExpression.visit(this);
var setterInfo = removeSetterInfo(leftExpression);
- if (setterInfo != null) {
+ if (setterInfo != null) { assert op != LOGICAL_OR;
if (ensureValidSetter(expression, leftExpression,
rightExpression, setterInfo)) {
return;
}
@@ -863,7 +867,16 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
} else {
lType = getType(leftExpression);
}
- rightExpression.visit(this);
+ if (op != LOGICAL_OR) {
+ rightExpression.visit(this);
+ } else {
+ var lhs =
typeCheckingContext.temporaryIfBranchTypeInformation.pop();
+ typeCheckingContext.pushTemporaryTypeInfo();
+ rightExpression.visit(this);
+
+ var rhs =
typeCheckingContext.temporaryIfBranchTypeInformation.pop();
+ propagateTemporaryTypeInfo(lhs, rhs); // `instanceof` on
either side?
+ }
}
ClassNode rType = isNullConstant(rightExpression)
@@ -992,6 +1005,43 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
}
}
+ private void propagateTemporaryTypeInfo(final Map<Object, List<ClassNode>>
lhs,
+ final Map<Object, List<ClassNode>>
rhs) {
+ // TODO: deal with (x !instanceof T)
+ lhs.keySet().removeIf(k -> k instanceof Object[]);
+ rhs.keySet().removeIf(k -> k instanceof Object[]);
+
+ Function<Object, List<ClassNode>> getOrAdd = (key) ->
+
typeCheckingContext.temporaryIfBranchTypeInformation.peek().computeIfAbsent(key,
x -> new LinkedList<>());
+
+ for (var entry : lhs.entrySet()) {
+ if (rhs.containsKey(entry.getKey())) {
+ // main case: (x instanceof A || x instanceof B) produces A|B
type
+ List<ClassNode> types = getOrAdd.apply(entry.getKey());
+ types.addAll(entry.getValue());
+ types.addAll(rhs.get(entry.getKey()));
+ } else if (entry.getKey() instanceof Variable) {
+ // edge case: (x instanceof A || ...) produces A|typeof(x) type
+ List<ClassNode> types = getOrAdd.apply(entry.getKey());
+ types.addAll(entry.getValue());
+ Variable v = (Variable) entry.getKey();
+ types.add(v instanceof ASTNode ? getType((ASTNode) v) :
v.getType());
+ }
+ }
+
+ rhs.keySet().removeAll(lhs.keySet());
+
+ for (var entry : rhs.entrySet()) {
+ if (entry.getKey() instanceof Variable) {
+ // edge case: (... || x instanceof B) produces typeof(x)|B type
+ List<ClassNode> types = getOrAdd.apply(entry.getKey());
+ Variable v = (Variable) entry.getKey();
+ types.add(v instanceof ASTNode ? getType((ASTNode) v) :
v.getType());
+ types.addAll(entry.getValue());
+ }
+ }
+ }
+
private void validateResourceInARM(final BinaryExpression expression,
final ClassNode lType) {
if (expression instanceof DeclarationExpression
&& TryCatchStatement.isResource(expression)
@@ -1173,8 +1223,8 @@ out: if ((samParameterTypes.length == 1 &&
isOrImplements(samParameterTypes[0
}
addStaticTypeError(message, leftExpression);
} else {
- ClassNode[] tergetTypes =
visibleSetters.stream().map(setterType).toArray(ClassNode[]::new);
- addAssignmentError(tergetTypes.length == 1 ? tergetTypes[0] :
new UnionTypeClassNode(tergetTypes), getType(valueExpression), expression);
+ ClassNode[] targetTypes =
visibleSetters.stream().map(setterType).toArray(ClassNode[]::new);
+ addAssignmentError(targetTypes.length == 1 ? targetTypes[0] :
new UnionTypeClassNode(targetTypes), getType(valueExpression), expression);
}
return true;
}
diff --git a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
index 21f1e14012..5cc59f07a2 100644
--- a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
@@ -241,7 +241,7 @@ class TypeInferenceSTCTest extends
StaticTypeCheckingTestCase {
'''
}
- // GROOVY-11769
+ // GROOVY-11754
void testInstanceOf10() {
assertScript '''
abstract class Foo {
diff --git
a/src/test/groovy/org/codehaus/groovy/classgen/asm/sc/TypeInferenceStaticCompileTest.groovy
b/src/test/groovy/org/codehaus/groovy/classgen/asm/sc/TypeInferenceStaticCompileTest.groovy
index 1e0aca5365..b7ae38f82b 100644
---
a/src/test/groovy/org/codehaus/groovy/classgen/asm/sc/TypeInferenceStaticCompileTest.groovy
+++
b/src/test/groovy/org/codehaus/groovy/classgen/asm/sc/TypeInferenceStaticCompileTest.groovy
@@ -31,10 +31,4 @@ class TypeInferenceStaticCompileTest extends
TypeInferenceSTCTest implements Sta
void testInstanceOf9() {
super.testInstanceOf9() // GROOVY-7971
}
-
- @Override
- @NotYetImplemented
- void testInstanceOf10() {
- super.testInstanceOf10() // GROOVY-11769
- }
}