This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new ae138bc5e1 GROOVY-11229: limit scope of `instanceof` variable
ae138bc5e1 is described below
commit ae138bc5e1478fa20263e295021b8c656b88ea11
Author: Eric Milles <[email protected]>
AuthorDate: Sat Mar 29 08:24:54 2025 -0500
GROOVY-11229: limit scope of `instanceof` variable
---
.../groovy/classgen/VariableScopeVisitor.java | 58 ++++++++++++
src/test/groovy/InstanceofTest.groovy | 100 +++++++++++++++++++++
2 files changed, 158 insertions(+)
diff --git
a/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java
b/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java
index 34105073e8..33d1efea23 100644
--- a/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java
@@ -48,11 +48,17 @@ import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.ForStatement;
import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.SwitchStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
import org.codehaus.groovy.control.PlaceholderVisitor;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.syntax.Types;
@@ -480,6 +486,13 @@ public class VariableScopeVisitor extends
ClassCodeVisitorSupport {
// statements:
+ @Override
+ public void visitAssertStatement(final AssertStatement statement) {
+ pushState();
+ super.visitAssertStatement(statement);
+ popState();
+ }
+
@Override
public void visitBlockStatement(final BlockStatement statement) {
pushState();
@@ -498,6 +511,25 @@ public class VariableScopeVisitor extends
ClassCodeVisitorSupport {
popState();
}
+ @Override
+ public void visitDoWhileLoop(final DoWhileStatement statement) {
+ pushState();
+ visitStatement(statement);
+ statement.getLoopBlock().visit(this);
+ pushState();
+ statement.getBooleanExpression().visit(this);
+ popState();
+ popState();
+ }
+
+ @Override
+ public void visitExpressionStatement(final ExpressionStatement statement) {
+ boolean declaresVariable = statement.getExpression() instanceof
DeclarationExpression;
+ if (!declaresVariable) pushState(); // GROOVY-11229: instanceof
variable in expression
+ super.visitExpressionStatement(statement);
+ if (!declaresVariable) popState();
+ }
+
@Override
public void visitForLoop(final ForStatement statement) {
pushState();
@@ -511,15 +543,39 @@ public class VariableScopeVisitor extends
ClassCodeVisitorSupport {
@Override
public void visitIfElse(final IfStatement statement) {
+ pushState();
+ visitStatement(statement);
statement.getBooleanExpression().visit(this);
pushState();
statement.getIfBlock().visit(this);
popState();
+ popState();
pushState();
statement.getElseBlock().visit(this);
popState();
}
+ @Override
+ public void visitReturnStatement(final ReturnStatement statement) {
+ pushState();
+ super.visitReturnStatement(statement);
+ popState();
+ }
+
+ @Override
+ public void visitSwitch(final SwitchStatement statement) {
+ pushState();
+ super.visitSwitch(statement);
+ popState();
+ }
+
+ @Override
+ public void visitWhileLoop(final WhileStatement statement) {
+ pushState();
+ super.visitWhileLoop(statement);
+ popState();
+ }
+
// expressions:
@Override
@@ -627,9 +683,11 @@ public class VariableScopeVisitor extends
ClassCodeVisitorSupport {
@Override
public void visitDeclarationExpression(final DeclarationExpression
expression) {
+ pushState(); // GROOVY-11229
visitAnnotations(expression);
// visit right side first to prevent the use of a variable before its
declaration
expression.getRightExpression().visit(this);
+ popState();
if (expression.isMultipleAssignmentDeclaration()) {
TupleExpression list = expression.getTupleExpression();
diff --git a/src/test/groovy/InstanceofTest.groovy
b/src/test/groovy/InstanceofTest.groovy
index 5564441836..e095a230c6 100644
--- a/src/test/groovy/InstanceofTest.groovy
+++ b/src/test/groovy/InstanceofTest.groovy
@@ -91,5 +91,105 @@ final class InstanceofTest {
} else {
assert n.intValue() == 12345
}
+ assert (n instanceof Integer i && i.intValue() == 12345)
+ }
+
+ // GROOVY-11229
+ @Test
+ void testVariableScope() {
+ def shell = GroovyShell.withConfig {
+ ast groovy.transform.TypeChecked
+ }
+
+ def err = shouldFail shell, '''
+ Number n = 12345
+ if (n instanceof Integer i) {
+ }
+ i.toString()
+ '''
+ assert err =~ /The variable .i. is undeclared\.\s+@ line 5, column 13/
+
+ err = shouldFail shell, '''
+ Number n = 12345
+ if (n instanceof Integer i) ; else {
+ i.toString()
+ }
+ '''
+ assert err =~ /The variable .i. is undeclared\.\s+@ line 4, column 17/
+
+ err = shouldFail shell, '''
+ Number n = 12345
+ while (n instanceof Integer i) {
+ n = i.doubleValue()
+ }
+ i.toString()
+ '''
+ assert err =~ /The variable .i. is undeclared\.\s+@ line 6, column 13/
+
+ err = shouldFail shell, '''
+ Number n = 12345
+ do {
+ n = n.doubleValue()
+ } while (n instanceof Integer i)
+ i.toString()
+ '''
+ assert err =~ /The variable .i. is undeclared\.\s+@ line 6, column 13/
+
+ err = shouldFail shell, '''
+ Number n = 12345
+ do {
+ i.toString()
+ } while (n instanceof Integer i && (n = i.doubleValue()))
+ '''
+ assert err =~ /The variable .i. is undeclared\.\s+@ line 4, column 17/
+
+ err = shouldFail shell, '''
+ Number n = 12345
+ switch (n instanceof Integer i) {
+ case true:
+ i.toString()
+ case false:
+ i.toString()
+ }
+ i.toString()
+ '''
+ assert err =~ /The variable .i. is undeclared\.\s+@ line 9, column 13/
+
+ err = shouldFail shell, '''
+ Number n = 12345
+ return (n instanceof Integer i && i.intValue() == 12345)
+ i.toString()
+ '''
+ assert err =~ /The variable .i. is undeclared\.\s+@ line 4, column 13/
+
+ err = shouldFail shell, '''
+ Number n = 12345
+ assert (n instanceof Integer i && i.intValue() == 12345)
+ i.toString()
+ '''
+ assert err =~ /The variable .i. is undeclared\.\s+@ line 4, column 13/
+
+ err = shouldFail shell, '''
+ Number n = 12345
+ print(n instanceof Integer i && i.doubleValue())
+ i.toString()
+ '''
+ assert err =~ /The variable .i. is undeclared\.\s+@ line 4, column 13/
+
+ err = shouldFail shell, '''
+ Number n = 12345;
+ {
+ print(n instanceof Integer i && i.doubleValue())
+ }
+ i.toString()
+ '''
+ assert err =~ /The variable .i. is undeclared\.\s+@ line 6, column 13/
+
+ err = shouldFail shell, '''
+ Number n = 12345
+ boolean b = (n instanceof Integer i && i.intValue())
+ i.toString()
+ '''
+ assert err =~ /The variable .i. is undeclared\.\s+@ line 4, column 13/
}
}