This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY_4_0_X in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 49f630daa2bccd84710d2640fecc7301f788f61e Author: Eric Milles <[email protected]> AuthorDate: Tue Aug 16 10:17:13 2022 -0500 GROOVY-8828: STC: `UnionTypeClassNode` plain node reference semantics --- .../transform/stc/StaticTypeCheckingSupport.java | 27 +++++++++------------- .../groovy/transform/stc/UnionTypeClassNode.java | 23 ++++++++++++------ .../transform/stc/TypeInferenceSTCTest.groovy | 16 +++++++++++++ 3 files changed, 43 insertions(+), 23 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 7e828d5745..3ef5df9ea4 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java @@ -893,31 +893,26 @@ public abstract class StaticTypeCheckingSupport { } public static boolean implementsInterfaceOrIsSubclassOf(final ClassNode type, final ClassNode superOrInterface) { - boolean result = (type.equals(superOrInterface) + if (type.isArray() && superOrInterface.isArray()) { + return implementsInterfaceOrIsSubclassOf(type.getComponentType(), superOrInterface.getComponentType()); + } + if (type == UNKNOWN_PARAMETER_TYPE // aka null || type.isDerivedFrom(superOrInterface) - || type.implementsInterface(superOrInterface) - || type == UNKNOWN_PARAMETER_TYPE); - if (result) { + || type.implementsInterface(superOrInterface)) { return true; } if (superOrInterface instanceof WideningCategories.LowestUpperBoundClassNode) { - WideningCategories.LowestUpperBoundClassNode cn = (WideningCategories.LowestUpperBoundClassNode) superOrInterface; - result = implementsInterfaceOrIsSubclassOf(type, cn.getSuperClass()); - if (result) { - for (ClassNode interfaceNode : cn.getInterfaces()) { - result = type.implementsInterface(interfaceNode); - if (!result) break; - } + if (implementsInterfaceOrIsSubclassOf(type, superOrInterface.getSuperClass()) + && Arrays.stream(superOrInterface.getInterfaces()).allMatch(type::implementsInterface)) { + return true; } - if (result) return true; } else if (superOrInterface instanceof UnionTypeClassNode) { for (ClassNode delegate : ((UnionTypeClassNode) superOrInterface).getDelegates()) { - if (implementsInterfaceOrIsSubclassOf(type, delegate)) return true; + if (implementsInterfaceOrIsSubclassOf(type, delegate)) { + return true; + } } } - if (type.isArray() && superOrInterface.isArray()) { - return implementsInterfaceOrIsSubclassOf(type.getComponentType(), superOrInterface.getComponentType()); - } if (isGroovyObjectType(superOrInterface) && isBeingCompiled(type) && !type.isInterface()) {//TODO: !POJO !Trait return true; } diff --git a/src/main/java/org/codehaus/groovy/transform/stc/UnionTypeClassNode.java b/src/main/java/org/codehaus/groovy/transform/stc/UnionTypeClassNode.java index 1513421c27..7d79463d8d 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/UnionTypeClassNode.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/UnionTypeClassNode.java @@ -43,6 +43,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.StringJoiner; /** * This class node type is very special and should only be used by the static type checker @@ -57,18 +58,17 @@ import java.util.Set; class UnionTypeClassNode extends ClassNode { private final ClassNode[] delegates; - public UnionTypeClassNode(ClassNode... classNodes) { - super("<UnionType:" + asArrayDescriptor(classNodes) + ">", 0, ClassHelper.OBJECT_TYPE); + UnionTypeClassNode(final ClassNode... classNodes) { + super(makeName(classNodes), 0, ClassHelper.OBJECT_TYPE); delegates = classNodes == null ? ClassNode.EMPTY_ARRAY : classNodes; } - private static String asArrayDescriptor(ClassNode... nodes) { - StringBuilder sb = new StringBuilder(); + private static String makeName(final ClassNode[] nodes) { + StringJoiner sj = new StringJoiner("+", "<UnionType:", ">"); for (ClassNode node : nodes) { - if (sb.length() > 0) sb.append("+"); - sb.append(node.getText()); + sj.add(node.getText()); } - return sb.toString(); + return sj.toString(); } public ClassNode[] getDelegates() { @@ -306,6 +306,15 @@ class UnionTypeClassNode extends ClassNode { return nodes; } + @Override + public ClassNode getPlainNodeReference(final boolean skipPrimitives) { + int n = delegates.length; ClassNode[] plainNodes = new ClassNode[n]; + for (int i = 0; i < n; i += 1) { + plainNodes[i] = delegates[i].getPlainNodeReference(skipPrimitives); + } + return new UnionTypeClassNode(plainNodes); + } + @Override public List<PropertyNode> getProperties() { List<PropertyNode> nodes = new LinkedList<PropertyNode>(); diff --git a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy index c172240660..372cc054c8 100644 --- a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy +++ b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy @@ -396,6 +396,22 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase { } } + // GROOVY-8828 + void testMultipleInstanceOf7() { + assertScript ''' + interface Foo { } + interface Bar { String name() } + + Map<String, Foo> map = [:] + map.values().each { foo -> + if (foo instanceof Bar) { + String name = foo.name() // method available through Bar + map.put(name, foo) // second parameter expects Foo + } + } + ''' + } + // GROOVY-8523 void testNotInstanceof1() { assertScript '''
