[ 
https://issues.apache.org/jira/browse/GROOVY-8788?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17583055#comment-17583055
 ] 

Eric Milles commented on GROOVY-8788:
-------------------------------------

It is this excerpt of 
[StaticTypeCheckingVisitor#getResultType|https://github.com/apache/groovy/blob/7f6fcb2f81b58a54bc7beef33ac382cb50a79cb4/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java#L4522]
 that determines the type of "x = map['key']" and "map['key'] = x".  Yes, it 
only looks for getAt...
{code:java}
        if (isArrayOp(op)) {
            Expression copy = binX(leftExpression, expr.getOperation(), 
rightExpression);
            copy.setSourcePosition(expr); // do not propagate 
BINARY_EXP_TARGET, etc.
            MethodNode method = findMethodOrFail(copy, left, "getAt", 
rightRedirect);
            if (method != null && !isNumberCategory(getWrapper(rightRedirect))) 
{
                return inferReturnTypeGenerics(left, method, rightExpression);
            }
            return inferComponentType(left, right);
        }
{code}

When method selection changes, it determines Object not Type for 
Map<String,Type>.  I can add a special case for Map left expression and String 
right expression.

For put, type checking is done via this block in 
[StaticTypeCheckingVisitor#visitBinaryExpression|https://github.com/apache/groovy/blob/7f6fcb2f81b58a54bc7beef33ac382cb50a79cb4/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java#L820]:
{code:java}
            if (isArrayOp(op)) {
                ...
                if (!lType.isArray()
                        && enclosingBinaryExpression != null
                        && enclosingBinaryExpression.getLeftExpression() == 
expression
                        && 
isAssignment(enclosingBinaryExpression.getOperation().getType())) {
                    // left hand side of a subscript assignment: map['foo'] = 
...
                    Expression enclosingExpressionRHS = 
enclosingBinaryExpression.getRightExpression();
                    if (!(enclosingExpressionRHS instanceof ClosureExpression)) 
{
                        enclosingExpressionRHS.visit(this);
                    }
                    ClassNode[] arguments = {rType, 
getType(enclosingExpressionRHS)};
                    List<MethodNode> methods = findMethod(lType, "putAt", 
arguments);
                    if (methods.size() == 1) {
                        typeCheckMethodsWithGenericsOrFail(lType, arguments, 
methods.get(0), enclosingExpressionRHS);
                    } else if (methods.isEmpty()) {
                        addNoMatchingMethodError(lType, "putAt", arguments, 
enclosingBinaryExpression);
                    }
                }
            }
{code}

It relies on method selection to produce "Cannot call #putAt(Map,K,V) with 
arguments ..." errors.  Since {{putAt(Object,String,Object)}} is now selected 
for string keys, this block checks nothing.  However, the result type allows 
"Cannot assign value of type X to variable of type Y" to be produced.

> Inconsistency in extension method selection with @CompileStatic
> ---------------------------------------------------------------
>
>                 Key: GROOVY-8788
>                 URL: https://issues.apache.org/jira/browse/GROOVY-8788
>             Project: Groovy
>          Issue Type: Bug
>          Components: Static compilation, Static Type Checker
>    Affects Versions: 2.4.15, 2.5.2
>            Reporter: Daniil Ovchinnikov
>            Assignee: Eric Milles
>            Priority: Major
>              Labels: breaking
>
> Given properly registered extension class:
> {code:java|title=MyExtensions.java}
> public class MyExtensions {
>     public static void foo(Object self, String s) {
>         System.out.println("Object#foo(String)");
>     }
>     public static void foo(String self, Object o) {
>         System.out.println("String#foo(Object)");
>     }
> }
> {code}
> Run
> {code:java|title=playground.groovy}
> void usageExt() {
>     "".foo("") // prints "Object#foo(String)" which is correct
> }
> @groovy.transform.CompileStatic
> void usageExtStatic() {
>     "".foo("") // prints "String#foo(Object)" which is questionable
> }
> usageExt()
> usageExtStatic()
> {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to