fernando88to opened a new pull request, #32:
URL: https://github.com/apache/grails-intellij-plugin/pull/32

     ## Problem
   
     In a Grails 3+ project, a named-argument call to `render` inside a 
controller is flagged
     as an error even though the code compiles and runs:
   
     ```groovy
     @GrailsCompileStatic
     class ErrorController {
   
         def index() {
             try {
                 doSomething()
             } catch (Exception e) {
                 log.error(e.message)
                 render(view: errorPage)   // <- "Method call is ambiguous"
             }
         }
     }
     ```
   
     The usual workaround is to force a positional argument, which resolves 
cleanly but is
     noise in the source:
   
     ```groovy
     render([view: errorPage] as Map)   // no error
     ```
   
     The same problem applies to other named-argument controller calls, e.g.
     `redirect(controller: 'foo', action: 'index')`.
   
     ## Root cause
   
     `ControllerMembersProvider` decided which members to contribute by probing 
for a single
     Grails 1.x/2.x class:
   
     ```java
     PsiClass apiClass = 
facade.findClass("org.codehaus.groovy.grails.plugins.web.api.ControllersApi", 
resolveScope);
     if (apiClass != null) {
       // Grails >= 1.4: enhance from the *Api classes
     }
     else {
       // assumed "Grails < 1.4": inject CLASS_SOURCE
       DynamicMemberUtils.process(executeProcessor, psiClass, ref, 
CLASS_SOURCE);
     }
     ```
   
     That class no longer exists in Grails 3+, so every modern project fell 
into the legacy
     branch and got `CLASS_SOURCE` injected — including:
   
     ```java
     " private void render(Map params, Closure cl = null){...}"
     ```
   
     Meanwhile Grails 3+ controllers already carry `grails.artefact.Controller`
     (`ResponseRenderer`, `ResponseRedirector`, `RequestForwarder`, 
`DataBinder`,
     `WebAttributes`, `ServletAttributes`), which 
`Grails3TraitInjectorContributor` adds as a
     supertype — so `render(Map)` is a real code member of the class.
   
     The call therefore had two applicable candidates and neither is more 
specific. Resolution
     probe on `render(view: page)` before the fix:
   
     ```
     candidates=2
       com.bar.CccController#render(java.util.Map)             [GrTraitMethod]  
           applicable=true
       <none>#render(java.util.Map, groovy.lang.Closure)        
[GrDynamicMethodWithCache]  applicable=true
       highlight[ERROR]: Method call is ambiguous
     ```
   
     And the same probe on `render([view: page] as Map)`:
   
     ```
     candidates=1
       com.bar.CccController#render(java.util.Map)             [GrTraitMethod]
       (no error)
     ```
   
     which is exactly why the `as Map` workaround appeared to help — the 
injected dynamic
     method simply stops being a candidate for a positional argument.
   
     ## Fix
   
     Do not contribute the legacy members when the trait-based controller model 
is present:
   
     ```java
     else if (facade.findClass(CONTROLLER_TRAIT_CLASS, resolveScope) == null) {
       // Grails version < 1.4: no API classes and no controller trait, so 
nothing declares these members
       if (!DynamicMemberUtils.process(executeProcessor, psiClass, ref, 
CLASS_SOURCE)) return;
     }
     ```
   
     Nothing is lost on Grails 3+. Verified against `grails-plugin-controllers` 
/
     `grails-web-common` 6.2.3 with `javap`: every member `CLASS_SOURCE` 
provided is declared
     by the trait set — `render`, `redirect`, `chain`, `forward`, `bindData`, 
`withForm`,
     `withFormat`, `hasErrors`, `errors`, `modelAndView`, `templateUri`, 
`viewUri`, plus
     `params`, `request`, `response`, `session`, `flash`, `servletContext`, 
`grailsApplication`
     and `webRequest`. The legacy versions were in fact worse: the injected 
`getParams()`
     returns `org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap`, a 
type that does
     not exist in Grails 3+.
   
     Behaviour for Grails < 1.4 and for 1.4–2.x is unchanged: the first branch 
still probes
     `ControllersApi`, and the legacy injection still runs when neither the API 
classes nor the
     controller trait are on the classpath.
   
     ## Tests
   
     New `GrailsControllerTraitMembersTest` (light fixture, 
`grails-app/controllers/` source
     root) declares the traits with `@grails.artefact.Enhances("Controller")`, 
which is the hook
     the plugin itself uses to inject them, so the real resolution path is 
exercised. Each case
     asserts that the call resolves to exactly one method — the trait's — and 
that no
     `ambiguous` highlight is produced:
   
     - `render(view: errorPage)` — the reported symptom
     - `render(text: 'hello')` — shows it is not specific to `view:`, and 
covers a gap in
       `GrailsControllerAmbiguousMethodInspectionTest`, which exercises that 
same snippet only
       on the Grails 1.4 fixture
     - `render([view: errorPage] as Map)` — keeps the positional form clean
     - `redirect(controller: 'ccc', action: 'index')` — same defect with 
identical arity
   
     With the fix reverted, the test fails and names the duplicate:
   
     ```
     render() must resolve to the single trait method, got
       com.bar.CccController#render(Map args); <none>#render(Map params, 
Closure cl = null)
     ```
   
     ## Out of scope
   
     Inner-closure support for `withFormat { html { } json { } }` is still 
keyed to
     `lightMethodKey="ControllerMembersProvider_controller_method"`
     (`grails-method-descriptors.xml`), which only exists on the Grails 1.x/2.x 
paths. On
     Grails 3+ that would need a `methodDescriptor` for 
`grails.artefact.Controller#withFormat`.
     This is a pre-existing gap — with the call ambiguous it could not resolve 
at all — and is
     left for a separate change.
   


-- 
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