You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2019/09/04 11:52:57 UTC

[groovy] branch master updated: GROOVY-9243: Fail to resolve nested type defined in base type written in Groovy

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 89c4dfa  GROOVY-9243: Fail to resolve nested type defined in base type written in Groovy
89c4dfa is described below

commit 89c4dfa95777fb40af362730f12a81f9daf863b8
Author: Daniel Sun <su...@apache.org>
AuthorDate: Tue Sep 3 00:15:16 2019 +0800

    GROOVY-9243: Fail to resolve nested type defined in base type written in Groovy
---
 .../java/org/codehaus/groovy/ast/CompileUnit.java  | 16 +++++-
 .../codehaus/groovy/control/ResolveVisitor.java    | 67 +++++++++++++++++-----
 .../groovy/bugs/groovy9243/Base.groovy             | 29 ++++++++++
 .../groovy/bugs/groovy9243/Main.groovy             | 49 ++++++++++++++++
 src/test/groovy/bugs/Groovy9243Bug.groovy          | 32 +++++++++++
 5 files changed, 178 insertions(+), 15 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/CompileUnit.java b/src/main/java/org/codehaus/groovy/ast/CompileUnit.java
index 79f76f6..656cc23 100644
--- a/src/main/java/org/codehaus/groovy/ast/CompileUnit.java
+++ b/src/main/java/org/codehaus/groovy/ast/CompileUnit.java
@@ -34,6 +34,7 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.BiConsumer;
 
 /**
  * Represents the entire contents of a compilation step which consists of one or more
@@ -186,7 +187,7 @@ public class CompileUnit implements NodeMetaDataHandler {
     public InnerClassNode getGeneratedInnerClass(String name) {
         return generatedInnerClasses.get(name);
     }
-    
+
     public void addGeneratedInnerClass(InnerClassNode icn) {
         generatedInnerClasses.put(icn.getName(), icn);
     }
@@ -228,6 +229,7 @@ public class CompileUnit implements NodeMetaDataHandler {
     @Internal
     public static class ConstructedOuterNestedClassNode extends ClassNode {
         private final ClassNode enclosingClassNode;
+        private final List<BiConsumer<ConstructedOuterNestedClassNode, ClassNode>> setRedirectListenerList = new ArrayList<>();
 
         public ConstructedOuterNestedClassNode(ClassNode outer, String innerClassName) {
             super(innerClassName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
@@ -235,8 +237,20 @@ public class CompileUnit implements NodeMetaDataHandler {
             this.isPrimaryNode = false;
         }
 
+        @Override
+        public void setRedirect(ClassNode cn) {
+            for (BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener : setRedirectListenerList) {
+                setRedirectListener.accept(this, cn);
+            }
+            super.setRedirect(cn);
+        }
+
         public ClassNode getEnclosingClassNode() {
             return enclosingClassNode;
         }
+
+        public void addSetRedirectListener(BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener) {
+            setRedirectListenerList.add(setRedirectListener);
+        }
     }
 }
diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index 9f6cf24..958a233 100644
--- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
@@ -78,6 +78,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.BiConsumer;
 
 import static org.codehaus.groovy.ast.CompileUnit.ConstructedOuterNestedClassNode;
 import static org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
@@ -354,10 +355,12 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
     // if the type to resolve is an inner class and it is in an outer class which is not resolved,
     // we set the resolved type to a placeholder class node, i.e. a ConstructedOuterNestedClass instance
     // when resolving the outer class later, we set the resolved type of ConstructedOuterNestedClass instance to the actual inner class node(SEE GROOVY-7812(#2))
-    private boolean resolveToOuterNested(ClassNode type) {
+    private boolean resolveToOuterNested(final ClassNode type) {
         CompileUnit compileUnit = currentClass.getCompileUnit();
         String typeName = type.getName();
 
+        BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener = (s, c) -> type.setRedirect(s);
+
         ModuleNode module = currentClass.getModule();
         for (ImportNode importNode : module.getStaticImports().values()) {
             String importFieldName = importNode.getFieldName();
@@ -365,7 +368,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
 
             if (!typeName.equals(importAlias)) continue;
 
-            ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNodeViaStaticImport(compileUnit, importNode, importFieldName);
+            ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNodeViaStaticImport(compileUnit, importNode, importFieldName, setRedirectListener);
             if (null != constructedOuterNestedClassNode) {
                 compileUnit.addClassNodeToResolve(constructedOuterNestedClassNode);
                 return true;
@@ -374,7 +377,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
 
         for (Map.Entry<String, ClassNode> entry : compileUnit.getClassesToCompile().entrySet()) {
             ClassNode outerClassNode = entry.getValue();
-            ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNode(type, outerClassNode);
+            ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNode(type, outerClassNode, setRedirectListener);
             if (null != constructedOuterNestedClassNode) {
                 compileUnit.addClassNodeToResolve(constructedOuterNestedClassNode);
                 return true;
@@ -383,40 +386,71 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
 
         boolean toResolveFurther = false;
         for (ImportNode importNode : module.getStaticStarImports().values()) {
-            ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNodeViaStaticImport(compileUnit, importNode, typeName);
+            ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNodeViaStaticImport(compileUnit, importNode, typeName, setRedirectListener);
             if (null != constructedOuterNestedClassNode) {
                 compileUnit.addClassNodeToResolve(constructedOuterNestedClassNode);
                 toResolveFurther = true; // do not return here to try all static star imports because currently we do not know which outer class the class to resolve is declared in.
             }
         }
+        if (toResolveFurther) return true;
+
+        // GROOVY-9243
+        toResolveFurther = false;
+        if (!whetherClassNameContainsDot(typeName)) {
+            Map<String, ClassNode> hierClasses = findHierClasses(currentClass);
+            for (ClassNode cn : hierClasses.values()) {
+                ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNodeForBaseType(compileUnit, typeName, cn, setRedirectListener);
+                if (null != constructedOuterNestedClassNode) {
+                    compileUnit.addClassNodeToResolve(constructedOuterNestedClassNode);
+                    toResolveFurther = true;
+                }
+            }
+        }
 
         return toResolveFurther;
     }
 
-    private ConstructedOuterNestedClassNode tryToConstructOuterNestedClassNodeViaStaticImport(CompileUnit compileUnit, ImportNode importNode, String typeName) {
+    private static boolean whetherClassNameContainsDot(String typeName) {
+        return typeName.contains(".");
+    }
+
+    private ConstructedOuterNestedClassNode tryToConstructOuterNestedClassNodeViaStaticImport(CompileUnit compileUnit, ImportNode importNode, String typeName, BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener) {
         String importClassName = importNode.getClassName();
         ClassNode outerClassNode = compileUnit.getClass(importClassName);
 
         if (null == outerClassNode) return null;
 
         String outerNestedClassName = importClassName + "$" + typeName.replace(".", "$");
-        return new ConstructedOuterNestedClassNode(outerClassNode, outerNestedClassName);
+        ConstructedOuterNestedClassNode constructedOuterNestedClassNode = new ConstructedOuterNestedClassNode(outerClassNode, outerNestedClassName);
+        constructedOuterNestedClassNode.addSetRedirectListener(setRedirectListener);
+        return constructedOuterNestedClassNode;
     }
 
-    private ConstructedOuterNestedClassNode tryToConstructOuterNestedClassNode(ClassNode type, ClassNode outerClassNode) {
+    private ConstructedOuterNestedClassNode tryToConstructOuterNestedClassNode(final ClassNode type, ClassNode outerClassNode, BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener) {
         String outerClassName = outerClassNode.getName();
 
         for (String typeName = type.getName(), ident = typeName; ident.contains("."); ) {
             ident = ident.substring(0, ident.lastIndexOf("."));
             if (outerClassName.endsWith(ident)) {
                 String outerNestedClassName = outerClassName + typeName.substring(ident.length()).replace(".", "$");
-                return new ConstructedOuterNestedClassNode(outerClassNode, outerNestedClassName);
+                ConstructedOuterNestedClassNode constructedOuterNestedClassNode = new ConstructedOuterNestedClassNode(outerClassNode, outerNestedClassName);
+                constructedOuterNestedClassNode.addSetRedirectListener(setRedirectListener);
+                return constructedOuterNestedClassNode;
             }
         }
 
         return null;
     }
 
+    private ConstructedOuterNestedClassNode tryToConstructOuterNestedClassNodeForBaseType(CompileUnit compileUnit, String typeName, ClassNode cn, BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener) {
+        if (!compileUnit.getClassesToCompile().containsValue(cn)) return null;
+
+        String outerNestedClassName = cn.getName() + "$" + typeName;
+        ConstructedOuterNestedClassNode constructedOuterNestedClassNode = new ConstructedOuterNestedClassNode(cn, outerNestedClassName);
+        constructedOuterNestedClassNode.addSetRedirectListener(setRedirectListener);
+        return constructedOuterNestedClassNode;
+    }
+
     private void resolveOrFail(ClassNode type, ASTNode node, boolean prefereImports) {
         resolveGenericsTypes(type.getGenericsTypes());
         if (prefereImports && resolveAliasFromModule(type)) return;
@@ -477,12 +511,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
         // to access that class directly, so A becomes a valid
         // name in X.
         // GROOVY-4043: Do this check up the hierarchy, if needed
-        Map<String, ClassNode> hierClasses = new LinkedHashMap<String, ClassNode>();
-        for(ClassNode classToCheck = currentClass; classToCheck != ClassHelper.OBJECT_TYPE;
-            classToCheck = classToCheck.getSuperClass()) {
-            if(classToCheck == null || hierClasses.containsKey(classToCheck.getName())) break;
-            hierClasses.put(classToCheck.getName(), classToCheck);
-        }
+        Map<String, ClassNode> hierClasses = findHierClasses(currentClass);
 
         for (ClassNode classToCheck : hierClasses.values()) {
             if (setRedirect(type, classToCheck)) return true;
@@ -1706,4 +1735,14 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
     public void setClassNodeResolver(ClassNodeResolver classNodeResolver) {
         this.classNodeResolver = classNodeResolver;
     }
+
+    private static Map<String, ClassNode> findHierClasses(ClassNode currentClass) {
+        Map<String, ClassNode> hierClasses = new LinkedHashMap<String, ClassNode>();
+        for(ClassNode classToCheck = currentClass; classToCheck != ClassHelper.OBJECT_TYPE;
+            classToCheck = classToCheck.getSuperClass()) {
+            if(classToCheck == null || hierClasses.containsKey(classToCheck.getName())) break;
+            hierClasses.put(classToCheck.getName(), classToCheck);
+        }
+        return hierClasses;
+    }
 }
diff --git a/src/test-resources/groovy/bugs/groovy9243/Base.groovy b/src/test-resources/groovy/bugs/groovy9243/Base.groovy
new file mode 100644
index 0000000..3ee9acb
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovy9243/Base.groovy
@@ -0,0 +1,29 @@
+/*
+ *  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.groovy9243
+
+class Base {
+    static class X {
+        def name = "staticClassX"
+    }
+
+    class Y {
+        def name = "classY"
+    }
+}
diff --git a/src/test-resources/groovy/bugs/groovy9243/Main.groovy b/src/test-resources/groovy/bugs/groovy9243/Main.groovy
new file mode 100644
index 0000000..72b8d47
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovy9243/Main.groovy
@@ -0,0 +1,49 @@
+/*
+ *  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.groovy9243
+
+class Groovy9243 extends Base {
+    def accessX() {
+        assert 'staticClassX' == new X().name
+    }
+    def accessBaseX() {
+        assert 'staticClassX' == new Base.X().name
+    }
+    def accessBaseX2() {
+        assert 'staticClassX' == new groovy.bugs.groovy9243.Base.X().name
+    }
+    def accessY() {
+        assert 'classY' == this.new Y().name
+    }
+    def accessY2() {
+        def g = new Groovy9243()
+        assert 'classY' == g.new Y().name
+    }
+    def accessY3() {
+        assert 'classY' == new Groovy9243().new Y().name
+    }
+}
+
+def g = new Groovy9243()
+g.accessX()
+g.accessBaseX()
+g.accessBaseX2()
+g.accessY()
+g.accessY2()
+g.accessY3()
diff --git a/src/test/groovy/bugs/Groovy9243Bug.groovy b/src/test/groovy/bugs/Groovy9243Bug.groovy
new file mode 100644
index 0000000..e0fb10b
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy9243Bug.groovy
@@ -0,0 +1,32 @@
+/*
+ *  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.tools.GroovyStarter
+
+class Groovy9243Bug extends GroovyTestCase {
+    void testResolveNestedClassFromBaseType() {
+        def mainScriptPath = new File(this.getClass().getResource('/groovy/bugs/groovy9243/Main.groovy').toURI()).absolutePath
+        runScript(mainScriptPath)
+    }
+
+    static void runScript(String path) {
+        GroovyStarter.main(new String[] { "--main", "groovy.ui.GroovyMain", path })
+    }
+}