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]
