Paul King created GROOVY-12041:
----------------------------------
Summary: STC: unresolvedVariable/unresolvedProperty no longer
fires when receiver inherits getProperty(String)
Key: GROOVY-12041
URL: https://issues.apache.org/jira/browse/GROOVY-12041
Project: Groovy
Issue Type: Bug
Reporter: Paul King
A {{TypeCheckingDSL}} extension that captures the receiver in
{{unresolvedVariable}} / {{unresolvedProperty}} and later matches it by node
identity in {{methodNotFound}} stops working in Groovy 5 when the enclosing
class inherits {{getProperty(String)}} (e.g. extends {{Script}} or a GSP-style
{{GroovyPage}} base). The {{unresolvedVariable}} / {{unresolvedProperty}}
callback never fires, so the extension has no chance to record the receiver,
and the downstream {{methodNotFound}} rejects the chained call.
h2. Observed behaviour
Reproducer extension (abridged):
{code:groovy}
newScope { dynamicProperties = [] as Set }
unresolvedVariable { VariableExpression ve ->
if (ve.name == 'g') { currentScope.dynamicProperties << ve; return
makeDynamic(ve) }
}
unresolvedProperty { PropertyExpression pe ->
if (pe.propertyAsString == 'g') { currentScope.dynamicProperties << pe;
return makeDynamic(pe) }
}
methodNotFound { receiver, name, argList, argTypes, call ->
if (call.objectExpression != null
&& currentScope.dynamicProperties.contains(call.objectExpression)) {
return makeDynamic(call)
}
}
{code}
Results across versions (running {{g.message(code: 'World')}} inside the
{{Subject}}):
||Subject shape||4.0.27||5.0.5 / 5.0.6||
|Plain class, {{g.message(...)}}|PASS ({{unresolvedVariable}} fires)|PASS
(fires twice, but works)|
|Plain class, {{this.g.message(...)}}|PASS ({{unresolvedProperty}} fires)|PASS|
|{{extends Script}}, {{g.message(...)}}|*PASS*|*FAIL* — {{unresolvedVariable}}
never fires|
|{{extends Base}} with {{Object getProperty(String)}},
{{g.message(...)}}|*PASS*|*FAIL* — same|
|{{extends Script}}, {{this.g.message(...)}}|PASS|PASS|
The Groovy 5 failure is:
{noformat}
[Static type checking] - Cannot find matching method
java.lang.Object#message(LinkedHashMap<String,String>)
{noformat}
Diagnostic prints confirm that on the failing cases the extension is *not*
called for {{g}} at all; {{methodNotFound}} fires with an empty
{{dynamicProperties}} set. Where the callbacks do fire, AST node identity is
*preserved* across callbacks on Groovy 5 — i.e. the originally reported "node
identity changes between callbacks" hypothesis is not what is happening.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)