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 <eric.mil...@thomsonreuters.com> 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 {