You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2026/02/27 16:59:59 UTC
(groovy) branch master updated: GROOVY-11865: collect array or list elements using expression conversion
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 6f29a43693 GROOVY-11865: collect array or list elements using expression conversion
6f29a43693 is described below
commit 6f29a436931b736b878408bd358bb26ee2c3bd7f
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Feb 27 10:33:13 2026 -0600
GROOVY-11865: collect array or list elements using expression conversion
---
.../customizers/ASTTransformationCustomizer.groovy | 92 ++++++++++++----------
.../ASTTransformationCustomizerTest.groovy | 38 ++++++++-
2 files changed, 85 insertions(+), 45 deletions(-)
diff --git a/src/main/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizer.groovy b/src/main/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizer.groovy
index 2d43061410..7160440fae 100644
--- a/src/main/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizer.groovy
+++ b/src/main/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizer.groovy
@@ -20,7 +20,6 @@ package org.codehaus.groovy.control.customizers
import groovy.transform.AutoFinal
import groovy.transform.CompilationUnitAware
-import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.AnnotationNode
@@ -49,7 +48,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
* Creating a customizer with the {@link ASTTransformationCustomizer#ASTTransformationCustomizer(Class)
* class constructor} will trigger an AST transformation for
* each class node of a source unit. However, you cannot pass parameters to the annotation so the default values
- * will be used. Writing :
+ * will be used. Writing:
* <pre>
* def configuration = new CompilerConfiguration()
* configuration.addCompilationCustomizers(new ASTTransformationCustomizer(Log))
@@ -60,8 +59,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
* }""")
* </pre>
*
- * is equivalent to :
- *
+ * is equivalent to:
* <pre>
* def shell = new GroovyShell()
* shell.evaluate("""
@@ -195,10 +193,6 @@ class ASTTransformationCustomizer extends CompilationCustomizer implements Compi
this.annotationParameters = annotationParams
}
- void setCompilationUnit(CompilationUnit unit) {
- compilationUnit = unit
- }
-
@SuppressWarnings('ClassForName')
private static Class<ASTTransformation> findASTTransformationClass(Class<? extends Annotation> anAnnotationClass, ClassLoader transformationClassLoader) {
GroovyASTTransformationClass annotation = anAnnotationClass.getAnnotation(GroovyASTTransformationClass)
@@ -257,53 +251,69 @@ class ASTTransformationCustomizer extends CompilationCustomizer implements Compi
* }.expression[0]
* customizer.annotationParameters = [value: expression]</pre>
*
- * @param params the annotation parameters
- *
* @since 1.8.1
*/
- @CompileDynamic
- void setAnnotationParameters(Map<String, Object> params) {
- if (annotationNode == null || params == null || params.isEmpty()) return
- params.each { name, value ->
- if (!annotationNode.classNode.getMethod(name)) {
+ void setAnnotationParameters(Map<String, Object> parameters) {
+ if (!annotationNode) return
+ for (entry in parameters) {
+ String name = entry.getKey()
+ if (annotationNode.classNode.getMethod(name) == null) {
throw new IllegalArgumentException("${annotationNode.classNode.name} does not accept any [$name] parameter")
}
- if (value instanceof Closure) {
- throw new IllegalArgumentException('Direct usage of closure is not supported by the AST compilation customizer. Please use ClosureExpression instead.')
- }
+ annotationNode.addMember(name, toExpression(entry.getValue()))
+ }
+ }
- Expression valueExpression
-
- if (value instanceof Expression) {
- valueExpression = value
- // avoid NPEs due to missing source code
- value.lineNumber = 0; value.lastLineNumber = 0
- } else if (value instanceof Class) {
- valueExpression = classX(value)
- } else if (value instanceof Enum) {
- valueExpression = propX(classX(ClassHelper.make(value.getClass())), value.toString())
- } else if (value instanceof List || value.getClass().isArray()) {
- valueExpression = listX(value.collect { it instanceof Class ? classX(it) : constX(it) })
- } else {
- valueExpression = constX(value)
- }
+ private static Expression toExpression(Object value) {
+ if (value instanceof Expression) {
+ // avoid exceptions due to missing source code
+ value.lineNumber = 0; value.lastLineNumber = 0
+ return value
+ }
+
+ if (value instanceof Closure) {
+ throw new IllegalArgumentException('Direct usage of closure is not supported by the AST compilation customizer. Please use ClosureExpression instead.')
+ }
+
+ if (value instanceof Class) {
+ return classX(value)
+ }
+
+ if (value instanceof Enum) {
+ return propX(classX(value.getClass()), value.toString())
+ }
- annotationNode.addMember(name, valueExpression)
+ if (value instanceof List) {
+ return listX(value.collect(this.&toExpression)) // GROOVY-11865
}
+
+ if (value.getClass().isArray()) {
+ def array = value as Object[]
+ return listX(array.collect(this.&toExpression)) // GROOVY-11865
+ }
+
+ return constX(value, true)
+ }
+
+ //--------------------------------------------------------------------------
+
+ @Override
+ void setCompilationUnit(CompilationUnit compilationUnit) {
+ this.compilationUnit = compilationUnit
}
@Override
- void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
- if (transformation instanceof CompilationUnitAware) {
- ((CompilationUnitAware) transformation).compilationUnit = compilationUnit
+ void call(SourceUnit sourceUnit, GeneratorContext context, ClassNode classNode) {
+ if (transformation instanceof CompilationUnitAware unitAware) {
+ unitAware.compilationUnit = compilationUnit
}
if (annotationNode != null) {
- // this is a local ast transformation which is applied on every class node
annotationNode.sourcePosition = classNode
- transformation.visit([annotationNode, classNode] as ASTNode[], source)
- } else {
+ // this is a local ast transformation which is applied on every class node
+ transformation.visit(new ASTNode[]{annotationNode, classNode}, sourceUnit)
+ } else if (!applied) {
// this is a global AST transformation
- if (!applied) transformation.visit(null, source)
+ transformation.visit(null, sourceUnit)
}
applied = true
}
diff --git a/src/test/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizerTest.groovy b/src/test/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizerTest.groovy
index df5dcb58ce..6f28ef472b 100644
--- a/src/test/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizerTest.groovy
+++ b/src/test/groovy/org/codehaus/groovy/control/customizers/ASTTransformationCustomizerTest.groovy
@@ -18,10 +18,9 @@
*/
package org.codehaus.groovy.control.customizers
-import groovy.transform.TimedInterrupt
+import groovy.transform.*
import groovy.util.logging.Log
import org.codehaus.groovy.ast.ASTNode
-import org.codehaus.groovy.ast.ClassHelper
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.SourceUnit
@@ -33,6 +32,7 @@ import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import static groovy.test.GroovyAssert.shouldFail
+import static groovy.transform.PackageScopeTarget.METHODS
import static org.codehaus.groovy.ast.tools.GeneralUtils.classX
import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
@@ -110,7 +110,7 @@ final class ASTTransformationCustomizerTest {
@Test
void testLocalTransformationPropertyExpressionParameter() {
def shell = GroovyShell.withConfig {
- ast(TimedInterrupt, value:300, unit:propX(classX(ClassHelper.make(TimeUnit)),'MILLISECONDS'))
+ ast(TimedInterrupt, value:300, unit:propX(classX(TimeUnit),'MILLISECONDS'))
imports { normal 'java.util.concurrent.TimeoutException' }
}
assert shell.evaluate('''
@@ -127,7 +127,8 @@ final class ASTTransformationCustomizerTest {
''')
}
- @Test // GROOVY-10654
+ // GROOVY-10654
+ @Test
void testLocalTransformationEnumerationConstantParameter() {
def shell = GroovyShell.withConfig {
ast(TimedInterrupt, value:300, unit:TimeUnit.MILLISECONDS)
@@ -147,6 +148,35 @@ final class ASTTransformationCustomizerTest {
''')
}
+ // GROOVY-11865
+ @Test
+ void testLocalTransformationListOfEnumerationConstantParameter() {
+ def shell = GroovyShell.withConfig {
+ ast(PackageScope, value: [PackageScopeTarget.CONSTRUCTORS, METHODS])
+ imports { staticStar 'java.lang.reflect.Modifier' }
+ }
+ shell.evaluate '''
+ class C {
+ C() {
+ }
+ def m() {
+ }
+ }
+
+ int mods = C.getModifiers()
+
+ assert isPublic(mods)
+
+ mods = C.getDeclaredConstructors().first().getModifiers()
+
+ assert !isPublic(mods) && !isPrivate(mods) && !isProtected(mods)
+
+ mods = C.getDeclaredMethod('m').getModifiers()
+
+ assert !isPublic(mods) && !isPrivate(mods) && !isProtected(mods)
+ '''
+ }
+
//--------------------------------------------------------------------------
@Test