Skip to content

Improve rendering fidelity of the AST-to-source transpiler#2393

Open
leonard84 wants to merge 18 commits into
masterfrom
fix-transpiler-nested-generics
Open

Improve rendering fidelity of the AST-to-source transpiler#2393
leonard84 wants to merge 18 commits into
masterfrom
fix-transpiler-nested-generics

Conversation

@leonard84

@leonard84 leonard84 commented Jul 9, 2026

Copy link
Copy Markdown
Member

This PR started as a fix for nested generic type arguments and grew into a general fidelity pass over SourceToAstNodeAndSourceTranspiler, after exercising it with constructs the test suite never covered. Each fix is a separate commit with an AstSpec snapshot test.

Fidelity fixes

  • Nested generic type arguments: Map<String, List<Integer>> was rendered as Map<String, List>
  • Elvis operator: c ?: d was rendered as c ? c : d, duplicating the condition expression
  • Attribute access: this.@order was rendered as this.order, silently turning direct field access into property access (also ?.@ and *.@)
  • Implicit-it closures: { it * 2 } was rendered as { -> it * 2 }, which declares a zero-arg closure without it. Note the AST convention: null parameters mean { -> }, an empty array means implicit it (see ClosureWriter)
  • Safe index access: l?[0] was rendered unbalanced as l ?[ 0 under Groovy 4+, and as a plain (non-safe) l [ 0] under Groovy 3, where the parser keeps the [ token and sets the safe flag instead
  • Numeric literal type suffixes: 42L, 2.5f, 3.5d, 10G were rendered without suffix, so re-parsing would change their types
  • Left-open ranges (Groovy 4+): 1<..5 was rendered as (1..5)
  • Explicit type arguments of method calls: Collections.<String>emptyList() was rendered as Collections.emptyList()
  • Multiple-assignment declarations (Groovy 5): the parser uses a plain TupleExpression as the left side, which the previous ArgumentListExpression check missed, rendering the broken java.lang.Object( foobar , b ) = [null, null] instead of def (java.lang.Object foobar, java.lang.Object b) = [null, null]

Adopted from upstream AstNodeToScriptAdapter

  • No spurious empty default: label for switches without one (groovy@dc020e06c8)
  • No spurious empty finally {} for try statements without one (groovy@db888a0537); this alone cleaned 56 snapshots of noise from Spock's condition rewriting
  • Groovy 5 for (index, value in iterable) loops render the index variable (groovy@876fbed302)
  • The internally created GroovyClassLoader is closed after compilation (groovy@49fb716307)
  • The cosmetic rendering overhaul (groovy@4988329696 and its predecessors): variables are no longer space-padded when used as assignment targets, unary/postfix/prefix operands, cast subjects, call receivers, or list and argument elements, so output reads like real Groovy: !a, i++, [1, 2, 3][0], (c as long), ((long) c) instead of !( a ), ( i )++, [1, 2, 3] [ 0], (( c ) as long), ((long) c ). Since the fidelity fixes above already touched most snapshots, folding this in only added 6 more changed snapshot files (72 to 78 of 133)

APIs that do not exist in Groovy 2.5 (BinaryExpression.isSafe(), RangeExpression.isExclusiveLeft(), ForStatement.getIndexVariable()) are probed dynamically.

Verification

The full snapshot suite (19 spec classes) passes under all variants (2.5, 3.0, 4.0, 5.0); shared snapshots are byte-identical across them. Version-specific constructs are guarded with @Requires on the Groovy major version.

Upstream still has all the fidelity bugs listed above (verified against groovy master aa30a69065); contributing them back is prepared separately.

AstNodeToScriptVisitor.visitGenerics printed each type argument by name only,
dropping any nested type arguments the argument itself carried. A signature
such as Iterator<Tuple2<Integer, Integer>> was rendered as Iterator<Tuple2>,
and Map<String, List<Integer>> as Map<String, List>.

Recurse into the type argument's own generics (for concrete arguments, which
are the only ones that can carry them) so the rendered source faithfully
reflects the declared signature. Placeholders and wildcards are unchanged.
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0fceea2b-92d8-4d10-adb0-f24ba7f9e12e

📥 Commits

Reviewing files that changed from the base of the PR and between d4bd9e8 and a56e2bd.

📒 Files selected for processing (1)
  • spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy
🚧 Files skipped from review as they are similar to previous changes (1)
  • spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy

📝 Walkthrough

Walkthrough

The transpiler now renders additional Groovy AST forms, and the smoke snapshots and release notes were updated to match the new output. Empty finally blocks and some closure arrows are removed from generated snapshots.

Changes

AST source rendering updates

Layer / File(s) Summary
Loader, generics, and control flow structure
spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy
compileScript now manages a temporary GroovyClassLoader, generic traversal skips placeholder and wildcard arguments, indexed for loops render both variables, and empty else/default branches are suppressed.
Expression and literal rendering
spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy
Method calls, safe index access, closures, lambdas, tuples, ranges, properties, numeric constants, declarations, unary/cast forms, argument lists, maps, lists, Elvis expressions, closure lists, and arrays are rendered with updated helpers and formatting.
Smoke snapshots and release notes
spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy, spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/*, spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/*, spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/*, docs/release_notes.adoc
The smoke test, snapshots, and release notes were updated to cover the new rendering cases and formatted output.
Condition and data snapshots
spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/*, spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/*, spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/*, spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/*
Generated snapshots remove empty finally blocks, simplify closure syntax, and adjust destructuring and block formatting in condition, data, cleanup, and block flows.

Estimated code review effort: 5 (Critical) | ~90 minutes

Suggested reviewers: AndreasTu

Poem

I hopped through generics, neat and spry,
With ?:, .@, and loops nearby.
I nibbled finally crumbs away,
Then left snapshots fresh in gray.
A bunny grin in AST sky. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: improving AST-to-source rendering fidelity.
Description check ✅ Passed The description is directly related to the changeset and matches the implemented rendering and transpiler fixes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown

Greptile Summary

This PR overhauls the SourceToAstNodeAndSourceTranspiler with a comprehensive fidelity pass, fixing nine categories of incorrect AST-to-source rendering and adopting four improvements from upstream Groovy's own AstNodeToScriptAdapter. Each fix is independently covered by a new AstSpec snapshot test, and 72 of 133 existing snapshots are updated to reflect the cosmetic clean-up (removing spurious space-padding around variable expressions).

  • Correctness fixes: Elvis operator no longer duplicates its condition; attribute access (this.@order) is no longer silently rendered as property access; implicit-it closures no longer gain a spurious -> arrow; numeric literals now retain their type suffixes (L, F, D, G); safe index access (l?[0]) is rendered correctly across Groovy 3–5; left-open ranges and nested generic type arguments are handled; explicit type-argument method calls (Collections.<String>emptyList()) are preserved.
  • Lifecycle fix: A GroovyClassLoader created internally is now closed in a finally block, preventing a resource leak on repeated calls; externally supplied class loaders are left open.
  • Upstream adoptions: Spurious empty default: and finally {} blocks are suppressed; Groovy 5 for (index, value in …) loops render the index variable; all three API gaps against older Groovy versions are bridged with respondsTo/@CompileDynamic guards.

Confidence Score: 5/5

Safe to merge. All nine fidelity fixes and four upstream adoptions are logically correct and independently snapshot-tested across Groovy 2.5, 3.0, 4.0, and 5.0.

The changed code is a pure rendering utility with no side-effects on test execution or framework behavior. Every change is covered by a dedicated snapshot test, version-specific APIs are guarded with respondsTo, and the classloader lifecycle fix tightens rather than loosens resource management.

No files require special attention.

Important Files Changed

Filename Overview
spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy Core transpiler: nine fidelity fixes plus four upstream adoptions; version-specific APIs guarded with @CompileDynamic + respondsTo; classloader lifecycle corrected; logic is well-reasoned and snapshot-tested across Groovy 2.5–5.0.
spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy Eleven new snapshot tests added, one per fidelity fix and major upstream adoption; version-sensitive tests are correctly gated with @requires on the Groovy major version.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["visitDeclarationExpression"] -->|"isMultipleAssignmentDeclaration()"| B["Multiple-assignment path\ndef (Type var, ...) = rhs"]
    A -->|"single variable"| C["visitType + visitBinaryExpression"]
    
    D["visitBinaryExpression"] -->|"DeclarationExpression OR non-Variable LHS"| E["lhs.visit(this) — spacePad=true"]
    D -->|"non-Decl + VariableExpression LHS"| F["visitVariableExpression(lhs, false)"]
    D -->|"LEFT_SQUARE_BRACKET op"| G{"isSafeIndexAccess?"}
    G -->|"true"| H["print '?['"]
    G -->|"false"| I["print '['"]
    H & I --> J["visit rhs; print ']'"]

    K["visitClosureExpression"] -->|"params non-null, non-empty"| L["visitParameters; print ' ->'"]
    K -->|"params == null (explicit zero-arg)"| M["print ' ->'"]
    K -->|"params empty array (implicit it)"| N["no arrow — it remains accessible"]

    O["visitConstantExpression"] -->|"Long"| P["append 'L'"]
    O -->|"Float"| Q["append 'F'"]
    O -->|"Double"| R["append 'D'"]
    O -->|"BigInteger"| S["append 'G'"]
    O -->|"other"| T["no suffix"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["visitDeclarationExpression"] -->|"isMultipleAssignmentDeclaration()"| B["Multiple-assignment path\ndef (Type var, ...) = rhs"]
    A -->|"single variable"| C["visitType + visitBinaryExpression"]
    
    D["visitBinaryExpression"] -->|"DeclarationExpression OR non-Variable LHS"| E["lhs.visit(this) — spacePad=true"]
    D -->|"non-Decl + VariableExpression LHS"| F["visitVariableExpression(lhs, false)"]
    D -->|"LEFT_SQUARE_BRACKET op"| G{"isSafeIndexAccess?"}
    G -->|"true"| H["print '?['"]
    G -->|"false"| I["print '['"]
    H & I --> J["visit rhs; print ']'"]

    K["visitClosureExpression"] -->|"params non-null, non-empty"| L["visitParameters; print ' ->'"]
    K -->|"params == null (explicit zero-arg)"| M["print ' ->'"]
    K -->|"params empty array (implicit it)"| N["no arrow — it remains accessible"]

    O["visitConstantExpression"] -->|"Long"| P["append 'L'"]
    O -->|"Float"| Q["append 'F'"]
    O -->|"Double"| R["append 'D'"]
    O -->|"BigInteger"| S["append 'G'"]
    O -->|"other"| T["no suffix"]
Loading

Reviews (2): Last reviewed commit: "Adopt upstream's cosmetic rendering impr..." | Re-trigger Greptile

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 44 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.28%. Comparing base (87e325a) to head (a56e2bd).

Files with missing lines Patch % Lines
...ock/util/SourceToAstNodeAndSourceTranspiler.groovy 66.66% 13 Missing and 31 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #2393      +/-   ##
============================================
- Coverage     82.32%   82.28%   -0.04%     
  Complexity     4877     4877              
============================================
  Files           474      474              
  Lines         15205    15251      +46     
  Branches       1935     1959      +24     
============================================
+ Hits          12518    12550      +32     
- Misses         1993     2002       +9     
- Partials        694      699       +5     
Files with missing lines Coverage Δ
...ock/util/SourceToAstNodeAndSourceTranspiler.groovy 77.62% <66.66%> (-0.61%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

leonard84 added 13 commits July 9, 2026 18:20
…spiler

visitShortTernaryExpression delegated to visitTernaryExpression, so c ?: d
was rendered as c ? c : d. The duplicated condition misrepresents the
source: an elvis operator evaluates its condition once, while re-compiling
the rendered ternary would evaluate it twice if it has side effects.

Render the ?: form directly instead.
visitAttributeExpression delegated to visitPropertyExpression without any
distinction, so this.@order was rendered as this.order. Direct field access
and property access are different operations, the rendered source silently
changed the semantics.

Print .@ for attribute expressions, including the safe (?.@) and spread
(*.@) variants.
…nspiler

visitClosureExpression printed the arrow unconditionally, so { it * 2 } was
rendered as { -> it * 2 }. That form declares an explicit zero-arg closure,
so the rendered source no longer has the implicit it parameter.

A null parameter array denotes an explicit zero-arg closure { -> }, while an
empty array denotes the implicit it (see ClosureWriter). Only render the
arrow for the former.
visitBinaryExpression only appended the closing bracket for the plain '['
operation, so a safe index access was rendered unbalanced: l?[0] became
l ?[ 0 under Groovy 4. Under Groovy 3, where the parser keeps the '[' token
and sets the safe flag instead, the safe navigation was silently dropped,
rendering a plain index access.

Map the safe flag to the ?[ operator and close the bracket for both index
tokens. BinaryExpression.isSafe() only exists in Groovy 3+, so it is probed
dynamically.
visitConstantExpression printed only the value, so 42L, 2.5f, 3.5d, and 10G
were rendered as 42, 2.5, 3.5, and 10. Re-parsing the rendered source would
change their types to Integer or BigDecimal.

Append the literal type suffix based on the constant's value type.
visitRangeExpression only checked the right boundary, so the Groovy 4 range
1<..5 was rendered as (1..5), silently including the excluded lower bound.

Print '<' before the range operator for exclusive-left ranges.
RangeExpression.isExclusiveLeft() only exists in Groovy 4+, so it is probed
dynamically.
…ranspiler

visitMethodCallExpression ignored the call's generics types, so
Collections.<String>emptyList() was rendered as Collections.emptyList().
…transpiler

A switch statement without a default case carries EmptyStatement as its
default statement, which is truthy, so a spurious default label with an
empty body was rendered.

Adopted from apache/groovy commit dc020e06c8.
…ource transpiler

A try statement without a finally block carries EmptyStatement, but an
empty finally block was rendered unconditionally. This mainly cluttered the
rendering of Spock's own condition rewriting, which generates try/catch
without finally.

Adopted from apache/groovy commit db888a0537.
…iler

Groovy 5 supports for (index, value in iterable) loops (GROOVY-10683), but
visitForLoop only rendered the value variable, silently dropping the index.

ForStatement.getIndexVariable() only exists in Groovy 5+, so it is probed
dynamically.

Adopted from apache/groovy commit 876fbed302.
compileScript never closed the GroovyClassLoader it creates when the caller
does not pass one, leaking resources on every transpilation.

Adopted from apache/groovy commit 49fb716307.
@leonard84 leonard84 changed the title Render nested generic type arguments in the AST-to-source transpiler Improve rendering fidelity of the AST-to-source transpiler Jul 9, 2026
@leonard84 leonard84 self-assigned this Jul 9, 2026
@leonard84 leonard84 added this to the 2.5 milestone Jul 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy (2)

809-810: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting a shared "is real statement" helper.

Both visitSwitch's default-guard and visitTryCatchFinally's finally-guard repeat the same stmt && !(stmt instanceof EmptyStatement) check. A small private helper (e.g. isPresent(Statement)) would remove the duplication.

♻️ Optional refactor
+  private static boolean isPresent(Statement statement) {
+    statement != null && !(statement instanceof EmptyStatement)
+  }
+
   void visitSwitch(SwitchStatement statement) {
     ...
-      if (statement?.defaultStatement && !(statement.defaultStatement instanceof EmptyStatement)) {
+      if (isPresent(statement?.defaultStatement)) {
   ...
   void visitTryCatchFinally(TryCatchStatement statement) {
     ...
-    if (statement?.finallyStatement && !(statement.finallyStatement instanceof EmptyStatement)) {
+    if (isPresent(statement?.finallyStatement)) {

Also applies to: 1248-1257

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy`
around lines 809 - 810, The same “present statement” check is duplicated in the
switch default guard and the try/finally guard, so extract it into a small
private helper on SourceToAstNodeAndSourceTranspiler (for example, a method like
isPresent on Statement) and use that helper in both visitSwitch and
visitTryCatchFinally. Keep the helper’s logic equivalent to the current
null-and-EmptyStatement check so both call sites share one definition.

756-760: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor duplication across version-detection helpers.

isSafeIndexAccess, forLoopIndexVariable, and isExclusiveLeft all follow the same respondsTo(...) ? ... : default pattern for cross-Groovy-version API detection. Could be consolidated into one generic dynamic helper, but each guards a different receiver type/signature so the payoff is limited.

Also applies to: 914-918, 991-995

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy`
around lines 756 - 760, There is repeated Groovy-version API detection logic in
isSafeIndexAccess, forLoopIndexVariable, and isExclusiveLeft, all using the same
respondsTo(...) ternary pattern. Refactor the shared pattern into a single
dynamic helper in SourceToAstNodeAndSourceTranspiler, and update those three
methods to delegate to it while preserving each method’s specific receiver type
and fallback value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy`:
- Around line 809-810: The same “present statement” check is duplicated in the
switch default guard and the try/finally guard, so extract it into a small
private helper on SourceToAstNodeAndSourceTranspiler (for example, a method like
isPresent on Statement) and use that helper in both visitSwitch and
visitTryCatchFinally. Keep the helper’s logic equivalent to the current
null-and-EmptyStatement check so both call sites share one definition.
- Around line 756-760: There is repeated Groovy-version API detection logic in
isSafeIndexAccess, forLoopIndexVariable, and isExclusiveLeft, all using the same
respondsTo(...) ternary pattern. Refactor the shared pattern into a single
dynamic helper in SourceToAstNodeAndSourceTranspiler, and update those three
methods to delegate to it while preserving each method’s specific receiver type
and fallback value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 83894986-c74c-4070-8f85-b2fc0786546a

📥 Commits

Reviewing files that changed from the base of the PR and between 52bb1ae and 0892f8c.

📒 Files selected for processing (74)
  • docs/release_notes.adoc
  • spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy
  • spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/attribute_access_is_rendered_with_the_at_sign.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/closure_parameter_lists_are_rendered_faithfully.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/elvis_operator_is_rendered_in_short_form.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/explicit_type_arguments_of_method_calls_are_rendered.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/for_in_loops_with_an_index_variable_render_both_variables.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/full_feature_exercise-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/full_feature_exercise.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/numeric_literals_keep_their_type_suffix.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/range_boundary_exclusions_are_rendered_on_both_sides.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/safe_index_accesses_are_rendered_with_the_closing_bracket.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/switch_without_a_default_case_is_rendered_without_a_default_label.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/try_without_a_finally_block_is_rendered_without_an_empty_one.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_combined_with_data_table__data_pipe__derived_variables__cross_multiplication_and_filter.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_with_multiple_assignment-groovy3_4.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_with_multiple_assignment-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/using_a_variable_in_a_cell_multiple_times_compiles.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_explicit_condition.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_implicit_condition.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_only_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_only_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_only_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/ignores_conditions_in_overwritten_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/ignores_methods_without_annotation.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/methods_without_condition_declarations_stay_unchanged_in_specs.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_at_all_nesting_levels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_methods_inside_spec_classes.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_methods_outside_of_spec_classes.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_private_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_static_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_explicit_conditions.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/ignores_conditions_in_overwritten_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/ignores_methods_without_annotation.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/methods_without_condition_declarations_stay_unchanged_in_specs.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_at_all_nesting_levels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_methods_inside_spec_classes.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_methods_outside_of_spec_classes.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_private_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_static_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_explicit_conditions.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy
💤 Files with no reviewable changes (34)
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_at_all_nesting_levels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_static_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_methods_outside_of_spec_classes.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_static_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/ignores_conditions_in_overwritten_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/methods_without_condition_declarations_stay_unchanged_in_specs.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_explicit_conditions.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/ignores_conditions_in_overwritten_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/ignores_methods_without_annotation.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/methods_without_condition_declarations_stay_unchanged_in_specs.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_at_all_nesting_levels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_methods_outside_of_spec_classes.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/ignores_methods_without_annotation.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_methods_inside_spec_classes.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_with_multiple_assignment-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_combined_with_data_table__data_pipe__derived_variables__cross_multiplication_and_filter.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_methods_inside_spec_classes.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_explicit_conditions.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_with_multiple_assignment-groovy3_4.groovy
✅ Files skipped from review due to trivial changes (32)
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/numeric_literals_keep_their_type_suffix.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_only_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_only_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/closure_parameter_lists_are_rendered_faithfully.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/switch_without_a_default_case_is_rendered_without_a_default_label.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/explicit_type_arguments_of_method_calls_are_rendered.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/attribute_access_is_rendered_with_the_at_sign.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/try_without_a_finally_block_is_rendered_without_an_empty_one.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_only_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/range_boundary_exclusions_are_rendered_on_both_sides.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_implicit_condition.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/elvis_operator_is_rendered_in_short_form.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/using_a_variable_in_a_cell_multiple_times_compiles.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/for_in_loops_with_an_index_variable_render_both_variables.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_private_methods.groovy
🚧 Files skipped from review as they are similar to previous changes (1)
  • spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy

… transpiler

Port the end state of apache/groovy commits 88f9b2af56, 922f3475b7,
ef8f3bd5e0, 145345e22a, 5a34d13798, 4a4ce4f4e1, 29de7e506e, and 4988329696:
variables are no longer space-padded when used as assignment targets, unary
or postfix or prefix operands, cast subjects, property or method call
receivers, or list and argument elements, and index accesses are rendered
without inner padding. Examples: '!( a )' becomes '!a', '( i )++' becomes
'i++', '[1, 2, 3] [ 0]' becomes '[1, 2, 3][0]', '(( c ) as long)' becomes
'(c as long)'.

The multiple-assignment declaration rewrite also fixes a fidelity bug:
Groovy 5 represents the left side as a plain TupleExpression, which the
previous ArgumentListExpression check missed, rendering the broken
'java.lang.Object( foobar , b ) = [null, null]' instead of
'def (java.lang.Object foobar, java.lang.Object b) = [null, null]'.

Spock-specific behavior is kept: Show filtering, AnnotationConstantExpression
handling, GString rendering, and the Groovy 2.5 compatible safe index logic.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy (1)

69-74: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Wrap non-GroovyClassLoader inputs before building the CompilationUnit.
compileScript accepts ClassLoader, but line 74 casts it to GroovyClassLoader unconditionally. Passing a plain ClassLoader will fail at runtime; create a GroovyClassLoader wrapper for those cases and only close the instance created here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy`
around lines 69 - 74, compileScript currently assumes classLoader is already a
GroovyClassLoader, but it accepts any ClassLoader and casts it directly when
creating the CompilationUnit. Update
SourceToAstNodeAndSourceTranspiler.compileScript to wrap non-GroovyClassLoader
inputs in a new GroovyClassLoader before constructing the CompilationUnit, and
keep track of whether the loader was created locally so only that instance is
closed afterward.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy`:
- Around line 69-74: compileScript currently assumes classLoader is already a
GroovyClassLoader, but it accepts any ClassLoader and casts it directly when
creating the CompilationUnit. Update
SourceToAstNodeAndSourceTranspiler.compileScript to wrap non-GroovyClassLoader
inputs in a new GroovyClassLoader before constructing the CompilationUnit, and
keep track of whether the loader was created locally so only that instance is
closed afterward.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8acd8fa8-ca50-4575-b6f6-d01f83ff1269

📥 Commits

Reviewing files that changed from the base of the PR and between 0892f8c and d4bd9e8.

📒 Files selected for processing (30)
  • docs/release_notes.adoc
  • spock-core/src/main/groovy/spock/util/SourceToAstNodeAndSourceTranspiler.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/enums-groovy4.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/enums.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/full_feature_exercise-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/full_feature_exercise.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/groovy_3_language_features.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/safe_index_accesses_are_rendered_with_the_closing_bracket.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_combined_with_data_table__data_pipe__derived_variables__cross_multiplication_and_filter.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_with_multiple_assignment-groovy3_4.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_with_multiple_assignment-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/using_a_variable_in_a_cell_multiple_times_compiles.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_at_all_nesting_levels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_at_all_nesting_levels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy
✅ Files skipped from review due to trivial changes (20)
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/groovy_3_language_features.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_with_multiple_assignment-groovy3_4.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/safe_index_accesses_are_rendered_with_the_closing_bracket.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_with_multiple_assignment-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/enums-groovy4.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/using_a_variable_in_a_cell_multiple_times_compiles.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/enums.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_combined_with_data_table__data_pipe__derived_variables__cross_multiplication_and_filter.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
  • docs/release_notes.adoc
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_at_all_nesting_levels.groovy
🚧 Files skipped from review as they are similar to previous changes (6)
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_at_all_nesting_levels.groovy

leonard84 added 2 commits July 9, 2026 19:49
compileScript declares a plain ClassLoader parameter but cast it directly to
GroovyClassLoader when constructing the CompilationUnit, so passing any other
classloader failed with a ClassCastException. Wrap such classloaders in a new
GroovyClassLoader instead, and keep closing only locally created loaders.
The absent-statement check (null or EmptyStatement) was duplicated across the
if/else, switch default, and finally guards; the Groovy version probing was
duplicated across three respondsTo ternaries. Extract isPresent and
invokeIfSupported and delegate to them.
@testlens-app

testlens-app Bot commented Jul 9, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: a56e2bd
▶️ Tests: 70304 executed
⚪️ Checks: 61/61 completed


Learn more about TestLens at testlens.app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant