Skip to content

Register CriteriaTypeCheckingExtension and cover createCriteria().<terminal>{} closures#15720

Open
codeconsole wants to merge 1 commit into
apache:8.0.xfrom
codeconsole:criteria-typecheck-chained-8
Open

Register CriteriaTypeCheckingExtension and cover createCriteria().<terminal>{} closures#15720
codeconsole wants to merge 1 commit into
apache:8.0.xfrom
codeconsole:criteria-typecheck-chained-8

Conversation

@codeconsole
Copy link
Copy Markdown
Contributor

What

CriteriaTypeCheckingExtension exists in grails-core but is not registered in @GrailsCompileStatic's extension list, and isCriteriaCall only recognizes the Domain.withCriteria { … } / Domain.createCriteria() forms (closure as the direct argument). The chained Domain.createCriteria().<terminal> { … } form — where the closure is the argument to a terminal called on the builder — is never matched, so a criteria scope is never opened for it.

Why it matters

Most chained terminals (list, listDistinct, get, scroll) still type-check today because BuildableCriteria declares them with a Closure parameter and @DelegatesTo resolves the closure body. But count(Closure) is not declared on BuildableCriteria, so:

@GrailsCompileStatic
class Foo {
    def c() { Person.createCriteria().count { eq 'name', 'Anakin' } }
}

fails static compilation on stock 8.0.x:

[Static type checking] - Cannot find matching method
org.grails.datastore.mapping.query.api.BuildableCriteria#count(groovy.lang.Closure)

forcing a @CompileDynamic escape hatch.

Change

  1. Extend isCriteriaCall to also open a criteria scope when the call is a terminal (list, listDistinct, get, count, scroll) chained directly on Domain.createCriteria(). Registration alone is not enough — the createCriteria() scope is exited (via afterMethodCall) before the terminal call is visited, so the terminal needs to be recognized in its own right.
  2. Add org.grails.compiler.CriteriaTypeCheckingExtension to the @CompileStatic(extensions=[…]) list on @GrailsCompileStatic.

Test

Adds 'Test compiling a class which invokes a chained createCriteria() terminal on a domain class' to GrailsCompileStaticCompilationErrorsSpec, covering all six chained terminals. It fails on stock 8.0.x (on the count terminal) and passes with this change. The existing GRAILS-11255 criteria spec continues to pass.

Scope / limits

The extension makes the direct children of the criteria closure dynamic. Deeply nested builder closures (e.g. projections { property '…' }) and post-processing that indexes the result (withCriteria { … }[0]) still require @CompileDynamic — out of scope here.

…rminal>{} closures

CriteriaTypeCheckingExtension existed but was never added to the
@GrailsCompileStatic extension list, and isCriteriaCall only recognized the
Domain.withCriteria {} / Domain.createCriteria() forms where the closure is
the direct argument. The chained Domain.createCriteria().<terminal> { } form
(closure as the argument to a terminal called on the builder) was not
matched, so its scope was never opened.

Most chained terminals (list/listDistinct/get/scroll) still type-checked
because BuildableCriteria declares them with a Closure parameter and
@DelegatesTo resolves the closure body. count(Closure) is not declared on
BuildableCriteria, so Domain.createCriteria().count {} failed static
compilation with 'Cannot find matching method BuildableCriteria#count(Closure)'.

Extend isCriteriaCall to also open a criteria scope for a terminal
(list/listDistinct/get/count/scroll) chained directly on createCriteria(),
and register the extension in @GrailsCompileStatic so criteria-builder
closures type-check under static compilation. Adds a test covering all six
chained terminals; count is the case that fails without this change.

git log --oneline -1
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 18.2620%. Comparing base (41f9fb9) to head (c1410a5).

Additional details and impacted files

Impacted file tree graph

@@                 Coverage Diff                 @@
##                8.0.x     #15720         +/-   ##
===================================================
- Coverage     48.3483%   18.2620%   -30.0864%     
+ Complexity      15104        258      -14846     
===================================================
  Files            1870         54       -1816     
  Lines           85459       3176      -82283     
  Branches        14901        527      -14374     
===================================================
- Hits            41318        580      -40738     
+ Misses          37805       2486      -35319     
+ Partials         6336        110       -6226     

see 1816 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codeconsole codeconsole marked this pull request as draft June 5, 2026 18:53
@testlens-app
Copy link
Copy Markdown

testlens-app Bot commented Jun 5, 2026

✅ All tests passed ✅

🏷️ Commit: c1410a5
▶️ Tests: 10442 executed
⚪️ Checks: 43/43 completed


Learn more about TestLens at testlens.app.

@codeconsole codeconsole marked this pull request as ready for review June 7, 2026 06:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant