Support final local variables in where-blocks (#138)#2370
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR adds support for declaring Changeswhere-block local variables feature
Sequence Diagram(s) sequenceDiagram
participant SpecInfoBuilder
participant WhereBlockRewriter
participant FeatureDataProviderIterator
participant DataProcessorIterator
participant IterationFilterIterator
SpecInfoBuilder->>WhereBlockRewriter: discover/record where-block declarations
WhereBlockRewriter->>SpecInfoBuilder: generate where-variables method name / methodInfo
FeatureDataProviderIterator->>WhereBlockRewriter: invoke whereVariablesMethod (createWhereVariableValues)
WhereBlockRewriter->>FeatureDataProviderIterator: return Object[] whereVariableValues
FeatureDataProviderIterator->>DataProcessorIterator: append whereVariableValues to provider args
DataProcessorIterator->>IterationFilterIterator: pass concatenated args including whereVariableValues
FeatureDataProviderIterator->>FeatureDataProviderIterator: on close -> closeWhereVariables() (reverse order)
Estimated code review effort 🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #2370 +/- ##
============================================
+ Coverage 82.18% 82.28% +0.10%
- Complexity 4832 4870 +38
============================================
Files 473 474 +1
Lines 15051 15169 +118
Branches 1912 1932 +20
============================================
+ Hits 12369 12482 +113
- Misses 1991 1994 +3
- Partials 691 693 +2
🚀 New features to boost your workflow:
|
leonard84
left a comment
There was a problem hiding this comment.
Can we support automatic cleanup for AutoCloseable types for where vars? Is there a good location to place a hook/register a cleanup?
Done. Effectively a where-block variable now behaves like a feature-local |
…able, pipe, combine and filter (spockframework#138)
…k#138) The filter-block previously could not reference where-block variables: such references compiled but failed at runtime with a MissingPropertyException. Extend access so filter-blocks see where-block variables, consistent with data tables, data pipes and derived data variables. - WhereBlockRewriter: append the where-variable parameters to the filter method signature, like the provider and processor methods already do. - DataIteratorFactory: propagate getWhereVariableValues() through the iterator chain and pass them when invoking the filter method. - FeatureInfo: mark whereVariablesMethod @nullable for consistency with the neighbouring optional method fields. - Document filter-block visibility and add a smoke test; regenerate the combined AST snapshot.
Allow 'final (a, b) = ...' tuple declarations for where-block variables, in addition to single 'final x = ...'. Each target becomes its own where-block variable; the declaration is emitted verbatim so Groovy handles the destructuring and the existing name-driven machinery covers the rest. Non-final tuples now report the regular must-be-final error. The tuple syntax requires the Parrot parser (Groovy 3.0+), so the new runtime, AST and docs tests are gated with @requires; the AST snapshot is keyed by Groovy version since 5.0 renders the declaration differently. Also address review feedback: return a shared empty-array constant from IDataIterator.getWhereVariableValues(), and tighten the data-variable collision assertion to check the full message.
Close where-block variables that implement AutoCloseable once the feature has finished, in reverse declaration order. Cleanup is quiet: errors thrown while closing are ignored. This hooks into FeatureDataProviderIterator.close(), which already runs after all iterations of the feature complete. A where-block variable thus behaves like a feature-local '@shared @AutoCleanup(quiet = true)' field.
10c209b to
833222f
Compare
Greptile SummaryThis PR implements
Confidence Score: 3/5The feature is well-designed and broadly tested, but the multiple-assignment validation loop in the compiler leaves The single-variable path and the happy path for multiple assignment are solid. The inconsistency in The validation loop in Important Files Changed
Sequence DiagramsequenceDiagram
participant C as Compiler (WhereBlockRewriter)
participant WV as wherevars() method
participant P as Provider methods
participant PR as Processor method
participant F as Filter method
Note over C: Compile time
C->>C: collectWhereBlockVariables()
C->>WV: generateWhereVariablesMethod()
C->>P: append whereVar params to provider signatures
C->>PR: append whereVar params to processor signature
C->>F: append whereVar params to filter signature
Note over WV,F: Runtime (once per feature)
WV-->>WV: evaluate all final initializers
loop Each iteration
WV->>P: appendArgs(prevTableArgs, whereVariableValues)
P-->>P: return data provider object
P->>PR: appendArgs(rawValues, whereVariableValues)
PR-->>PR: return processed Object[]
PR->>F: appendArgs(processedData, whereVariableValues)
F-->>F: assert filter condition
end
Note over WV: Feature teardown
WV->>WV: closeWhereVariables() LIFO
Reviews (1): Last reviewed commit: "Auto-close AutoCloseable where-block var..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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.
Inline comments:
In `@docs/data_driven_testing.adoc`:
- Around line 429-431: Reflow the newly added AsciiDoc text so every sentence is
on its own line: find the section that explains declaring a `final` local
variable at the start of the `where:` block (the paragraph starting "Sometimes
you need a helper value to build your data...") and break any wrapped sentences
so each sentence occupies a single line; apply the same one-sentence-per-line
fix to the nearby paragraphs mentioned (around lines described in the comment:
the blocks containing the phrases "final local variable", "start of the `where:`
block", and the subsequent explanatory sentences at 443-450, 455-456, 464-465).
Ensure punctuation is preserved and no sentences are merged or split
incorrectly.
In
`@spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java`:
- Around line 956-975: The generated where-vars method
(createWhereVariablesMethod) must assign each initializer into the result array
immediately and ensure already-initialized AutoCloseable values are cleaned up
if a later initializer throws: modify the method built in
createWhereVariablesMethod so it constructs an Object[] up front, for each
whereBlockVariables entry evaluate the initializer into the corresponding array
slot (e.g., result[i] = <expr>) instead of collecting expressions separately,
and surround the per-element sequence with a try/catch that on exception
iterates the already-assigned slots and closes any AutoCloseable values before
rethrowing; also ensure the method signature and return (currently in MethodNode
created here) still returns the filled Object[] so
FeatureDataProviderIterator.createWhereVariableValues() receives actual values
or sees proper cleanup on failure.
- Around line 225-247: In collectMultipleAssignmentWhereBlockVariables, skip
wildcard placeholders named "_" so they are not treated as exported where-block
variables: when iterating targets in the second loop (the one that currently
does the InternalIdentifiers.isInternalName and isFinalLocal checks and then
adds to whereBlockVariableNames), detect if varExpr.getName().equals("_") and
simply continue (do not perform reserved-name or final checks and do not add to
whereBlockVariableNames) so that patterns like final (_, x) = ... only export x
and duplicate "_" entries are avoided; leave whereBlockVariables.add(stat)
unchanged so the statement is still recorded.
In
`@spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java`:
- Around line 390-405: The close() path can double-close the same AutoCloseable
when a where-variable is also present in dataProviders or dataProviderIterators;
modify close() / closeWhereVariables() to record identities already closed from
dataProviders and dataProviderIterators (e.g., an Identity-based Set populated
when calling closeQuietly on elements of dataProviders and
dataProviderIterators) and then in closeWhereVariables() skip any
whereVariableValues entries whose identity is present in that set, preserving
the reverse-declaration closing order and still ignoring close failures.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1dc2a863-143a-4435-9aaa-c39726cced32
📒 Files selected for processing (18)
docs/data_driven_testing.adocdocs/release_notes.adocspock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.javaspock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.javaspock-core/src/main/java/org/spockframework/runtime/IDataIterator.javaspock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.javaspock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.javaspock-core/src/main/java/org/spockframework/runtime/model/MethodKind.javaspock-core/src/main/java/org/spockframework/util/InternalIdentifiers.javaspock-specs/src/test/groovy/org/spockframework/docs/datadriven/DataSpec.groovyspock-specs/src/test/groovy/org/spockframework/runtime/StoreSpec.groovyspock-specs/src/test/groovy/org/spockframework/smoke/ast/DataAstSpec.groovyspock-specs/src/test/groovy/org/spockframework/smoke/parameterization/InvalidWhereBlocks.groovyspock-specs/src/test/groovy/org/spockframework/smoke/parameterization/WhereBlockVariables.groovyspock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables.groovyspock-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.groovyspock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_with_multiple_assignment-groovy3_4.groovyspock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/where_block_variables_with_multiple_assignment-groovy5.groovy
- reject the '_' wildcard in multiple-assignment where-block variables with a clear error - validate all multiple-assignment targets before exposing any name, avoiding partial-state corruption - document NO_WHERE_VARIABLE_VALUES as a shared, must-not-mutate sentinel - reflow new docs section to one sentence per line and document known auto-close limitations
✅ All tests passed ✅Test Summary
🏷️ Commit: 7124dea Learn more about TestLens at testlens.app. |
Summary
Implements #138:
finallocal variables inwhere:blocks, following the design discussed on the issue.You can declare
finallocals at the start of awhere:block as scoped helper values that are not turned into data variables / method parameters:Semantics
finalis the marker.final x = …declares a where-block variable; a barex = …remains a derived data variable. Non-finaldeclarations are a compile error pointing atfinal.final fixture = new Fixture()is a single shared instance.filter:block; not feature-method parameters and not visible in the feature body.@Shared/staticfields, not instance fields.$spock_.Implementation
The compiler collects the leading
finaldeclarations into a single generated…wherevars()method that returns their values as anObject[]. The runtime invokes it once per feature and passes the values into every data provider, the data processor, and the filter method as trailing parameters named after the user's variables (mirroring the existing previous-data-table-variable mechanism). No AST duplication; single evaluation.Touched:
WhereBlockRewriter,InternalIdentifiers,MethodKind,FeatureInfo,SpecInfoBuilder,IDataIterator,DataIteratorFactory.Tests & docs
WhereBlockVariables): data tables, data pipes, derived variables, single-evaluation across providers, cross-multiplication (combined:),filter:,@Sharedaccess, not-visible-in-body.InvalidWhereBlocks): non-final, misplaced, multiple assignment, reserved prefix, name collision, instance-field access.DataAstSpec): a focused case and a comprehensive one combining table + pipe + derived + combine + filter.data_driven_testing.adoc(with a tested example) and a release note.Closes #138