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