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()
+ }
+ }
+}