codeconsole opened a new pull request, #15733:
URL: https://github.com/apache/grails-core/pull/15733

   ## Summary
   
   `message(code: ..., args: [...])` is by far the most common tag invoked as a 
method from controllers — it is what scaffolding generates for every flash 
message:
   
   ```groovy
   flash.message = message(code: 'default.created.message', args: [...])
   redirect book
   ```
   
   Today that call only resolves via `TagLibraryInvoker.methodMissing`, so 
under `@CompileStatic` / `@GrailsCompileStatic` it fails to compile and forces 
`@CompileDynamic` onto exactly the controller actions that are otherwise fully 
statically compilable (the same pain #15715 addressed for attribute access). 
This PR makes the canonical idiom compile statically, verbatim.
   
   ## Design
   
   A new trait `grails.artefact.gsp.ControllerTagLibraryInvoker extends 
TagLibraryInvoker` declares a real `Object message(Map attrs)`, and 
`ControllerTagLibraryTraitInjector` now injects it (controllers keep the full 
inherited `TagLibraryInvoker` API).
   
   - **Dispatch parity.** The method resolves the tag through the same lookup 
order as `methodMissing` (declared `taglibNamespace` first, then the default 
namespace) and invokes it through the same `TagOutput.captureTagOutput(..., 
OutputContextLookupHelper.lookupOutputContext())` machinery that GSP-compiled 
code and the `TagLibraryMetaUtils`-registered meta-methods use — so attribute 
handling (`code`, `args`, `default`, `error`, `message`, `locale`, `encodeAs`), 
encoding, and return semantics are identical to the dynamic path. When no tag 
library provides the tag, it throws `MissingMethodException`, matching 
`methodMissing`'s failure mode.
   - **Why a subtrait instead of `TagLibraryInvoker` itself:** tag libraries 
also implement `TagLibraryInvoker` (via `grails.artefact.TagLibrary`), and a 
real `message` method inherited by every tag library is picked up by 
`TagMethodInvoker` as if the library declared a `message` tag — recursing 
infinitely for libraries that don't (caught by `grails-test-suite-uber` during 
development). Scoping the method to the controller-injected trait avoids 
leaking it into the tag-dispatch path. The javadoc documents this constraint.
   
   ## Tests
   
   New `ControllerTagLibraryInvokerMessageSpec`:
   - dispatches to the `message` tag of the default namespace with attrs passed 
through
   - prefers the declared `taglibNamespace`, falling back to the default — 
mirroring `methodMissing` order
   - throws `MissingMethodException` when no tag library provides the tag
   - a `@CompileStatic` controller-style class invoking the canonical idiom 
compiles and resolves (this guard fails test compilation without the trait 
method)
   
   `:grails-web-taglib:test`, `:grails-gsp:test`, 
`:grails-test-suite-web:test`, and `:grails-test-suite-uber:test` all pass.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to