You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2026/07/10 20:51:51 UTC
(groovy) branch master updated (aa30a69065 -> c5dc1a10f4)
This is an automated email from the ASF dual-hosted git repository.
daniellansun pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
from aa30a69065 Close per-test GroovyClassLoader in contracts BaseTestClass
new 63865c6ed9 GROOVY-12145: Re-enable AstNodeToScriptAdapterTest disabled by JUnit 5 migration
new c5dc1a10f4 GROOVY-12144: Fix AstNodeToScriptAdapter decompiler rendering fidelity
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../console/ui/AstNodeToScriptAdapter.groovy | 28 ++-
.../console/ui/AstNodeToScriptAdapterTest.groovy | 195 ++++++++++++++++++++-
2 files changed, 213 insertions(+), 10 deletions(-)
(groovy) 01/02: GROOVY-12145: Re-enable AstNodeToScriptAdapterTest disabled by JUnit 5 migration
Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
daniellansun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 63865c6ed9d98c226a786cb3ecebc69b6be81db7
Author: Leonard Brünings <le...@gradle.com>
AuthorDate: Fri Jul 10 12:53:15 2026 +0200
GROOVY-12145: Re-enable AstNodeToScriptAdapterTest disabled by JUnit 5 migration
The "Convert to JUnit5" migration (commit a1897e0811) removed
`extends GroovyTestCase` from AstNodeToScriptAdapterTest but did not add
`@Test` to its `void testXxx()` methods. Because the build uses
useJUnitPlatform(), JUnit Platform discovers tests only by `@Test`, so all
63 tests silently stopped executing -- a filtered run reports "No tests
found for given includes". The `import org.junit.jupiter.api.Test` was also
mis-inserted inside a triple-quoted script string.
Add `@Test` to every test method, move the import to the top-level import
block, and refresh two assertions whose expected output had drifted while
the tests were dark: @Immutable's hashCode now initializes the accumulator
as Integer rather than Object, and @Lazy now emits @Internal before the
backing field.
GROOVY-12145 surveys the broader migration fallout (~195 dead methods
across ~32 files); this change fixes the groovy-console instance, which is
a prerequisite for the GROOVY-12144 decompiler-fidelity tests to run.
Assisted-by: Claude Code (Opus 4.8)
---
.../console/ui/AstNodeToScriptAdapterTest.groovy | 69 +++++++++++++++++++++-
1 file changed, 66 insertions(+), 3 deletions(-)
diff --git a/subprojects/groovy-console/src/test/groovy/groovy/console/ui/AstNodeToScriptAdapterTest.groovy b/subprojects/groovy-console/src/test/groovy/groovy/console/ui/AstNodeToScriptAdapterTest.groovy
index 7807535bfa..92fa9f4f5b 100644
--- a/subprojects/groovy-console/src/test/groovy/groovy/console/ui/AstNodeToScriptAdapterTest.groovy
+++ b/subprojects/groovy-console/src/test/groovy/groovy/console/ui/AstNodeToScriptAdapterTest.groovy
@@ -24,6 +24,7 @@ import org.codehaus.groovy.ast.stmt.BlockStatement
import org.codehaus.groovy.ast.stmt.DoWhileStatement
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.control.CompilerConfiguration
+import org.junit.jupiter.api.Test
import static org.codehaus.groovy.ast.tools.GeneralUtils.args
import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX
@@ -43,6 +44,7 @@ final class AstNodeToScriptAdapterTest {
new AstNodeToScriptAdapter().compileToScript(script, phase.phaseNumber)
}
+ @Test
void testScript() {
String result = compileToScript('true')
@@ -52,6 +54,7 @@ final class AstNodeToScriptAdapterTest {
assert result =~ /public java.lang.Object run\(\) \{\s*true\s*\}/
}
+ @Test
void testStringEscaping() {
String script = ''' if (_out.toString().endsWith('\\n\\n')) { } '''
String result = compileToScript(script)
@@ -59,6 +62,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains("_out.toString().endsWith('\\n\\n')")
}
+ @Test
void testPackage() {
String script = ''' package foo.bar
true '''
@@ -67,6 +71,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('package foo.bar\n')
}
+ @Test
void testSubScriptOperator() {
String script = '''
def file = new File((String)args[0])
@@ -78,6 +83,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('file = new java.io.File(((java.lang.String) args[0]))')
}
+ @Test
void testMethods() {
String script = '''
def method1() {}
@@ -105,6 +111,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('public java.lang.Integer[] method10(java.lang.String[] parm1, java.lang.Object[] parm2)')
}
+ @Test
void testGenerics() {
String script = '''
public class MyList<E> extends AbstractList<E> implements List<E> {
@@ -117,6 +124,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('public class MyList<E> extends java.util.AbstractList<E> implements java.util.List<E> {')
}
+ @Test
void testGenericBoundsOnClass() {
String script = '''import java.util.concurrent.Callable
abstract class MyClass<T extends String & Callable<String>, U extends Integer> extends AbstractList<String> implements Callable<? super Number> { }
@@ -128,6 +136,7 @@ final class AstNodeToScriptAdapterTest {
'implements java.util.concurrent.Callable<? super java.lang.Number> {')
}
+ @Test
void testGenericsInVariables() {
// you think you know Java generics? Just contemplate a load of this mess:
String script = '''
@@ -153,6 +162,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('b.addBranch(new Tree<java.lang.Double>(java.lang.Math.PI))')
}
+ @Test
void testGenericsInMethods() {
// you think you know Java generics? Just contemplate a load of this mess:
String script = '''
@@ -180,6 +190,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('void addBranch(Tree<? extends java.lang.Object<V>> branch)')
}
+ @Test
void testBitwiseNegation() {
String script = '''
def foo = { it }
@@ -191,6 +202,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('~(foo.call(~( bar ) ))')
}
+ @Test
void testRangeExpression() {
String script = '''
(1..4).each { println it }
@@ -202,6 +214,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains("('1'..'4').collect({ java.lang.Object it ->")
}
+ @Test
void testUnaryOperators() {
String script = '''
def x = 1
@@ -233,6 +246,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('+x')
}
+ @Test
void testArrayHandling() {
String script = '''
String[] x = [] as String[]
@@ -247,6 +261,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('arr = new java.lang.String[0]')
}
+ @Test
void testArrayHandling_Multidimension() {
String script = '''
String[][] x = [] as String[][]
@@ -263,6 +278,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('arr = new java.lang.String[this.xSize(), 3]')
}
+ @Test
void testForLoop() {
String script = '''
for (int x = 0; x < 10;( x )++) {
@@ -276,6 +292,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('continue')
}
+ @Test
void testForLoopEmptyParameter() {
String script = '''
int y = 0
@@ -290,6 +307,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('for (;;) {')
}
+ @Test
void testPreAndPostFix() {
String script = '''
def x = 1, y = 2
@@ -300,6 +318,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('x++ + --y - --(x++)')
}
+ @Test
void testMultipleAssignments() {
String script = '''
def (a, b, c, d) = [1, 2, 3, 4]
@@ -316,6 +335,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('java.lang.Object z = [1, 2]')
}
+ @Test
void testListsAndMaps() {
String script = '''
def (bar, x, bif, qux) = [1, 2, 3, 4]
@@ -330,6 +350,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains("['foo': 'bar', 'baz': bif + qux ]")
}
+ @Test
void testForEachLoop() {
String script = '''
for (int x : (1..10)) {
@@ -341,6 +362,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('for (int x : (1..10)) {')
}
+ @Test
void testSwitchStatements() {
String script = '''import java.awt.Color
switch (someMethod()) {
@@ -373,6 +395,7 @@ final class AstNodeToScriptAdapterTest {
assert !result.contains('default:')
}
+ @Test
void testLogAnnotation() {
String script = '''
@groovy.util.logging.Log
@@ -389,6 +412,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('return log.isLoggable(java.util.logging.Level.FINE) ? log.fine(this.someMethod()) : null')
}
+ @Test
void testFieldDeclarationWithValue() {
String script = '''
class Event {
@@ -404,6 +428,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains("bar = '123'")
}
+ @Test
void testClassAndProperties() {
String script = '''
class Event {
@@ -418,6 +443,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('metaClass = /*BytecodeExpression*/')
}
+ @Test
void testClassAnnotations() {
String script = '''import org.codehaus.groovy.transform.*
@SuppressWarnings
@@ -443,6 +469,7 @@ final class AstNodeToScriptAdapterTest {
result.contains('includeNames = false')
}
+ @Test
void testMethodAnnotations() {
String script = '''import org.codehaus.groovy.transform.*
class Event {
@@ -469,6 +496,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('@MyAnnotation(') && result.contains('p2 = false') && result.contains('p1 = true')
}
+ @Test
void testPropertyAnnotations() {
String script = '''import org.codehaus.groovy.transform.*
class Event {
@@ -495,6 +523,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('@MyAnnotation(') && result.contains('p2 = false') && result.contains('p1 = true')
}
+ @Test
void testPackageAnnotations() {
String script = '''
@SuppressWarnings
@@ -522,6 +551,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('package foo.bar.baz')
}
+ @Test
void testImportStatements() {
String script = '''
@SuppressWarnings
@@ -567,6 +597,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('import java.util.concurrent.*')
}
+ @Test
void testParameterAnnotations() {
String script = '''
def method(@SuppressWarnings @SuppressWarnings('foo') parameter) {
@@ -577,6 +608,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains("public java.lang.Object method(@java.lang.SuppressWarnings @java.lang.SuppressWarnings(value = 'foo') java.lang.Object parameter) {")
}
+ @Test
void testParenthesisInArgumentList() {
String script = '''
public java.lang.String toString() {
@@ -593,6 +625,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains("_result.append(')')")
}
+ @Test
void testStaticMethods() {
String script = "Math.min(5, Math.min('str', 2))"
String result = compileToScript(script)
@@ -600,6 +633,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains("java.lang.Math.min(5, java.lang.Math.min('str', 2))")
}
+ @Test
void testAtImmutableClass() {
String script = '@groovy.transform.Immutable class Event { }'
String result = compileToScript(script, CompilePhase.CANONICALIZATION)
@@ -613,6 +647,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('return $to$string')
}
+ @Test
void testToStringClassAndStaticMethodCallExpression() {
String script = '@groovy.transform.ToString class Event { Date when }'
String result = compileToScript(script, CompilePhase.CANONICALIZATION)
@@ -621,10 +656,10 @@ final class AstNodeToScriptAdapterTest {
assert result.contains("_result.append(org.codehaus.groovy.runtime.FormatHelper.toString(this.getWhen())")
}
+ @Test
void testAtImmutableClassWithProperties() {
String script = '''
import groovy.transform.Immutable
-import org.junit.jupiter.api.Test
@Immutable class Event {
String title
Date when
@@ -639,7 +674,7 @@ import org.junit.jupiter.api.Test
// assert hashCode
assert result.contains('public int hashCode()')
- assert result.contains('java.lang.Object _result = org.codehaus.groovy.util.HashCodeHelper.initHash()')
+ assert result.contains('java.lang.Integer _result = org.codehaus.groovy.util.HashCodeHelper.initHash()')
assert result.contains("_result = org.codehaus.groovy.util.HashCodeHelper.updateHash(_result, this.getTitle())")
assert result.contains("_result = org.codehaus.groovy.util.HashCodeHelper.updateHash(_result, this.getWhen())")
assert result.contains("_result = org.codehaus.groovy.util.HashCodeHelper.updateHash(_result, this.getColor())")
@@ -652,6 +687,7 @@ import org.junit.jupiter.api.Test
}
}
+ @Test
void testAnonymousInnerClass() {
String script = '''
new Object() {
@@ -665,6 +701,7 @@ import org.junit.jupiter.api.Test
assert result =~ /public java\.lang\.String toString\(\)/
}
+ @Test
void testLazyAnnotation() {
String script = '''
class Event {
@@ -673,12 +710,13 @@ import org.junit.jupiter.api.Test
'''
String result = compileToScript(script, CompilePhase.CANONICALIZATION)
- assert result =~ /Lazy\s*private java\.util\.ArrayList .*speakers /
+ assert result =~ /Lazy\s*(?:@groovy\.transform\.Internal\s*)?private java\.util\.ArrayList .*speakers /
assert result.contains('public java.util.ArrayList getSpeakers() {')
assert result.contains('if ($speakers != null') || result.contains('if (null != $speakers')
assert result.contains('$speakers = new java.util.ArrayList()')
}
+ @Test
void testDelegateClass() {
String script = '''
class Event {
@@ -693,6 +731,7 @@ import org.junit.jupiter.api.Test
assert result.contains('public java.util.Date getWhen() {')
}
+ @Test
void testAnnotationWithValueClass() {
String script = '''
class Event {
@@ -707,6 +746,7 @@ import org.junit.jupiter.api.Test
assert result.contains('private java.util.Date when ')
}
+ @Test
void testTryCatch() {
String script = '''\
try {
@@ -738,6 +778,7 @@ import org.junit.jupiter.api.Test
assert !result.contains('finally {')
}
+ @Test
void testSuperAndThisCalls() {
String script = '''
class MyClass {
@@ -756,6 +797,7 @@ import org.junit.jupiter.api.Test
assert result.contains('public MyClass(java.lang.Object foo) {')
}
+ @Test
void testTryCatchFinally() {
String script = '''\
try {
@@ -769,6 +811,7 @@ import org.junit.jupiter.api.Test
assert result =~ /try\s*\{\s*false\s*\}\s*finally\s*\{\s*true\s*\}/
}
+ @Test
void testSynchronizedBlock() {
String script = '''\
synchronized(this) {
@@ -787,6 +830,7 @@ import org.junit.jupiter.api.Test
assert result.contains('}')
}
+ @Test
void testTernaryOperaters() {
String script = """true || false ? 'y' : 'n'
foo ?: 'y'"""
@@ -795,6 +839,7 @@ import org.junit.jupiter.api.Test
assert result.contains("foo ? foo : 'y'")
}
+ @Test
void testWhileLoop() {
String script = '''while ("foo") println 5
while ("foo") {println 5; println 5; break; }'''
@@ -803,6 +848,7 @@ import org.junit.jupiter.api.Test
assert result.contains("while ('foo') {")
}
+ @Test
void testDoWhileLoop() {
def doWhile = new DoWhileStatement(
new BooleanExpression(constX(true)),
@@ -819,6 +865,7 @@ import org.junit.jupiter.api.Test
assert result.contains('} while (true)')
}
+ @Test
void testAssertStatement() {
String script = '''
assert 1 == 1; assert true == foo() : 'message'
@@ -829,6 +876,7 @@ import org.junit.jupiter.api.Test
assert result.contains("assert true == this.foo() : 'message'")
}
+ @Test
void testMethodPointer() {
String script = '''
class Event {
@@ -855,6 +903,7 @@ import org.junit.jupiter.api.Test
assert result.contains('assert 1 == m3.call(6) : null')
}
+ @Test
void testAssertRegexExpression() {
String script = '''
assert "abc.def" =~ /[a-z]b[a-z]\\.def/
@@ -866,6 +915,7 @@ import org.junit.jupiter.api.Test
assert result.contains("assert 'cheesecheese' =~ 'cheese' : null")
}
+ @Test
void testArrayExpression() {
String script = '''
def x = [4, 5, 6] as String[]
@@ -877,6 +927,7 @@ import org.junit.jupiter.api.Test
assert result.contains('[1, 2, 3] << new java.lang.Integer[x.length]')
}
+ @Test
void testSpreadDot() {
String script = '''
def x = [ ['a':11, 'b':12], ['a':21, 'b':22] ]
@@ -890,6 +941,7 @@ import org.junit.jupiter.api.Test
assert result.contains('assert x*.a == [11, 21] : null')
}
+ @Test
void testSpreadNotationNullHandling() {
String script = '''
def x = [ ['a':11, 'b':12], ['a':21, 'b':22], null ]
@@ -905,6 +957,7 @@ import org.junit.jupiter.api.Test
assert result.contains('}) : null')
}
+ @Test
void testSpreadNotationAdvanced() {
String script = '''
class MyClass{ def getA(){ 'abc' } }
@@ -917,6 +970,7 @@ import org.junit.jupiter.api.Test
assert result.contains("assert x*.a == [21, null, 'abc'] : null")
}
+ @Test
void testSpreadNotationForMethodsOnLists() {
String script = '''
class MyClass{ def getA(){ 'abc' } }
@@ -933,6 +987,7 @@ import org.junit.jupiter.api.Test
assert result.contains("assert [c1, c2]*.getA() == ['abc', 'abc'] : null")
}
+ @Test
void testVisitSafeMethodCall() {
String script = 'someMethod()?.somethingElse()*.sum()'
String result = compileToScript(script)
@@ -940,6 +995,7 @@ import org.junit.jupiter.api.Test
assert result.contains('someMethod()?.somethingElse()*.sum()')
}
+ @Test
void testSpreadNotationInMapDefinition() {
String script = '''
assert ['z':900, *:['a':100, 'b':200], 'a':300] == ['a':300, 'b':200, 'z':900]
@@ -951,6 +1007,7 @@ import org.junit.jupiter.api.Test
assert result.contains('assert [*: [3: 3, *: [5: 5]], 7: 7] == [3: 3, 5: 5, 7: 7] : null')
}
+ @Test
void testSpreadNotationInClosure() {
String script = '''
def f(){ [ 1:'u', 2:'v', 3:'w' ] }
@@ -969,6 +1026,7 @@ import org.junit.jupiter.api.Test
}
// GROOVY-4636
+ @Test
void testObjectInitializers() {
String script = '''
class A {
@@ -985,6 +1043,7 @@ import org.junit.jupiter.api.Test
assert result =~ /(?s)public A\(\) \{.*?v = 2\s*v = 1\s*\}/
}
+ @Test
void testStatementLabels() {
String script = '''
label1:
@@ -1039,6 +1098,7 @@ import org.junit.jupiter.api.Test
assert result.contains('label6:\nsynchronized')
}
+ @Test
void testStatementLabelsForDoWhileLoop() {
def doWhile = new DoWhileStatement(
new BooleanExpression(constX(true)),
@@ -1056,6 +1116,7 @@ import org.junit.jupiter.api.Test
assert result.contains('} while (true)')
}
+ @Test
void testNestedObjectInitializers() {
String script = '''
class A {
@@ -1077,6 +1138,7 @@ import org.junit.jupiter.api.Test
assert result =~ /(?s)oi:.*?\{.*?v \+= 2.*?\}/
}
+ @Test
void testVisitIfElse() {
String script = '''
String a = 'foo'
@@ -1087,6 +1149,7 @@ import org.junit.jupiter.api.Test
assert result.contains('if (a instanceof java.lang.String) {')
}
+ @Test
void testVisitCastExpression() {
String script = '''
String a = 'foo'
(groovy) 02/02: GROOVY-12144: Fix AstNodeToScriptAdapter decompiler rendering fidelity
Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
daniellansun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit c5dc1a10f4c0c05a14fe43c2f24d1777c15a479d
Author: Leonard Brünings <le...@gradle.com>
AuthorDate: Fri Jul 10 12:53:49 2026 +0200
GROOVY-12144: Fix AstNodeToScriptAdapter decompiler rendering fidelity
AstNodeToScriptVisitor rendered several constructs incorrectly, producing
source that did not round-trip -- it re-parsed to a different type or with
changed semantics. Fix eight independent defects, each localized to a
single visitor method:
1. nested generic type arguments dropped (Map<String, List<Integer>>
rendered Map<String, List>) -- visitGenerics recurses into concrete args
2. range bound exclusions dropped (1..<5 / 1<..5 / 1<..<5 all rendered
1..5) -- visitRangeExpression honors isExclusiveLeft/isExclusiveRight
3. Elvis rendered as a duplicated ternary (c ?: d rendered c ? c : d,
evaluating c twice) -- visitShortTernaryExpression emits ?:
4. attribute access lost the at-sign (obj.@f rendered obj.f) --
visitPropertyExpression emits .@ for an AttributeExpression
5. explicit zero-arg closures lost the arrow ({ -> ... } rendered
{ ... }, gaining an implicit it) -- visitClosureExpression treats a
null parameter list distinctly from Parameter.EMPTY_ARRAY
6. safe index access lost safe navigation (list?[0] rendered list[0]) --
visitBinaryExpression honors isSafe()
7. numeric literal type suffixes dropped (42L/2.5f/3.5d/10G rendered as
42/2.5/3.5/10) -- visitConstantExpression re-appends L/F/D/G
8. explicit method-call type arguments dropped
(Collections.<String>emptyList()) -- visitMethodCallExpression renders them
Add one regression test per defect. Fix 4 makes AttributeExpression render
faithfully everywhere: @Log builds Level.FINE as an AttributeExpression, so
it now renders Level.@FINE (testLogAnnotation updated). Two existing tests
are updated to the corrected output (testTernaryOperaters; testGenericsInMethods,
whose comments had asked whether the nested type args should be dropped).
These fidelity issues were found and fixed downstream in Spock's transpiler
(derived from this class); this upstreams them.
Assisted-by: Claude Code (Opus 4.8)
---
.../console/ui/AstNodeToScriptAdapter.groovy | 28 ++++-
.../console/ui/AstNodeToScriptAdapterTest.groovy | 126 ++++++++++++++++++++-
2 files changed, 147 insertions(+), 7 deletions(-)
diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/AstNodeToScriptAdapter.groovy b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/AstNodeToScriptAdapter.groovy
index 9f13c48a29..c86ccfb312 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/AstNodeToScriptAdapter.groovy
+++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/AstNodeToScriptAdapter.groovy
@@ -446,6 +446,9 @@ class AstNodeToScriptVisitor implements CompilationUnit.IPrimaryClassNodeOperati
}
first = false
print it.name
+ if (!it.placeholder && !it.wildcard) {
+ visitGenerics it.type?.genericsTypes
+ }
if (it.upperBounds) {
print ' extends '
boolean innerFirst = true
@@ -754,6 +757,7 @@ class AstNodeToScriptVisitor implements CompilationUnit.IPrimaryClassNodeOperati
print '?'
}
print '.'
+ visitGenerics expression.genericsTypes
Expression method = expression.method
if (method instanceof ConstantExpression) {
visitConstantExpression(method, true)
@@ -799,7 +803,7 @@ class AstNodeToScriptVisitor implements CompilationUnit.IPrimaryClassNodeOperati
boolean isAssign = expression?.operation?.type == Types.ASSIGN
boolean isAccess = expression?.operation?.type == Types.LEFT_SQUARE_BRACKET
if (!isAssign || expression?.rightExpression !instanceof EmptyExpression) {
- print isAccess ? '[' : " ${expression?.operation?.text} "
+ print isAccess ? (expression.safe ? '?[' : '[') : " ${expression?.operation?.text} "
expression?.rightExpression?.visit this
if (isAccess) {
print ']'
@@ -840,6 +844,8 @@ class AstNodeToScriptVisitor implements CompilationUnit.IPrimaryClassNodeOperati
if (expression?.parameters) {
visitParameters expression?.parameters
print ' ->'
+ } else if (expression?.parameters == null) {
+ print ' ->'
}
printLineBreak()
indented {
@@ -876,7 +882,9 @@ class AstNodeToScriptVisitor implements CompilationUnit.IPrimaryClassNodeOperati
void visitRangeExpression(RangeExpression expression) {
print '('
expression?.from?.visit this
+ if (expression.exclusiveLeft) print '<'
print '..'
+ if (expression.exclusiveRight) print '<'
expression?.to?.visit this
print ')'
}
@@ -890,7 +898,7 @@ class AstNodeToScriptVisitor implements CompilationUnit.IPrimaryClassNodeOperati
} else if (expression?.isSafe()) {
print '?'
}
- print '.'
+ print expression instanceof AttributeExpression ? '.@' : '.'
if (expression?.property instanceof ConstantExpression) {
visitConstantExpression((ConstantExpression) expression?.property, true)
} else {
@@ -919,6 +927,18 @@ class AstNodeToScriptVisitor implements CompilationUnit.IPrimaryClassNodeOperati
print "'$escaped'"
} else {
print expression.value
+ // re-append the literal's type suffix so re-parsing yields the same type
+ // (Integer and BigDecimal are the literal defaults and need no suffix)
+ def value = expression.value
+ if (value instanceof Long) {
+ print 'L'
+ } else if (value instanceof Float) {
+ print 'F'
+ } else if (value instanceof Double) {
+ print 'D'
+ } else if (value instanceof BigInteger) {
+ print 'G'
+ }
}
}
@@ -1145,7 +1165,9 @@ class AstNodeToScriptVisitor implements CompilationUnit.IPrimaryClassNodeOperati
/** Renders an Elvis expression. */
@Override
void visitShortTernaryExpression(ElvisOperatorExpression expression) {
- visitTernaryExpression expression
+ expression?.booleanExpression?.visit this
+ print ' ?: '
+ expression?.falseExpression?.visit this
}
/** Renders a boolean expression. */
diff --git a/subprojects/groovy-console/src/test/groovy/groovy/console/ui/AstNodeToScriptAdapterTest.groovy b/subprojects/groovy-console/src/test/groovy/groovy/console/ui/AstNodeToScriptAdapterTest.groovy
index 92fa9f4f5b..0de3b0a0bd 100644
--- a/subprojects/groovy-console/src/test/groovy/groovy/console/ui/AstNodeToScriptAdapterTest.groovy
+++ b/subprojects/groovy-console/src/test/groovy/groovy/console/ui/AstNodeToScriptAdapterTest.groovy
@@ -181,8 +181,8 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('public class Tree<V> extends java.lang.Object implements groovy.lang.GroovyObject')
assert result.contains('private java.lang.Object<V> value') // todo: is Object<V> correct? How do you know?
- assert result.contains('private java.util.List<Tree> branches') // should the <? extends V> be dropped?
- assert result.contains('branches = new java.util.ArrayList<Tree>()') // should the <? extends V> be dropped?
+ assert result.contains('private java.util.List<Tree<? extends java.lang.Object<V>>> branches')
+ assert result.contains('branches = new java.util.ArrayList<Tree<? extends java.lang.Object<V>>>()')
assert result.contains('public Tree(java.lang.Object<V> value)') // again, is this correct?
assert result.contains(' public java.lang.Object<V> getValue()') // is this correct?
assert result.contains('public void setValue(java.lang.Object<V> value)')
@@ -409,7 +409,7 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('private static final transient java.util.logging.Logger log')
assert result.contains("log = java.util.logging.Logger.getLogger('Event')")
- assert result.contains('return log.isLoggable(java.util.logging.Level.FINE) ? log.fine(this.someMethod()) : null')
+ assert result.contains('return log.isLoggable(java.util.logging.Level.@FINE) ? log.fine(this.someMethod()) : null')
}
@Test
@@ -836,7 +836,7 @@ final class AstNodeToScriptAdapterTest {
foo ?: 'y'"""
String result = compileToScript(script)
assert result.contains("true || false ? 'y' : 'n'")
- assert result.contains("foo ? foo : 'y'")
+ assert result.contains("foo ?: 'y'")
}
@Test
@@ -1159,4 +1159,122 @@ final class AstNodeToScriptAdapterTest {
assert result.contains('(a as java.lang.String)')
}
+
+ // GROOVY-12144
+ @Test
+ void testNestedGenerics() {
+ String script = '''
+ Map<String, List<Integer>> nested() { null }
+ '''
+ String result = compileToScript(script)
+
+ assert result.contains('java.util.Map<String, List<Integer>> nested()')
+ }
+
+ // GROOVY-12144
+ @Test
+ void testRangeExpressionExclusiveBounds() {
+ String script = '''
+ def a = 1..5
+ def b = 1..<5
+ def c = 1<..5
+ def d = 1<..<5
+ '''
+ String result = compileToScript(script)
+
+ assert result.contains('(1..5)')
+ assert result.contains('(1..<5)')
+ assert result.contains('(1<..5)')
+ assert result.contains('(1<..<5)')
+ }
+
+ // GROOVY-12144
+ @Test
+ void testElvisOperator() {
+ String script = '''
+ def a = c ?: d
+ '''
+ String result = compileToScript(script)
+
+ assert result.contains('c ?: d')
+ assert !result.contains('c ? c : d')
+ }
+
+ // GROOVY-12144
+ @Test
+ void testAttributeExpression() {
+ String script = '''
+ def a = other.@order
+ def b = foos*.@order
+ def c = other?.@order
+ '''
+ String result = compileToScript(script)
+
+ assert result.contains('other.@order')
+ assert result.contains('foos*.@order')
+ assert result.contains('other?.@order')
+ }
+
+ // GROOVY-12144
+ @Test
+ void testClosureParameterArrow() {
+ String script = '''
+ def implicitIt = { it * 2 }
+ def noArgs = { -> 'x' }
+ '''
+ String result = compileToScript(script)
+
+ // explicit zero-argument closure keeps its arrow (otherwise it would gain an implicit 'it')
+ assert result.contains('{ ->')
+ // an implicit-'it' closure has no arrow
+ assert result =~ /\{\s*it \* 2/
+ }
+
+ // GROOVY-12144
+ @Test
+ void testSafeIndexAccess() {
+ String script = '''
+ def a = list?[0]
+ def b = map?['key']
+ list?[1] = 42
+ '''
+ String result = compileToScript(script)
+
+ assert result.contains('list?[0]')
+ assert result.contains("map?['key']")
+ assert result.contains('list?[1] = 42')
+ }
+
+ // GROOVY-12144
+ @Test
+ void testNumericLiteralSuffixes() {
+ String script = '''
+ def a = 42L
+ def b = 2.5f
+ def c = 3.5d
+ def d = 10G
+ def e = 42
+ def f = 2.5
+ '''
+ String result = compileToScript(script)
+
+ assert result.contains('= 42L')
+ assert result.contains('= 2.5F')
+ assert result.contains('= 3.5D')
+ assert result.contains('= 10G')
+ // Integer and BigDecimal are the literal defaults and carry no suffix
+ assert result.contains('= 42\n')
+ assert result.contains('= 2.5\n')
+ }
+
+ // GROOVY-12144
+ @Test
+ void testExplicitMethodTypeArguments() {
+ String script = '''
+ def a = Collections.<String>emptyList()
+ '''
+ String result = compileToScript(script)
+
+ assert result.contains('java.util.Collections.<String>emptyList()')
+ }
}