Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,30 @@
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.PropertyNode;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.syntax.SyntaxException;
import org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport;
import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor;
import org.codehaus.groovy.transform.stc.StaticTypesMarker;

import java.util.Collections;
import java.util.Map;

import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;

/**
* Handles the implementation of the {@link groovy.transform.TypeChecked} transformation.
*/
Expand Down Expand Up @@ -73,6 +81,45 @@ public void visit(ASTNode[] nodes, SourceUnit source) {
}
if (visitor != null) {
visitor.performSecondPass();
resolveEnumConstantsInSwitchCases(node, source);
}
}

/**
* GROOVY-8444, GROOVY-11614, GROOVY-12190: replaces "CONST" expressions resolved by the
* type checker in enum switch case labels with "EnumType.CONST" expressions;
* without static compilation they would otherwise be dynamic property lookups
* on the enclosing class, failing at runtime.
*/
private static void resolveEnumConstantsInSwitchCases(final AnnotatedNode node, final SourceUnit source) {
ClassCodeExpressionTransformer transformer = new ClassCodeExpressionTransformer() {
@Override
protected SourceUnit getSourceUnit() {
return source;
}

@Override
public Expression transform(final Expression expression) {
if (expression instanceof ClosureExpression) { // switch expressions are desugared to closures
expression.visit(this);
return expression;
}
if (expression instanceof VariableExpression) {
ClassNode enumType = expression.getNodeMetaData(StaticTypesMarker.SWITCH_CONDITION_EXPRESSION_TYPE);
if (enumType != null) {
PropertyExpression pe = propX(classX(enumType), expression.getText());
setSourcePosition(pe, expression);
return pe;
}
return expression;
}
return super.transform(expression);
}
};
if (node instanceof ClassNode classNode) {
transformer.visitClass(classNode);
} else if (node instanceof MethodNode methodNode) {
transformer.visitMethod(methodNode);
}
}

Expand Down
87 changes: 87 additions & 0 deletions src/test/groovy/bugs/Groovy8444.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -433,4 +433,91 @@ final class Groovy8444 {
assert 2.2 == meth(SomeEnum.B, OtherEnum.D)
'''
}

// GROOVY-12190
@Test
void testAccessingEnumConstantInSwitchCaseWithTypeChecked() {
assertScript '''\
enum SomeEnum {
A, B
}
@groovy.transform.TypeChecked
def meth(SomeEnum e) {
switch (e) {
case A: return 1
case B: return 2
}
}
assert 1 == meth(SomeEnum.A)
assert 2 == meth(SomeEnum.B)
'''
}

// GROOVY-12190
@Test
void testAccessingEnumConstantInSwitchExprCaseWithTypeChecked() {
assertScript '''\
enum SomeEnum {
A, B
}
@groovy.transform.TypeChecked
def meth(SomeEnum e) {
switch (e) {
case A -> 1
case B -> 2
}
}
assert 1 == meth(SomeEnum.A)
assert 2 == meth(SomeEnum.B)
'''
}

// GROOVY-12190
@Test
void testAccessingEnumConstantInNestedSwitchExprCaseWithTypeChecked() {
assertScript '''\
enum SomeEnum {
A, B
}
@groovy.transform.TypeChecked
def meth(SomeEnum e) {
switch (e) {
case A ->
switch(e) {
case A -> 1.1
case B -> 1.2
}
case B ->
switch(e) {
case A -> 2.1
case B -> 2.2
}
}
}
assert 1.1 == meth(SomeEnum.A)
assert 2.2 == meth(SomeEnum.B)
'''
}

// GROOVY-12190
@Test
void testAccessingEnumConstantInSwitchCaseInClosureWithTypeChecked() {
assertScript '''\
enum SomeEnum {
A, B
}
@groovy.transform.TypeChecked
def meth(SomeEnum e) {
def c = {
switch (e) {
case A: return 1
case B: return 2
}
}
c()
}
assert 1 == meth(SomeEnum.A)
assert 2 == meth(SomeEnum.B)
'''
}
}
Loading