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 81098ecb52 GROOVY-11614: SC: apply enum-case transformation after STC
visitation
81098ecb52 is described below
commit 81098ecb525b55455fb6490a9fe0ceed5218233c
Author: Eric Milles <[email protected]>
AuthorDate: Tue Jul 8 13:50:06 2025 -0500
GROOVY-11614: SC: apply enum-case transformation after STC visitation
---
.../codehaus/groovy/control/CompilationUnit.java | 41 ----------------------
.../VariableExpressionTransformer.java | 16 +++++++++
.../transform/stc/EnumTypeCheckingExtension.java | 34 +++++++-----------
src/test/groovy/bugs/Groovy8444.groovy | 26 +++++++-------
4 files changed, 42 insertions(+), 75 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
index 5665a6d8d4..43704358d1 100644
--- a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
+++ b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
@@ -23,17 +23,12 @@ import groovy.lang.GroovyRuntimeException;
import groovy.transform.CompilationUnitAware;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.AnnotationNode;
-import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.CompileUnit;
import org.codehaus.groovy.ast.GroovyClassVisitor;
import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.ModuleNode;
-import org.codehaus.groovy.ast.expr.ClosureExpression;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
-import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.classgen.AsmClassGenerator;
import org.codehaus.groovy.classgen.ClassCompletionVerifier;
import org.codehaus.groovy.classgen.EnumCompletionVisitor;
@@ -76,10 +71,7 @@ import java.util.Optional;
import java.util.Queue;
import java.util.Set;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
import static
org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.DYNAMIC_OUTER_NODE_CALLBACK;
-import static
org.codehaus.groovy.transform.stc.StaticTypesMarker.SWITCH_CONDITION_EXPRESSION_TYPE;
/**
* The CompilationUnit collects all compilation data as it is generated by the
compiler system.
@@ -354,39 +346,6 @@ public class CompilationUnit extends ProcessingUnit {
classNode.removeNodeMetaData(DYNAMIC_OUTER_NODE_CALLBACK);
}
}, Phases.INSTRUCTION_SELECTION);
-
- addPhaseOperation((final SourceUnit source, final GeneratorContext
context, final ClassNode classNode) -> {
- // TODO: Can this be moved into
org.codehaus.groovy.transform.sc.transformers.VariableExpressionTransformer?
- GroovyClassVisitor visitor = new ClassCodeExpressionTransformer() {
- @Override
- protected SourceUnit getSourceUnit() {
- return source;
- }
-
- @Override
- public Expression transform(final Expression expression) {
- if (expression instanceof VariableExpression) {
- // check for "switch(enumType) { case CONST: ... }"
- ClassNode enumType =
expression.getNodeMetaData(SWITCH_CONDITION_EXPRESSION_TYPE);
- if (enumType != null) {
- // replace "CONST" variable expression with
"EnumType.CONST" property expression
- Expression propertyExpression =
propX(classX(enumType), expression.getText());
- setSourcePosition(propertyExpression, expression);
- return propertyExpression;
- }
- } else if (expression instanceof MethodCallExpression) {
- // we wrap SwitchExpressions into a method call on a
ClosureExpression
- MethodCallExpression mce = (MethodCallExpression)
expression;
- if (mce.getObjectExpression() instanceof
ClosureExpression) {
- expression.visit(this);
- return expression;
- }
- }
- return expression;
- }
- };
- visitor.visitClass(classNode);
- }, Phases.INSTRUCTION_SELECTION);
}
private void applyCompilationCustomizers() {
diff --git
a/src/main/java/org/codehaus/groovy/transform/sc/transformers/VariableExpressionTransformer.java
b/src/main/java/org/codehaus/groovy/transform/sc/transformers/VariableExpressionTransformer.java
index ecd633b073..7763a0703d 100644
---
a/src/main/java/org/codehaus/groovy/transform/sc/transformers/VariableExpressionTransformer.java
+++
b/src/main/java/org/codehaus/groovy/transform/sc/transformers/VariableExpressionTransformer.java
@@ -37,6 +37,9 @@ class VariableExpressionTransformer {
Expression transformVariableExpression(final VariableExpression ve) {
Expression xe = tryTransformImplicitReceiver(ve);
+ if (xe == null) {
+ xe = tryTransformEnumConstantAccess(ve);
+ }
if (xe == null) {
xe = tryTransformPrivateFieldAccess(ve);
}
@@ -77,6 +80,19 @@ class VariableExpressionTransformer {
return pe;
}
+ private static Expression tryTransformEnumConstantAccess(final
VariableExpression ve) {
+ ClassNode enumType =
ve.getNodeMetaData(StaticTypesMarker.SWITCH_CONDITION_EXPRESSION_TYPE);
+ if (enumType == null) {
+ return null;
+ }
+
+ // GROOVY-8444, GROOVY-11614: replace "CONST" expression with an
"EnumType.CONST" expression
+ PropertyExpression pe = propX(classX(enumType), ve.getText());
+ pe.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, enumType);
+ pe.getProperty().setSourcePosition(ve);
+ return pe;
+ }
+
private static Expression tryTransformPrivateFieldAccess(final
VariableExpression ve) {
FieldNode field =
ve.getNodeMetaData(StaticTypesMarker.PV_FIELDS_ACCESS);
if (field == null) {
diff --git
a/src/main/java/org/codehaus/groovy/transform/stc/EnumTypeCheckingExtension.java
b/src/main/java/org/codehaus/groovy/transform/stc/EnumTypeCheckingExtension.java
index e798f27ea2..e439e1722c 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/EnumTypeCheckingExtension.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/EnumTypeCheckingExtension.java
@@ -21,45 +21,35 @@ package org.codehaus.groovy.transform.stc;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.expr.VariableExpression;
-import org.codehaus.groovy.ast.stmt.SwitchStatement;
-
-import java.lang.reflect.Modifier;
import static
org.codehaus.groovy.transform.stc.StaticTypesMarker.SWITCH_CONDITION_EXPRESSION_TYPE;
/**
- * A type checking extension that will take care of handling errors which are
specific to enums. In particular, it will
- * handle the enum constants within switch-case statement.
+ * A type checking extension that will take care of handling errors which are
+ * specific to enums. In particular, it will handle the enum constants within
+ * a switch's case statements.
*
* @since 3.0.0
*/
public class EnumTypeCheckingExtension extends TypeCheckingExtension {
- public EnumTypeCheckingExtension(StaticTypeCheckingVisitor
staticTypeCheckingVisitor) {
+
+ public EnumTypeCheckingExtension(final StaticTypeCheckingVisitor
staticTypeCheckingVisitor) {
super(staticTypeCheckingVisitor);
}
@Override
- public boolean handleUnresolvedVariableExpression(VariableExpression vexp)
{
- SwitchStatement switchStatement =
this.typeCheckingVisitor.typeCheckingContext.getEnclosingSwitchStatement();
-
- if (null == switchStatement) return false;
-
- ClassNode type =
switchStatement.getExpression().getNodeMetaData(StaticTypesMarker.TYPE);
-
- if (null == type) return false;
-
- if (type.isEnum()) {
- FieldNode fieldNode = type.redirect().getField(vexp.getName());
- if (null != fieldNode) {
- int modifiers = fieldNode.getModifiers();
- if (Modifier.isPublic(modifiers) &&
Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
- && type.equals(fieldNode.getType())) {
+ public boolean handleUnresolvedVariableExpression(final VariableExpression
vexp) {
+ var switchStatement =
this.typeCheckingVisitor.typeCheckingContext.getEnclosingSwitchStatement();
+ if (switchStatement != null) {
+ ClassNode type =
switchStatement.getExpression().getNodeMetaData(StaticTypesMarker.TYPE);
+ if (type != null && type.isEnum()) {
+ FieldNode fieldNode = type.redirect().getField(vexp.getName());
+ if (fieldNode != null && fieldNode.isEnum()) {
vexp.putNodeMetaData(SWITCH_CONDITION_EXPRESSION_TYPE,
type);
return true;
}
}
}
-
return false;
}
}
diff --git a/src/test/groovy/bugs/Groovy8444.groovy
b/src/test/groovy/bugs/Groovy8444.groovy
index dedd9823ff..dc3b59effe 100644
--- a/src/test/groovy/bugs/Groovy8444.groovy
+++ b/src/test/groovy/bugs/Groovy8444.groovy
@@ -18,7 +18,7 @@
*/
package bugs
-import org.junit.Test
+import org.junit.jupiter.api.Test
import static groovy.test.GroovyAssert.assertScript
import static groovy.test.GroovyAssert.shouldFail
@@ -224,14 +224,16 @@ final class Groovy8444 {
'''
}
- @Test
// GROOVY-11614
+ @Test
void testAccessingEnumConstantInSwitchExprCase() {
- assertScript '''\
+ def shell = GroovyShell.withConfig {
+ ast(groovy.transform.CompileStatic)
+ }
+ assertScript shell, '''\
enum SomeEnum {
A, B
}
- @groovy.transform.CompileStatic
def meth(SomeEnum e) {
switch (e) {
case A -> 1
@@ -243,8 +245,8 @@ final class Groovy8444 {
'''
}
- @Test
// GROOVY-11614
+ @Test
void testAccessingEnumConstantInSwitchExprCase2() {
assertScript '''\
enum SomeEnum {
@@ -266,8 +268,8 @@ final class Groovy8444 {
'''
}
- @Test
// GROOVY-11614
+ @Test
void testAccessingEnumConstantInSwitchExprCase3() {
assertScript '''\
enum SomeEnum {
@@ -285,8 +287,8 @@ final class Groovy8444 {
'''
}
- @Test
// GROOVY-11614
+ @Test
void testAccessingNonEnumConstantInSwitchExprCase() {
def err = shouldFail '''\
enum SomeEnum {
@@ -307,8 +309,8 @@ final class Groovy8444 {
assert err.message.contains('@ line 9, column 26.')
}
- @Test
// GROOVY-11614
+ @Test
void testAccessingNonEnumConstantInSwitchExprCase2() {
def err = shouldFail '''\
enum SomeEnum {
@@ -329,8 +331,8 @@ final class Groovy8444 {
assert err.message.contains('@ line 9, column 26.')
}
- @Test
// GROOVY-11614
+ @Test
void testAccessingNonEnumConstantInSwitchExprCase3() {
def err = shouldFail '''\
enum SomeEnum {
@@ -351,8 +353,8 @@ final class Groovy8444 {
assert err.message.contains('@ line 9, column 26.')
}
- @Test
// GROOVY-11614
+ @Test
void testAccessingNonEnumConstantInSwitchExprCase4() {
def err = shouldFail '''\
enum SomeEnum {
@@ -373,8 +375,8 @@ final class Groovy8444 {
assert err.message.contains('@ line 9, column 26.')
}
- @Test
// GROOVY-11614
+ @Test
void testAccessingEnumConstantInNestedSwitchExprCase() {
assertScript '''\
enum SomeEnum {
@@ -400,8 +402,8 @@ final class Groovy8444 {
'''
}
- @Test
// GROOVY-11614
+ @Test
void testAccessingEnumConstantInNestedSwitchExprCase2() {
assertScript '''\
enum SomeEnum {