Paul King created GROOVY-12632:
----------------------------------
Summary: CS: reading an attribute obj.@name where the receiver
class has no field name, but there is an extension getter getName(), compiles
to a call of that getter
Key: GROOVY-12632
URL: https://issues.apache.org/jira/browse/GROOVY-12632
Project: Groovy
Issue Type: Bug
Reporter: Paul King
Fix For: 7.x
h2. Description
Under {{@CompileStatic}}, reading an attribute ({{obj.@name}}) where the
receiver class has no field {{name}}, but there is an extension getter
{{getName()}}, compiles to a call of that getter. Attribute access is field
access, so it should not. Dynamic code throws {{MissingFieldException}} for the
same expression, and {{@TypeChecked}} code compiles and then throws it at run
time.
{code:groovy}
def sb = new StringBuilder('abc')
assert sb.@length == 3 // under @CompileStatic: calls
StringGroovyMethods.getLength(StringBuilder)
{code}
|| expression || dynamic || {{@TypeChecked}} || {{@CompileStatic}} ||
| {{sb.@length}} | {{MissingFieldException}} | compiles,
{{MissingFieldException}} | {{3}} |
| {{'abc'.@chars}} | {{MissingFieldException}} | compiles,
{{MissingFieldException}} | {{abc}} (calls {{getChars()}}) |
| {{file.@text}} | {{MissingFieldException}} | compiles,
{{MissingFieldException}} | reads the file (calls {{getText()}}) |
When the getter is a real method, the type checker already rejects the
attribute:
{code:groovy}
class B { int getX() { 1 } }
new B().@x // [Static type checking] - No such attribute: x for class: B
{code}
h2. Versions
The {{@CompileStatic}} getter call is seen on 4.0.32, 5.0.6, 6.0.0 and master.
On 3.0.25 the same {{@CompileStatic}} code throws {{MissingFieldException}}, as
dynamic code does. {{sb.@length}} gives "No such attribute" on 4.0.32 only
because the extension {{getLength()}} did not exist then.
h2. Cause
When it resolves an {{AttributeExpression}}, the type checker falls back to
extension accessors when no field matches. It records the extension getter as
the {{DIRECT_METHOD_CALL_TARGET}}:
{noformat}
sb.@length -> java.lang.StringBuilder#getLength() [extension]
{noformat}
Static compilation then writes a direct call to that getter. For a write, the
same resolution makes the compiler crash; see GROOVY-12631.
h2. Suggested fix
Do not let extension accessors satisfy an attribute expression. With no field,
report {{No such attribute: <name> for class: <type>}}, as is done for real
accessors. That makes {{@CompileStatic}} and {{@TypeChecked}} agree with each
other, and the compile error replaces the run-time {{MissingFieldException}} in
both.
This is a behaviour change for {{@CompileStatic}} code that relies on
{{obj.@name}} calling an extension getter. Such code has never worked in
dynamic or {{@TypeChecked}} code, and the property form ({{obj.name}}) is the
intended spelling. It probably still deserves a COMPATIBILITY note.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)