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)

Reply via email to