You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/03/11 20:10:56 UTC
[groovy] 01/01: GROOVY-6919: STC: check bound types for fields, methods, properties, ...
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 911963a04a036fa03a453d041883107569301e98
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Mar 11 13:54:27 2022 -0600
GROOVY-6919: STC: check bound types for fields, methods, properties, ...
3_0_X backport
---
.../transform/stc/StaticTypeCheckingVisitor.java | 135 ++++++++-------------
.../stc/ClosureParamTypeInferenceSTCTest.groovy | 59 ++++-----
.../groovy/transform/stc/GenericsSTCTest.groovy | 132 ++++++++++++++++----
3 files changed, 182 insertions(+), 144 deletions(-)
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 2c67eca..9194c46 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -3779,44 +3779,61 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
protected List<Receiver<String>> makeOwnerList(final Expression objectExpression) {
ClassNode receiver = getType(objectExpression);
List<Receiver<String>> owners = new ArrayList<>();
- if (isClassClassNodeWrappingConcreteType(receiver)) {
- ClassNode staticType = receiver.getGenericsTypes()[0].getType();
- owners.add(Receiver.make(staticType)); // Type from Class<Type>
- addTraitType(staticType, owners); // T in Class<T$Trait$Helper>
- owners.add(Receiver.make(receiver)); // Class<Type>
- } else {
- owners.add(Receiver.make(receiver));
- if (receiver.isInterface()) {
- owners.add(Receiver.make(OBJECT_TYPE));
- }
- addSelfTypes(receiver, owners);
- addTraitType(receiver, owners);
- }
- if (!typeCheckingContext.temporaryIfBranchTypeInformation.isEmpty()) {
- List<ClassNode> potentialReceiverType = getTemporaryTypesForExpression(objectExpression);
- if (potentialReceiverType != null && !potentialReceiverType.isEmpty()) {
- for (ClassNode node : potentialReceiverType) {
- owners.add(Receiver.make(node));
- }
- }
- }
- if (typeCheckingContext.lastImplicitItType != null
- && objectExpression instanceof VariableExpression
- && ((VariableExpression) objectExpression).getName().equals("it")) {
- owners.add(Receiver.make(typeCheckingContext.lastImplicitItType));
- }
if (typeCheckingContext.delegationMetadata != null
&& objectExpression instanceof VariableExpression
&& ((VariableExpression) objectExpression).getName().equals("owner")
&& /*isNested:*/typeCheckingContext.delegationMetadata.getParent() != null) {
- owners.clear();
List<Receiver<String>> enclosingClass = Collections.singletonList(
Receiver.make(typeCheckingContext.getEnclosingClassNode()));
addReceivers(owners, enclosingClass, typeCheckingContext.delegationMetadata.getParent(), "owner.");
+ } else {
+ if (isClassClassNodeWrappingConcreteType(receiver)) {
+ ClassNode staticType = receiver.getGenericsTypes()[0].getType();
+ owners.add(Receiver.make(staticType)); // Type from Class<Type>
+ addTraitType(staticType, owners); // T in Class<T$Trait$Helper>
+ owners.add(Receiver.make(receiver)); // Class<Type>
+ } else {
+ addBoundType(receiver, owners);
+ addSelfTypes(receiver, owners);
+ addTraitType(receiver, owners);
+ if (receiver.redirect().isInterface()) {
+ owners.add(Receiver.make(OBJECT_TYPE));
+ }
+ }
+ if (!typeCheckingContext.temporaryIfBranchTypeInformation.isEmpty()) {
+ List<ClassNode> potentialReceiverType = getTemporaryTypesForExpression(objectExpression);
+ if (potentialReceiverType != null && !potentialReceiverType.isEmpty()) {
+ for (ClassNode node : potentialReceiverType) {
+ owners.add(Receiver.make(node));
+ }
+ }
+ }
+ if (typeCheckingContext.lastImplicitItType != null
+ && objectExpression instanceof VariableExpression
+ && ((VariableExpression) objectExpression).getName().equals("it")) {
+ owners.add(Receiver.make(typeCheckingContext.lastImplicitItType));
+ }
}
return owners;
}
+ private static void addBoundType(final ClassNode receiver, final List<Receiver<String>> owners) {
+ if (!receiver.isGenericsPlaceHolder() || receiver.getGenericsTypes() == null) {
+ owners.add(Receiver.make(receiver));
+ return;
+ }
+
+ GenericsType gt = receiver.getGenericsTypes()[0];
+ if (gt.getLowerBound() == null && gt.getUpperBounds() != null) {
+ for (ClassNode cn : gt.getUpperBounds()) { // T extends C & I
+ addBoundType(cn, owners);
+ addSelfTypes(cn, owners);
+ }
+ } else {
+ owners.add(Receiver.make(OBJECT_TYPE)); // T or T super Type
+ }
+ }
+
private static void addSelfTypes(final ClassNode receiver, final List<Receiver<String>> owners) {
for (ClassNode selfType : Traits.collectSelfTypes(receiver, new LinkedHashSet<>())) {
owners.add(Receiver.make(selfType));
@@ -5845,71 +5862,17 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ParameterVariableExpression(final Parameter parameter) {
super(parameter);
this.parameter = parameter;
- ClassNode inferred = parameter.getNodeMetaData(INFERRED_TYPE);
- if (inferred == null) {
- inferred = infer(parameter);
-
- parameter.setNodeMetaData(INFERRED_TYPE, inferred);
- }
- }
-
- private static ClassNode infer(final Variable variable) {
- ClassNode originType = variable.getOriginType();
-
- if (originType.isGenericsPlaceHolder()) {
- GenericsType[] genericsTypes = originType.getGenericsTypes();
-
- if (genericsTypes != null && genericsTypes.length > 0) {
- GenericsType gt = genericsTypes[0];
- ClassNode[] upperBounds = gt.getUpperBounds();
-
- if (upperBounds != null && upperBounds.length > 0) {
- return upperBounds[0];
- }
- }
- }
-
- return originType;
- }
-
- @Override
- public void copyNodeMetaData(final ASTNode other) {
- parameter.copyNodeMetaData(other);
- }
-
- @Override
- public Object putNodeMetaData(final Object key, final Object value) {
- return parameter.putNodeMetaData(key, value);
- }
-
- @Override
- public void removeNodeMetaData(final Object key) {
- parameter.removeNodeMetaData(key);
- }
-
- @Override
- public Map<?, ?> getNodeMetaData() {
- return parameter.getNodeMetaData();
- }
-
- @Override
- public <T> T getNodeMetaData(final Object key) {
- return parameter.getNodeMetaData(key);
- }
-
- @Override
- public void setNodeMetaData(final Object key, final Object value) {
- parameter.setNodeMetaData(key, value);
+ this.parameter.getNodeMetaData(INFERRED_TYPE, x -> parameter.getOriginType());
}
@Override
- public int hashCode() {
- return parameter.hashCode();
+ public Map<?, ?> getMetaDataMap() {
+ return parameter.getMetaDataMap();
}
@Override
- public boolean equals(final Object other) {
- return parameter.equals(other);
+ public void setMetaDataMap(final Map<?, ?> metaDataMap) {
+ parameter.setMetaDataMap(metaDataMap);
}
}
diff --git a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
index bc0ac8a..717a48d 100644
--- a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
@@ -22,22 +22,41 @@ package groovy.transform.stc
* Unit tests for static type checking : closure parameter type inference.
*/
class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
- void testInferenceForDGM_CollectUsingExplicitIt() {
+
+ void testInferenceForDGM_collectUsingExplicitIt() {
assertScript '''
['a','b'].collect { it -> it.toUpperCase() }
'''
- }
-
- void testInferenceForDGM_CollectUsingExplicitItAndIncorrectType() {
shouldFailWithMessages '''
['a','b'].collect { Date it -> it.toUpperCase() }
''', 'Expected parameter of type java.lang.String but got java.util.Date'
}
- void testInferenceForDGM_CollectUsingImplicitIt() {
+ void testInferenceForDGM_collectUsingImplicitIt() {
assertScript '''
['a','b'].collect { it.toUpperCase() }
'''
+ assertScript '''
+ def items = []
+ ['a','b','c'].collect(items) { it.toUpperCase() }
+ '''
+ assertScript '''
+ String[] array = ['foo', 'bar', 'baz']
+ assert array.collect { it.startsWith('ba') } == [false, true, true]
+ '''
+ assertScript '''
+ List<Boolean> answer = [true]
+ String[] array = ['foo', 'bar', 'baz']
+ array.collect(answer){it.startsWith('ba')}
+ assert answer == [true, false, true, true]
+ '''
+ assertScript '''
+ Iterator<String> iter = ['foo', 'bar', 'baz'].iterator()
+ assert iter.collect { it.startsWith('ba') } == [false, true, true]
+ '''
+ assertScript '''
+ assert [1234, 3.14].collect { it.intValue() } == [1234,3]
+ '''
}
void testInferenceForDGM_eachUsingExplicitIt() {
@@ -52,12 +71,6 @@ class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
'''
}
- void testInferenceForDGM_CollectUsingImplicitItAndLUB() {
- assertScript '''
- assert [1234, 3.14].collect { it.intValue() } == [1234,3]
- '''
- }
-
void testInferenceForDGM_countUsingFirstSignature() {
assertScript '''
def src = [a: 1, b:2, c:3]
@@ -103,13 +116,6 @@ assert result == ['b', 'r', 'e', 'a', 'd', 'b', 'u', 't', 't', 'e', 'r']
'''
}
- void testInferenceForDGM_Collect2() {
- assertScript '''
-def items = []
-['a','b','c'].collect(items) { it.toUpperCase() }
-'''
- }
-
void testInferenceForDGM_CollectMap() {
assertScript '''
assert [a: 'foo',b:'bar'].collect { k,v -> k+v } == ['afoo','bbar']
@@ -221,23 +227,6 @@ def items = []
'''
}
- void testDGM_collectOnArray() {
- assertScript '''
- String[] arr = ['foo', 'bar', 'baz']
- assert arr.collect { it.startsWith('ba') } == [false, true, true]
- List<Boolean> answer = [true]
- arr.collect(answer) { it.startsWith('ba') }
- assert answer == [true, false, true, true]
- '''
- }
-
- void testDGM_collectOnIterator() {
- assertScript '''
- Iterator<String> itr = ['foo', 'bar', 'baz'].iterator()
- assert itr.collect { it.startsWith('ba') } == [false, true, true]
- '''
- }
-
void testInferenceOnNonExtensionMethod() {
assertScript '''import groovy.transform.stc.ClosureParams
import groovy.transform.stc.FirstParam
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index ae365ab..c16b875 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1726,29 +1726,23 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
void testOutOfBoundsByExtendsPlaceholderParameterType() {
shouldFailWithMessages '''
class Foo {
- static <T extends List<? extends CharSequence>> void bar(T a) {}
+ static <T extends List<? extends CharSequence>> void bar(T list) {}
}
- class Baz {
- static <T extends List<Object>> void qux(T a) {
- Foo.bar(a)
- }
+ def <U extends List<Object>> void baz(U list) {
+ Foo.bar(list)
}
- Baz.qux([new Object()])
- ''', 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.List <Object>]'
+ ''', 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
}
void testOutOfBoundsBySuperPlaceholderParameterType() {
shouldFailWithMessages '''
class Foo {
- static <T extends List<? super CharSequence>> void bar(T a) {}
+ static <T extends List<? super CharSequence>> void bar(T list) {}
}
- class Baz {
- static <T extends List<String>> void qux(T a) {
- Foo.bar(a)
- }
+ def <U extends List<String>> void baz(U list) {
+ Foo.bar(list)
}
- Baz.qux(['abc'])
- ''', 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.List <String>]'
+ ''', 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
}
// GROOVY-5721
@@ -2172,8 +2166,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'#foo(java.util.List <? extends A>) with arguments [java.util.ArrayList <java.lang.Object>]'
}
+ // GROOVY-5891
void testMethodLevelGenericsForMethodCall() {
- // Groovy-5891
assertScript '''
public <T extends List<Integer>> T foo(Class<T> type, def x) {
return type.cast(x)
@@ -2187,24 +2181,116 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
def cl = {1}
assert foo(cl.class, cl) == cl
- '''
- assertScript '''
+ '''
+ assertScript '''
public <T extends Runnable> T foo(Class<T> type, def x) {
return type.cast(x) as T
}
def cl = {1}
assert foo(cl.class, cl) == cl
- '''
- // GROOVY-5885
- assertScript '''
+ '''
+ }
+
+ // GROOVY-5885
+ void testMethodLevelGenericsForMethodCall2() {
+ assertScript '''
class Test {
- public <X extends Test> X castToMe(Class<X> type, Object o) {
+ public <T extends Test> T castToMe(Class<T> type, Object o) {
return type.cast(o);
}
}
def t = new Test()
- assert t.castToMe(Test, t) == t
- '''
+ assert t == t.castToMe(Test, t)
+ '''
+ }
+
+ // GROOVY-6919
+ void testMethodLevelGenericsForMethodCall3() {
+ assertScript '''
+ interface I1 {
+ String getFoo()
+ }
+ interface I2 {
+ String getBar()
+ }
+ def <T extends I1 & I2> void test(T obj) {
+ obj?.getFoo()
+ obj?.getBar()
+ }
+ test(null)
+ '''
+ }
+
+ // GROOVY-6919
+ void testMethodLevelGenericsForPropertyRead() {
+ assertScript '''
+ interface I1 {
+ String getFoo()
+ }
+ interface I2 {
+ String getBar()
+ }
+ def <T extends I1 & I2> void test(T obj) {
+ obj?.foo
+ obj?.bar
+ }
+ test(null)
+ '''
+ }
+
+ @NotYetImplemented
+ void testMethodLevelGenericsForPropertyRead2() {
+ assertScript '''
+ interface I1 {
+ static String getFoo() { 'foo' }
+ }
+ interface I2 {
+ String bar = 'bar'
+ }
+ def <T extends I1 & I2> void test(Class<T> cls) {
+ cls?.foo
+ cls?.bar
+ }
+ test(null)
+ '''
+ }
+
+ void testMethodLevelGenericsForPropertyRead3() {
+ assertScript '''
+ interface I1 {
+ String getFoo()
+ }
+ interface I2 {
+ String getBar()
+ }
+ class C<T extends I1 & I2> {
+ def <U extends T> void test(U obj) {
+ obj?.foo
+ obj?.bar
+ }
+ }
+ new C().test(null)
+ '''
+ }
+
+ void testMethodLevelGenericsForPropertyRead4() {
+ assertScript '''
+ interface I1 {
+ String getFoo()
+ }
+ interface I2 {
+ String getBar()
+ }
+ @groovy.transform.SelfType(I2) trait T2 {
+ abstract String getBaz()
+ }
+ def <T extends I1 & T2> void test(T obj) {
+ obj?.foo
+ obj?.bar
+ obj?.baz
+ }
+ test(null)
+ '''
}
// GROOVY-5839