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 49e1d6487e GROOVY-3069, GROOVY-11677: call expr variable or parameter precedence 49e1d6487e is described below commit 49e1d6487e18a7165b92f670590761877594baaa Author: Eric Milles <eric.mil...@thomsonreuters.com> AuthorDate: Fri Jul 18 09:55:16 2025 -0500 GROOVY-3069, GROOVY-11677: call expr variable or parameter precedence --- .../groovy/classgen/VariableScopeVisitor.java | 31 +++++----- .../{Groovy3069Bug.groovy => Groovy3069.groovy} | 70 +++++++++++++++------- 2 files changed, 63 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java b/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java index e4001ee0c5..e615e6909b 100644 --- a/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java +++ b/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java @@ -18,7 +18,6 @@ */ package org.codehaus.groovy.classgen; -import org.codehaus.groovy.GroovyBugError; import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.AnnotationNode; import org.codehaus.groovy.ast.ClassCodeVisitorSupport; @@ -716,27 +715,25 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport { @Override public void visitMethodCallExpression(final MethodCallExpression expression) { - if (expression.isImplicitThis() && expression.getMethod() instanceof ConstantExpression) { - ConstantExpression methodNameConstant = (ConstantExpression) expression.getMethod(); - String methodName = methodNameConstant.getText(); - - if (methodName == null) { - throw new GroovyBugError("method name is null"); - } - + String methodName = expression.getMethodAsString(); + if (methodName != null && expression.isImplicitThis()) { + // GROOVY-3069, GROOVY-11677: variable or parameter call Variable variable = findVariableDeclaration(methodName); - if (variable != null && !(variable instanceof DynamicVariable)) { - checkVariableContextAccess(variable, expression); - } - + // if "name" resolves to a variable, replace "name(...)" with "name.call(...)" if (variable instanceof VariableExpression || variable instanceof Parameter) { - VariableExpression object = new VariableExpression(variable); - object.setSourcePosition(methodNameConstant); + Expression object = new VariableExpression(variable); + object.setSourcePosition(expression.getMethod()); expression.setObjectExpression(object); - ConstantExpression method = new ConstantExpression("call"); - method.setSourcePosition(methodNameConstant); // important for GROOVY-4344 expression.setImplicitThis(false); + + Expression method = new ConstantExpression("call"); + // GROOVY-4344: ensure result of "call" for assert + method.setSourcePosition(expression.getMethod()); expression.setMethod(method); + + checkVariableContextAccess(variable, expression); + expression.getArguments().visit(this); + return; } } else if (expression.getGenericsTypes() != null) { visitTypeVariables(expression.getGenericsTypes()); diff --git a/src/test/groovy/bugs/Groovy3069Bug.groovy b/src/test/groovy/bugs/Groovy3069.groovy similarity index 56% rename from src/test/groovy/bugs/Groovy3069Bug.groovy rename to src/test/groovy/bugs/Groovy3069.groovy index b72bd02f18..886f757a23 100644 --- a/src/test/groovy/bugs/Groovy3069Bug.groovy +++ b/src/test/groovy/bugs/Groovy3069.groovy @@ -18,47 +18,75 @@ */ package bugs -import groovy.test.GroovyTestCase +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource -class Groovy3069Bug extends GroovyTestCase { - final String CLOSURE_STR = '[Closure]' - final String CLASS_METHOD_STR = '[ClassMethod]' +import static groovy.test.GroovyAssert.shouldFail - def void testClosureParamPrecedenceWithTypeSpecified() { - def cl = { CLOSURE_STR } - checkPrecendenceWithTypeSpecified(cl) - } +final class Groovy3069 { - def void testClosureParamPrecedenceWithTypeNotSpecified() { - def cl = { CLOSURE_STR } - checkPrecendenceWithTypeNotSpecified(cl) - } + private static final String CLOSURE_STR = '[Closure]' + private static final String CLASS_METHOD_STR = '[ClassMethod]' - def void testClosureLocalVarPrecedenceExplicitClosureType() { - Closure method = { CLOSURE_STR } + String method() { + CLASS_METHOD_STR + } + String checkPrecendenceWithTypeSpecified(Closure method) { assert method() == CLOSURE_STR assert this.method() == CLASS_METHOD_STR } - def void testClosureLocalVarPrecedenceImplicitClosureType() { - def method = { CLOSURE_STR } - + String checkPrecendenceWithTypeNotSpecified(method) { assert method() == CLOSURE_STR assert this.method() == CLASS_METHOD_STR } - String method() { - return CLASS_METHOD_STR + // + + @Test + void testClosureParamPrecedenceWithTypeSpecified() { + def cl = { CLOSURE_STR } + checkPrecendenceWithTypeSpecified(cl) } - String checkPrecendenceWithTypeSpecified(Closure method) { + @Test + void testClosureParamPrecedenceWithTypeNotSpecified() { + def cl = { CLOSURE_STR } + checkPrecendenceWithTypeNotSpecified(cl) + } + + @Test + void testClosureLocalVarPrecedenceExplicitClosureType() { + Closure method = { CLOSURE_STR } assert method() == CLOSURE_STR assert this.method() == CLASS_METHOD_STR } - String checkPrecendenceWithTypeNotSpecified(method) { + @Test + void testClosureLocalVarPrecedenceImplicitClosureType() { + def method = { CLOSURE_STR } assert method() == CLOSURE_STR assert this.method() == CLASS_METHOD_STR } + + // GROOVY-11677 + @ParameterizedTest + @ValueSource(strings=['CompileDynamic','TypeChecked','CompileStatic']) + void testNonCallableVariable(String mode) { + shouldFail """ + class C { + @groovy.transform.$mode + void test(List<String> names = ['fizz','buzz']) { + print(names [0]) + print(names()[0]) // error: not callable + } + List<String> names() { + ['foo','bar'] + } + } + new C().test() + """ + } }