This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 6f0f5ce GROOVY-9033: STC: resolve "def list = []" to List<Object> not
List<E>
6f0f5ce is described below
commit 6f0f5cee090616149de777123d8dd2843777b615
Author: Eric Milles <[email protected]>
AuthorDate: Fri Apr 16 16:01:20 2021 -0500
GROOVY-9033: STC: resolve "def list = []" to List<Object> not List<E>
---
.../transform/stc/StaticTypeCheckingSupport.java | 79 ++++++++++------------
.../transform/stc/StaticTypeCheckingVisitor.java | 36 ++++++----
.../groovy/transform/stc/GenericsSTCTest.groovy | 54 ++++++++++-----
.../classgen/asm/sc/BugsStaticCompileTest.groovy | 8 +--
.../ginq/provider/collection/GinqAstWalker.groovy | 4 +-
5 files changed, 98 insertions(+), 83 deletions(-)
diff --git
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index d9f8c3e..9b47aaa 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1326,32 +1326,33 @@ public abstract class StaticTypeCheckingSupport {
}
protected static ClassNode fullyResolveType(final ClassNode type, final
Map<GenericsTypeName, GenericsType> placeholders) {
- if (type.isUsingGenerics() && !type.isGenericsPlaceHolder()) {
- GenericsType[] gts = type.getGenericsTypes();
- if (gts != null) {
- GenericsType[] copy = new GenericsType[gts.length];
- for (int i = 0, n = gts.length; i < n; i += 1) {
- GenericsType genericsType = gts[i];
- if (genericsType.isPlaceholder() &&
placeholders.containsKey(new GenericsTypeName(genericsType.getName()))) {
- copy[i] = placeholders.get(new
GenericsTypeName(genericsType.getName()));
- } else {
- copy[i] = fullyResolve(genericsType, placeholders);
- }
- }
- gts = copy;
- }
- ClassNode result = type.getPlainNodeReference();
- result.setGenericsTypes(gts);
- return result;
- } else if (type.isUsingGenerics() && OBJECT_TYPE.equals(type) &&
type.getGenericsTypes() != null) {
- // Object<T>
- GenericsType genericsType = placeholders.get(new
GenericsTypeName(type.getGenericsTypes()[0].getName()));
- if (genericsType != null) {
- return genericsType.getType();
- }
- } else if (type.isArray()) {
+ if (type.isArray()) {
return fullyResolveType(type.getComponentType(),
placeholders).makeArray();
}
+ if (type.isUsingGenerics()) {
+ if (type.isGenericsPlaceHolder()) {
+ GenericsType gt = placeholders.get(new
GenericsTypeName(type.getUnresolvedName()));
+ if (gt != null) {
+ return gt.getType();
+ }
+ ClassNode cn = type.redirect();
+ return cn != type ? cn : OBJECT_TYPE;
+ } else {
+ GenericsType[] gts = type.getGenericsTypes();
+ if (gts != null) {
+ gts = Arrays.stream(gts).map(gt -> {
+ if (gt.isPlaceholder()) {
+ GenericsTypeName gtn = new
GenericsTypeName(gt.getName());
+ return placeholders.getOrDefault(gtn,
extractType(gt).asGenericsType());
+ }
+ return fullyResolve(gt, placeholders);
+ }).toArray(GenericsType[]::new);
+ }
+ ClassNode cn = type.getPlainNodeReference();
+ cn.setGenericsTypes(gts);
+ return cn;
+ }
+ }
return type;
}
@@ -1751,7 +1752,7 @@ public abstract class StaticTypeCheckingSupport {
}
public static ClassNode getCorrectedClassNode(final ClassNode type, final
ClassNode superClass, final boolean handlingGenerics) {
- if (handlingGenerics && missesGenericsTypes(type)) return
superClass.getPlainNodeReference();
+ if (handlingGenerics && GenericsUtils.hasUnresolvedGenerics(type))
return superClass.getPlainNodeReference();
return
GenericsUtils.correctToGenericsSpecRecurse(GenericsUtils.createGenericsSpec(type),
superClass);
}
@@ -2142,26 +2143,14 @@ public abstract class StaticTypeCheckingSupport {
return node.getSuperClass() != null &&
isParameterizedWithString(node.getUnresolvedSuperClass());
}
- public static boolean missesGenericsTypes(final ClassNode cn) {
- if (cn.isArray()) return missesGenericsTypes(cn.getComponentType());
- GenericsType[] cnTypes = cn.getGenericsTypes();
- GenericsType[] rnTypes = cn.redirect().getGenericsTypes();
- if (rnTypes != null && cnTypes == null) return true;
- if (cnTypes != null) {
- for (GenericsType genericsType : cnTypes) {
- if (genericsType.isPlaceholder()) return true;
- if (genericsType.isWildcard()) {
- ClassNode lowerBound = genericsType.getLowerBound();
- ClassNode[] upperBounds = genericsType.getUpperBounds();
- if (lowerBound != null) {
- if (lowerBound.isGenericsPlaceHolder() ||
missesGenericsTypes(lowerBound)) return true;
- } else if (upperBounds != null) {
- if (upperBounds[0].isGenericsPlaceHolder() ||
missesGenericsTypes(upperBounds[0])) return true;
- }
- }
- }
- }
- return false;
+ /**
+ * Determines if node is a raw type or references any generics
placeholders.
+ */
+ public static boolean missesGenericsTypes(ClassNode cn) {
+ while (cn.isArray()) cn = cn.getComponentType();
+ GenericsType[] cnGenerics = cn.getGenericsTypes();
+ GenericsType[] rnGenerics = cn.redirect().getGenericsTypes();
+ return cnGenerics == null ? rnGenerics != null :
GenericsUtils.hasUnresolvedGenerics(cn);
}
/**
diff --git
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 5daab43..b546226 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -794,15 +794,20 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
if (resultType == null) {
resultType = lType;
- } else if (lType.isUsingGenerics() && isAssignment(op) &&
missesGenericsTypes(resultType)) {
- // unchecked assignment
- // List<Type> list = new LinkedList()
- // Iterable<Type> iter = new LinkedList()
- // Collection<Type> coll = Collections.emptyList()
+ } else if (isAssignment(op)) {
+ if (lType.isUsingGenerics() &&
missesGenericsTypes(resultType)) {
+ // unchecked assignment
+ // List<Type> list = new LinkedList()
+ // Iterable<Type> iter = new LinkedList()
+ // Collection<Type> coll = Collections.emptyList()
- // the inferred type of the binary expression is the type of
the RHS
- // "completed" with generics type information available from
the LHS
- resultType = GenericsUtils.parameterizeType(lType,
resultType.getPlainNodeReference());
+ // the inferred type of the binary expression is the type
of the RHS
+ // "completed" with generics type information available
from the LHS
+ resultType = GenericsUtils.parameterizeType(lType,
resultType.getPlainNodeReference());
+ } else if (lType.equals(OBJECT_TYPE) &&
GenericsUtils.hasUnresolvedGenerics(resultType)) { // def list = []
+ Map<GenericsTypeName, GenericsType> placeholders =
extractGenericsParameterMapOfThis(typeCheckingContext);
+ resultType = fullyResolveType(resultType,
Optional.ofNullable(placeholders).orElseGet(Collections::emptyMap));
+ }
}
// GROOVY-5874: if left expression is a closure shared variable, a
second pass should be done
@@ -841,7 +846,7 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
// and we must update the result type
if (!implementsInterfaceOrIsSubclassOf(getWrapper(resultType),
getWrapper(originType))) {
resultType = originType;
- } else if (lType.isUsingGenerics() && !lType.isEnum() &&
hasRHSIncompleteGenericTypeInfo(resultType)) {
+ } else if (lType.isUsingGenerics() && !lType.isEnum() &&
GenericsUtils.hasUnresolvedGenerics(resultType)) {
// for example, LHS is List<ConcreteClass> and RHS is
List<T> where T is a placeholder
resultType = lType;
} else {
@@ -1260,9 +1265,9 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
private void checkTypeGenerics(final ClassNode leftExpressionType, final
ClassNode wrappedRHS, final Expression rightExpression) {
// last, check generic type information to ensure that inferred types
are compatible
if (!leftExpressionType.isUsingGenerics()) return;
- // List<Foo> l = new List() is an example for incomplete generics type
info
- // we assume arity related errors are already handled here.
- if (hasRHSIncompleteGenericTypeInfo(wrappedRHS)) return;
+ // example of incomplete type info: "List<Type> list = new
LinkedList()"
+ // we assume arity related errors are already handled here
+ if (missesGenericsTypes(wrappedRHS)) return;
GenericsType gt = GenericsUtils.buildWildcardType(leftExpressionType);
if (UNKNOWN_PARAMETER_TYPE.equals(wrappedRHS) ||
@@ -1356,6 +1361,7 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
typeCheckingContext.popEnclosingBinaryExpression();
}
+ @Deprecated
protected static boolean hasRHSIncompleteGenericTypeInfo(final ClassNode
inferredRightExpressionType) {
boolean replaceType = false;
GenericsType[] genericsTypes =
inferredRightExpressionType.getGenericsTypes();
@@ -5658,11 +5664,13 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
protected boolean typeCheckMethodsWithGenericsOrFail(final ClassNode
receiver, final ClassNode[] arguments, final MethodNode candidateMethod, final
Expression location) {
if (!typeCheckMethodsWithGenerics(receiver, arguments,
candidateMethod)) {
- Map<GenericsTypeName, GenericsType> classGTs =
GenericsUtils.extractPlaceholders(receiver);
+ Map<GenericsTypeName, GenericsType> generics =
GenericsUtils.extractPlaceholders(receiver);
+
applyGenericsConnections(extractGenericsParameterMapOfThis(typeCheckingContext),
generics);
+ addMethodLevelDeclaredGenerics(candidateMethod, generics);
Parameter[] parameters = candidateMethod.getParameters();
ClassNode[] paramTypes = new ClassNode[parameters.length];
for (int i = 0, n = parameters.length; i < n; i += 1) {
- paramTypes[i] = fullyResolveType(parameters[i].getType(),
classGTs);
+ paramTypes[i] = fullyResolveType(parameters[i].getType(),
generics);
// GROOVY-10010: check for List<String> parameter and
["foo","$bar"] argument
if (i < arguments.length &&
hasGStringStringError(paramTypes[i], arguments[i], location)) {
return false;
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 4d4ce4e..a542b59 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -146,7 +146,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- void testReturnTypeInference() {
+ void testReturnTypeInference1() {
assertScript '''
class Foo<U> {
U method() { }
@@ -156,13 +156,12 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- void testReturnTypeInferenceWithDiamond() {
+ void testReturnTypeInference2() {
assertScript '''
- class Foo<U> {
- U method() { }
- }
- Foo<Integer> foo = new Foo<>()
- Integer result = foo.method()
+ Object m() {
+ def s = '1234'
+ println 'Hello'
+ }
'''
}
@@ -217,20 +216,48 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-9033
+ void testReturnTypeInferenceWithMethodGenerics8() {
+ shouldFailWithMessages '''
+ List<String> test() {
+ def x = [].each { }
+ x.add(new Object())
+ return x // List<E>
+ }
+ ''', 'Incompatible generic argument types.' // Cannot assign
java.util.List<java.lang.Object> to: java.util.List<java.lang.String>
+
+ assertScript '''
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.getNodeMetaData(INFERRED_TYPE)
+ assert type.genericsTypes[0].toString() == 'java.lang.String'
+ assert type.genericsTypes[1].toString() ==
'java.util.List<java.lang.Object>' // not List<E>
+ })
+ def map = [ key: [] ]
+ '''
+ }
+
void testDiamondInferrenceFromConstructor1() {
assertScript '''
- Set<Long> set = new HashSet<>()
+ class Foo<U> {
+ U method() { }
+ }
+ Foo<Integer> foo = new Foo<>()
+ Integer result = foo.method()
'''
}
void testDiamondInferrenceFromConstructor2() {
assertScript '''
- new HashSet<>(Arrays.asList(0L))
+ Set<Long> set = new HashSet<>()
'''
}
void testDiamondInferrenceFromConstructor3() {
assertScript '''
+ new HashSet<>(Arrays.asList(0L))
+ '''
+
+ assertScript '''
Set<Number> set = new HashSet<>(Arrays.asList(0L))
'''
@@ -726,15 +753,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
''', 'Cannot find matching method
FooWithGenerics#say(java.lang.Object)'
}
- void testVoidReturnTypeInferrence() {
- assertScript '''
- Object m() {
- def s = '1234'
- println 'Hello'
- }
- '''
- }
-
// GROOVY-5237
void testGenericTypeArgumentAsField() {
assertScript '''
diff --git
a/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
b/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
index 1bcca69..e567be9 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
@@ -1157,9 +1157,9 @@ assert it.next() == 1G
def map = new LinkedHashMap<>([a:1,b:2])
@ASTTest(phase=INSTRUCTION_SELECTION, value={
def ift = node.getNodeMetaData(INFERRED_TYPE)
- assert ift == make(Set)
+ assert ift == SET_TYPE
assert ift.isUsingGenerics()
- assert ift.genericsTypes[0].type==STRING_TYPE
+ assert ift.genericsTypes[0].type == STRING_TYPE
})
def set = map.keySet()
def key = set[0]
@@ -1169,9 +1169,9 @@ assert it.next() == 1G
def map = new LinkedHashMap([a:1,b:2])
@ASTTest(phase=INSTRUCTION_SELECTION, value={
def ift = node.getNodeMetaData(INFERRED_TYPE)
- assert ift == make(Set)
+ assert ift == SET_TYPE
assert ift.isUsingGenerics()
- assert ift.genericsTypes[0].name=='K'
+ assert ift.genericsTypes[0].name == 'java.lang.Object'
})
def set = map.keySet()
def key = set[0]
diff --git
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
index 15dffdc..785e8e2 100644
---
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
+++
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
@@ -820,7 +820,7 @@ class GinqAstWalker implements GinqAstVisitor<Expression>,
SyntaxErrorReportable
}
})).getExpression(0)
- def extra = []
+ List<Expression> extra = []
if (enableCount || rowNumberUsed) {
currentGinqExpression.putNodeMetaData(__RN_USED, true)
extra << callX(varX(rowNumberName), 'getAndIncrement')
@@ -873,7 +873,7 @@ class GinqAstWalker implements GinqAstVisitor<Expression>,
SyntaxErrorReportable
validateWindowClause(classifierExpr, orderExpr, rowsExpr,
rangeExpr, ignoredMethodCallExpressionList)
}
- def argumentExpressionList = []
+ List<Expression> argumentExpressionList = []
if (classifierExpr) {
List<Expression> expressionList = ((ArgumentListExpression)
classifierExpr).getExpressions()