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 2023/12/18 01:14:46 UTC

(groovy) branch GROOVY_4_0_X updated: GROOVY-11250: `UnionTypeClassNode` super class

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


The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
     new 8f0461399a GROOVY-11250: `UnionTypeClassNode` super class
8f0461399a is described below

commit 8f0461399a9c1f51872766cf5be715aada9ea39d
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Dec 17 18:56:28 2023 -0600

    GROOVY-11250: `UnionTypeClassNode` super class
---
 .../codehaus/groovy/ast/tools/GenericsUtils.java   |   7 +-
 .../groovy/transform/stc/UnionTypeClassNode.java   |  23 +++-
 src/test/groovy/bugs/Groovy11250.groovy            | 132 +++++++++++++++++++++
 3 files changed, 155 insertions(+), 7 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index ca75cf559c..0192d3510d 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -54,7 +54,6 @@ import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
-import static groovy.lang.Tuple.tuple;
 import static org.apache.groovy.util.SystemUtil.getSystemPropertySafe;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.plus;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf;
@@ -434,7 +433,7 @@ public class GenericsUtils {
                     newgTypes[i] = fixed;
                 } else if (oldgType.isPlaceholder()) {
                     // correct "T"
-                    newgTypes[i] = new GenericsType(genericsSpec.getOrDefault(oldgType.getName(), ClassHelper.OBJECT_TYPE));
+                    newgTypes[i] = genericsSpec.containsKey(oldgType.getName())? new GenericsType(genericsSpec.get(oldgType.getName())): erasure(oldgType);
                 } else {
                     // correct "List<T>", etc.
                     newgTypes[i] = new GenericsType(correctToGenericsSpecRecurse(genericsSpec, correctToGenericsSpec(genericsSpec, oldgType), exclusions));
@@ -515,9 +514,9 @@ public class GenericsUtils {
                     }
                     if (redirect.isGenericsPlaceHolder()) {
                         // "T extends U" or "T super U"
-                        type = redirect;
+                        type = redirect; // GROOVY-9059
                     } else {
-                        // "T" or "T extends Thing" or "T super Thing"
+                        // "T" or "T extends Type" or "T super Type"
                         type = ClassHelper.makeWithoutCaching(name);
                         type.setGenericsPlaceHolder(true);
                         type.setRedirect(redirect);
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 7d79463d8d..5df5e76161 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/UnionTypeClassNode.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/UnionTypeClassNode.java
@@ -20,7 +20,6 @@ package org.codehaus.groovy.transform.stc;
 
 import org.codehaus.groovy.ast.ASTNode;
 import org.codehaus.groovy.ast.AnnotationNode;
-import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.ConstructorNode;
 import org.codehaus.groovy.ast.FieldNode;
@@ -35,6 +34,7 @@ import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.transform.ASTTransformation;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -45,6 +45,10 @@ import java.util.Map;
 import java.util.Set;
 import java.util.StringJoiner;
 
+import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
+import static org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound;
+import static org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode;
+
 /**
  * This class node type is very special and should only be used by the static type checker
  * to represent types which are the union of other types. This is useful when, for example,
@@ -59,8 +63,9 @@ class UnionTypeClassNode extends ClassNode {
     private final ClassNode[] delegates;
 
     UnionTypeClassNode(final ClassNode... classNodes) {
-        super(makeName(classNodes), 0, ClassHelper.OBJECT_TYPE);
-        delegates = classNodes == null ? ClassNode.EMPTY_ARRAY : classNodes;
+        super(makeName(classNodes), 0, makeSuper(classNodes));
+        delegates = classNodes;
+        isPrimaryNode = false;
     }
 
     private static String makeName(final ClassNode[] nodes) {
@@ -71,6 +76,18 @@ class UnionTypeClassNode extends ClassNode {
         return sj.toString();
     }
 
+    private static ClassNode makeSuper(final ClassNode[] nodes) {
+        ClassNode upper = lowestUpperBound(Arrays.asList(nodes));
+        if (upper instanceof LowestUpperBoundClassNode) {
+            upper = upper.getUnresolvedSuperClass();
+        } else if (upper.isInterface()) {
+            upper = OBJECT_TYPE;
+        }
+        return upper;
+    }
+
+    //--------------------------------------------------------------------------
+
     public ClassNode[] getDelegates() {
         return delegates;
     }
diff --git a/src/test/groovy/bugs/Groovy11250.groovy b/src/test/groovy/bugs/Groovy11250.groovy
new file mode 100644
index 0000000000..e1fbf071e0
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy11250.groovy
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs
+
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
+import org.junit.Test
+
+final class Groovy11250 {
+
+    @Test
+    void testMultipleInstanceOfPropertyAccess() {
+        def config = new CompilerConfiguration().tap {
+            jointCompilationOptions = [memStub: true]
+            targetDirectory = File.createTempDir()
+        }
+        File parentDir = File.createTempDir()
+        try {
+            def a = new File(parentDir, 'Property.java')
+            a.write '''
+                public class Property {
+                    private String targetName;
+                    private String propertyName;
+                    public String getName() { return propertyName; }
+                    public String getTargetName() { return targetName; }
+                    public void setTargetName(String targetName) { this.targetName = targetName; }
+                    public void setPropertyName(String propertyName) { this.propertyName = propertyName; }
+                }
+            '''
+
+            def b = new File(parentDir, 'PersistentProperty.java')
+            b.write '''
+                public interface PersistentProperty<T extends Property> {
+                    String getName();
+                }
+            '''
+
+            def c = new File(parentDir, 'AbstractPersistentProperty.java')
+            c.write '''
+                public abstract class AbstractPersistentProperty<T extends Property> implements PersistentProperty<T> {
+                    protected final String name;
+                    public AbstractPersistentProperty(String name) {
+                        this.name = name;
+                    }
+                    @Override
+                    public String getName() {
+                        return name;
+                    }
+                }
+            '''
+
+            def d = new File(parentDir, 'Association.java')
+            d.write '''
+                public abstract class Association<T extends Property> extends AbstractPersistentProperty<T> {
+                    public Association(String name) {
+                        super(name);
+                    }
+                }
+            '''
+
+            def e = new File(parentDir, 'Main.groovy')
+            e.write '''import groovy.transform.CompileStatic
+                @CompileStatic
+                class Attribute extends Property {
+                }
+                @CompileStatic
+                abstract class ToOne<T extends Property> extends Association<T> {
+                    ToOne(String name) { super(name) }
+                }
+                @CompileStatic
+                abstract class ToMany<T extends Property> extends Association<T> {
+                    ToMany(String name) { super(name) }
+                }
+
+                @CompileStatic
+                abstract class OneToOne<T extends Property> extends ToOne<T> {
+                    OneToOne(String name) { super(name) }
+                }
+                @CompileStatic
+                abstract class OneToMany<T extends Property> extends ToMany<T> {
+                    OneToMany(String name) { super(name) }
+                }
+                @CompileStatic
+                abstract class ManyToMany<T extends Property> extends ToMany<T> {
+                    ManyToMany(String name) { super(name) }
+                }
+
+                @CompileStatic
+                static main(args) {
+                    def oneToOne = new OneToOne<Attribute>('foo') {}
+                    def oneToMany = new OneToMany<Attribute>('bar') {}
+                    for (association in [oneToOne, oneToMany]) {
+                        if (association instanceof ToOne) {
+                            def propertyName = association.name
+                            println "to-one -> $propertyName"
+                        } else if (association instanceof OneToMany || association instanceof ManyToMany) {
+                            def associationName = association.getName()
+                            def propertyName = association.name
+                            println "to-many -> $propertyName"
+                        }
+                    }
+                }
+            '''
+
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources(a, b, c, d, e)
+            cu.compile()
+
+            loader.loadClass('Main').main()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+        }
+    }
+}