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/03/06 17:44:49 UTC

[groovy] branch GROOVY_3_0_X updated: GROOVY-7490, GROOVY-8389, GROOVY-10329: import static callable property

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


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new 054c762947 GROOVY-7490, GROOVY-8389, GROOVY-10329: import static callable property
054c762947 is described below

commit 054c7629478067dd886bebb15c46867cba52d60f
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Mar 6 11:25:21 2023 -0600

    GROOVY-7490, GROOVY-8389, GROOVY-10329: import static callable property
    
    3_0_X backport
---
 .../org/codehaus/groovy/classgen/Verifier.java     |  18 +-
 .../classgen/asm/sc/StaticInvocationWriter.java    |  11 +-
 .../groovy/control/StaticImportVisitor.java        | 236 +++++++++++----------
 src/test/Outer3.groovy                             |  22 --
 src/test/Outer4.groovy                             |  22 --
 src/test/groovy/StaticImportTest.groovy            |  44 ++++
 src/test/groovy/bugs/Groovy4145.groovy             |  25 ---
 src/test/groovy/bugs/Groovy8389Bug.groovy          | 111 ----------
 8 files changed, 189 insertions(+), 300 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 76dfebb5fc..9b8d6e5153 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -103,7 +103,6 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
@@ -354,7 +353,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
 
         FieldNode staticMetaClassField = node.addField(staticMetaClassFieldName, ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, ClassHelper.make(ClassInfo.class, false), null);
         staticMetaClassField.setSynthetic(true);
-
+        ClassNode classNode = this.classNode;
         node.addSyntheticMethod(
                 "$getStaticMetaClass",
                 ACC_PROTECTED,
@@ -661,7 +660,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
         FieldNode field = node.getField();
 
         String getterName = "get" + capitalize(name);
-        String setterName = getSetterName(name);
+        String setterName = "set" + capitalize(name);
 
         int accessorModifiers = adjustPropertyModifiersForMethod(node);
 
@@ -1189,14 +1188,15 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
     }
 
     protected Statement createGetterBlock(final PropertyNode propertyNode, final FieldNode field) {
+        String owner = BytecodeHelper.getClassInternalName(classNode);
         return new BytecodeSequence(new BytecodeInstruction() {
             @Override
             public void visit(final MethodVisitor mv) {
                 if (field.isStatic()) {
-                    mv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
+                    mv.visitFieldInsn(GETSTATIC, owner, field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
                 } else {
                     mv.visitVarInsn(ALOAD, 0);
-                    mv.visitFieldInsn(GETFIELD, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
+                    mv.visitFieldInsn(GETFIELD, owner, field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
                 }
                 BytecodeHelper.doReturn(mv, field.getType());
             }
@@ -1204,16 +1204,17 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
     }
 
     protected Statement createSetterBlock(final PropertyNode propertyNode, final FieldNode field) {
+        String owner = BytecodeHelper.getClassInternalName(classNode);
         return new BytecodeSequence(new BytecodeInstruction() {
             @Override
             public void visit(final MethodVisitor mv) {
                 if (field.isStatic()) {
                     BytecodeHelper.load(mv, field.getType(), 0);
-                    mv.visitFieldInsn(PUTSTATIC, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
+                    mv.visitFieldInsn(PUTSTATIC, owner, field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
                 } else {
                     mv.visitVarInsn(ALOAD, 0);
                     BytecodeHelper.load(mv, field.getType(), 1);
-                    mv.visitFieldInsn(PUTFIELD, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
+                    mv.visitFieldInsn(PUTFIELD, owner, field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
                 }
                 mv.visitInsn(RETURN);
             }
@@ -1399,6 +1400,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
 
         // if we reach this point there is least one parameter or return type
         // that is different in its specified form, so create a bridge method
+        String owner = BytecodeHelper.getClassInternalName(classNode);
         return new MethodNode(
                 oldMethod.getName(),
                 overridingMethod.getModifiers() | ACC_SYNTHETIC | ACC_BRIDGE,
@@ -1423,7 +1425,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
                                 BytecodeHelper.doCast(mv, goal[i].getType());
                             }
                         }
-                        mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(classNode), overridingMethod.getName(), BytecodeHelper.getMethodDescriptor(nmr, overridingMethod.getParameters()), false);
+                        mv.visitMethodInsn(INVOKEVIRTUAL, owner, overridingMethod.getName(), BytecodeHelper.getMethodDescriptor(nmr, overridingMethod.getParameters()), false);
 
                         BytecodeHelper.doReturn(mv, oldMethod.getReturnType());
                     }
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
index 6b7cb08bd3..3bdf965890 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
@@ -21,6 +21,7 @@ package org.codehaus.groovy.classgen.asm.sc;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.EnumConstantClassNode;
 import org.codehaus.groovy.ast.FieldNode;
 import org.codehaus.groovy.ast.GroovyCodeVisitor;
 import org.codehaus.groovy.ast.InnerClassNode;
@@ -482,7 +483,7 @@ public class StaticInvocationWriter extends InvocationWriter {
                 } else {
                     controller.getSourceUnit().addFatalError("Binding failed" +
                             " for arguments [" + argumentList.stream().map(arg -> typeChooser.resolveType(arg, classNode).toString(false)).collect(Collectors.joining(", ")) + "]" +
-                            " and parameters [" + Arrays.stream(parameters).map(prm -> prm.getType().toString(false)).collect(Collectors.joining(", ")) + "]", currentCall);
+                            " and parameters [" + Arrays.stream(parameters).map(prm -> prm.getType().toString(false)).collect(Collectors.joining(", ")) + "]", getCurrentCall());
                 }
             }
             for (int i = 0; i < nArgs; i += 1) {
@@ -736,9 +737,11 @@ public class StaticInvocationWriter extends InvocationWriter {
                         type = ClassHelper.getWrapper(type);
                     }
                     ClassNode declaringClass = target.getDeclaringClass();
-                    if (type.getClass() != ClassNode.class
-                            && type.getClass() != InnerClassNode.class
-                            && type.getClass() != DecompiledClassNode.class) {
+                    Class<?>  typeClass = type.getClass();
+                    if (typeClass != ClassNode.class
+                            && typeClass != InnerClassNode.class
+                            && typeClass != DecompiledClassNode.class
+                            && typeClass != EnumConstantClassNode.class) {
                         type = declaringClass; // ex: LUB type
                     }
                     if (ClassHelper.OBJECT_TYPE.equals(declaringClass)) {
diff --git a/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java b/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
index c47311a5fe..18436ed427 100644
--- a/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
@@ -64,7 +64,6 @@ import static org.apache.groovy.ast.tools.ExpressionUtils.isThisOrSuper;
 import static org.apache.groovy.ast.tools.ExpressionUtils.transformInlineConstants;
 import static org.apache.groovy.util.BeanUtils.capitalize;
 import static org.codehaus.groovy.ast.tools.ClosureUtils.getParametersSafe;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName;
 
 /**
  * Visitor to resolve constants and method calls from static imports.
@@ -224,7 +223,7 @@ public class StaticImportVisitor extends ClassCodeExpressionTransformer {
     protected Expression transformVariableExpression(VariableExpression ve) {
         Variable v = ve.getAccessedVariable();
         if (v instanceof DynamicVariable) {
-            Expression result = findStaticFieldOrPropAccessorImportFromModule(v.getName());
+            Expression result = findStaticFieldOrPropertyAccessorImportFromModule(v.getName());
             if (result != null) {
                 setSourcePosition(result, ve);
                 if (inAnnotation) {
@@ -260,19 +259,9 @@ public class StaticImportVisitor extends ClassCodeExpressionTransformer {
             if (!thisOrSuperMethod && currentClass.getOuterClasses().stream().noneMatch(oc -> oc.tryFindPossibleMethod(name, args) != null)) { // GROOVY-5239
                 Expression result = findStaticMethodImportFromModule(method, args);
                 if (result != null) {
-                    result.setSourcePosition(mce);
+                    setSourcePosition(result, mce);
                     return result;
                 }
-                if (name != null && !inLeftExpression) { // maybe a closure field
-                    result = findStaticFieldOrPropAccessorImportFromModule(name);
-                    if (result != null) {
-                        setSourcePosition(result, method);
-                        result = new MethodCallExpression(result, "call", args);
-                        ((MethodCallExpression) result).setImplicitThis(false);
-                        result.setSourcePosition(mce);
-                        return result;
-                    }
-                }
             }
         } else if (staticWrtCurrent && isSuperExpression(object)) {
             Expression result = new MethodCallExpression(new ClassExpression(currentClass.getSuperClass()), method, args);
@@ -401,117 +390,127 @@ public class StaticImportVisitor extends ClassCodeExpressionTransformer {
         return pe;
     }
 
-    private Expression findStaticFieldOrPropAccessorImportFromModule(String name) {
-        ModuleNode module = currentClass.getModule();
-        if (module == null) return null;
-        Map<String, ImportNode> importNodes = module.getStaticImports();
-        Expression expression;
-        String accessorName = getAccessorName(name);
+    //--------------------------------------------------------------------------
+
+    private Expression findStaticFieldOrPropertyAccessorImportFromModule(String name) {
+        ModuleNode module = currentClass.getModule(); if (module == null) return null;
+        Map<String, ImportNode> staticImports = module.getStaticImports();
+
         // look for one of these:
         //   import static MyClass.setProp [as setOtherProp]
         //   import static MyClass.getProp [as getOtherProp]
-        // when resolving prop reference
-        if (importNodes.containsKey(accessorName)) {
-            expression = findStaticProperty(importNodes, accessorName);
+        //   import static MyClass.isProp [as isOtherProp]
+        // when resolving property reference
+        Expression expression;
+        expression = findStaticProperty(staticImports, getAccessorName(name));
+        if (expression != null) return expression;
+        if (!inLeftExpression) {
+            expression = findStaticProperty(staticImports, "is" + capitalize(name));
             if (expression != null) return expression;
         }
-        if (accessorName.startsWith("get")) {
-            accessorName = "is" + accessorName.substring(3);
-            if (importNodes.containsKey(accessorName)) {
-                expression = findStaticProperty(importNodes, accessorName);
-                if (expression != null) return expression;
-            }
-        }
 
         // look for one of these:
         //   import static MyClass.prop [as otherProp]
-        // when resolving prop or field reference
-        if (importNodes.containsKey(name)) {
-            ImportNode importNode = importNodes.get(name);
-            expression = findStaticPropertyAccessor(importNode.getType(), importNode.getFieldName());
-            if (expression != null) return expression;
-            expression = findStaticField(importNode.getType(), importNode.getFieldName());
+        // when resolving property or field reference
+        if (staticImports.containsKey(name)) { ImportNode importNode = staticImports.get(name);
+            expression = findStaticPropertyOrField(importNode.getType(), importNode.getFieldName());
             if (expression != null) return expression;
         }
+
         // look for one of these:
         //   import static MyClass.*
-        // when resolving prop or field reference
+        // when resolving property or field reference
         for (ImportNode importNode : module.getStaticStarImports().values()) {
-            ClassNode node = importNode.getType();
-            expression = findStaticPropertyAccessor(node, name);
-            if (expression != null) return expression;
-            expression = findStaticField(node, name);
+            expression = findStaticPropertyOrField(importNode.getType(), name);
             if (expression != null) return expression;
         }
-        return null;
-    }
 
-    private Expression findStaticProperty(Map<String, ImportNode> importNodes, String accessorName) {
-        Expression result = null;
-        ImportNode importNode = importNodes.get(accessorName);
-        ClassNode importClass = importNode.getType();
-        String importMember = importNode.getFieldName();
-        result = findStaticPropertyAccessorByFullName(importClass, importMember);
-        if (result == null) {
-            result = findStaticPropertyAccessor(importClass, getPropNameForAccessor(importMember));
-        }
-        return result;
+        return null;
     }
 
     private Expression findStaticMethodImportFromModule(Expression method, Expression args) {
-        ModuleNode module = currentClass.getModule();
-        if (module == null || !(method instanceof ConstantExpression)) return null;
-        Map<String, ImportNode> importNodes = module.getStaticImports();
-        ConstantExpression ce = (ConstantExpression) method;
+        if (currentClass.getModule() == null) return null;
+        if (!(method instanceof ConstantExpression)) return null;
+        if (!(((ConstantExpression) method).getValue() instanceof String)) return null;
+
         Expression expression;
-        Object value = ce.getValue();
-        // skip non-Strings, e.g. Integer
-        if (!(value instanceof String)) return null;
-        final String name = (String) value;
+        String name = method.getText();
+        Map<String, ImportNode> staticImports = currentClass.getModule().getStaticImports();
         // look for one of these:
-        //   import static SomeClass.method [as otherName]
-        // when resolving methodCall() or getProp() or setProp()
-        if (importNodes.containsKey(name)) {
-            ImportNode importNode = importNodes.get(name);
+        //   import static MyClass.field [as alias]
+        //   import static MyClass.method [as alias]
+        //   import static MyClass.property [as alias]
+        // when resolving implicit-this call name(args)
+        if (staticImports.containsKey(name)) {
+            ImportNode importNode = staticImports.get(name);
             expression = findStaticMethod(importNode.getType(), importNode.getFieldName(), args);
-            if (expression != null) return expression;
-            expression = findStaticPropertyAccessorGivenArgs(importNode.getType(), getPropNameForAccessor(importNode.getFieldName()), args);
             if (expression != null) {
-                return newStaticMethodCallX(importNode.getType(), importNode.getFieldName(), args);
+                return expression;
+            }
+            if (!inClosure && !inLeftExpression) {
+                expression = findStaticPropertyOrField(importNode.getType(), importNode.getFieldName());
+                if (expression != null) { // assume name refers to a callable static field/property
+                    MethodCallExpression call = new MethodCallExpression(expression, "call", args);
+                    call.setImplicitThis(false);
+                    return call;
+                }
             }
         }
         // look for one of these:
-        //   import static SomeClass.someProp [as otherName]
-        // when resolving getProp() or setProp()
-        if (isValidAccessorName(name)) {
-            String propName = getPropNameForAccessor(name);
-            if (importNodes.containsKey(propName)) {
-                ImportNode importNode = importNodes.get(propName);
-                ClassNode importClass = importNode.getType();
+        //   import static MyClass.property [as alias]
+        //   import static MyClass.setProperty [as alias]
+        //   import static MyClass.getProperty [as alias]
+        //   import static MyClass.isProperty  [as alias]
+        // when resolving isName(), getName() or setName(args)
+        boolean accessor = isValidAccessorName(name);
+        if (accessor) {
+            ImportNode importNode = staticImports.get(name);
+            if (importNode != null) {
+                String propName = getPropNameForAccessor(importNode.getFieldName());
+                expression = findStaticPropertyAccessorGivenArgs(importNode.getType(), propName, args);
+                if (expression != null) { // expression may refer to getter or setter, so make new call
+                    return newStaticMethodCallX(importNode.getType(), importNode.getFieldName(), args);
+                }
+            }
+            importNode = staticImports.get(getPropNameForAccessor(name));
+            if (importNode != null) {
+                ClassNode importType = importNode.getType();
                 String importMember = importNode.getFieldName();
-                expression = findStaticMethod(importClass, prefix(name) + capitalize(importMember), args);
+                expression = findStaticMethod(importType, prefix(name) + capitalize(importMember), args);
                 if (expression != null) return expression;
-                expression = findStaticPropertyAccessorGivenArgs(importClass, importMember, args);
+                expression = findStaticPropertyAccessorGivenArgs(importType, importMember, args);
                 if (expression != null) {
-                    return newStaticMethodCallX(importClass, prefix(name) + capitalize(importMember), args);
+                    return newStaticMethodCallX(importType, prefix(name) + capitalize(importMember), args);
                 }
             }
         }
-        Map<String, ImportNode> starImports = module.getStaticStarImports();
-        ClassNode starImportType;
-        if (currentClass.isEnum() && starImports.containsKey(currentClass.getName())) {
-            ImportNode importNode = starImports.get(currentClass.getName());
-            starImportType = importNode == null ? null : importNode.getType();
-            expression = findStaticMethod(starImportType, name, args);
+
+        Map<String, ImportNode> staticStarImports = currentClass.getModule().getStaticStarImports();
+        if (currentClass.isEnum() && staticStarImports.containsKey(currentClass.getName())) {
+            ImportNode importNode = staticStarImports.get(currentClass.getName());
+            expression = findStaticMethod(importNode.getType(), name, args);
             return expression;
-        } else {
-            for (ImportNode importNode : starImports.values()) {
-                starImportType = importNode == null ? null : importNode.getType();
-                expression = findStaticMethod(starImportType, name, args);
-                if (expression != null) return expression;
-                expression = findStaticPropertyAccessorGivenArgs(starImportType, getPropNameForAccessor(name), args);
-                if (expression != null) {
-                    return newStaticMethodCallX(starImportType, name, args);
+        }
+        // look for one of these:
+        //   import static MyClass.*
+        // when resolving name(args), getName(), etc.
+        for (ImportNode importNode : staticStarImports.values()) {
+            ClassNode importType = importNode.getType();
+            expression = findStaticMethod(importType, name, args);
+            if (expression != null) return expression;
+            if (!inClosure && !inLeftExpression) { // GROOVY-10329
+                expression = findStaticPropertyOrField(importType, name);
+                if (expression != null) { // assume name refers to a callable static field/property
+                    MethodCallExpression call = new MethodCallExpression(expression, "call", args);
+                    call.setImplicitThis(false);
+                    return call;
+                }
+            }
+            if (accessor) {
+                String propName = getPropNameForAccessor(name);
+                expression = findStaticPropertyAccessorGivenArgs(importType, propName, args);
+                if (expression != null) { // expression may refer to getter or setter, so ...
+                    return newStaticMethodCallX(importType, name, args);
                 }
             }
         }
@@ -523,44 +522,65 @@ public class StaticImportVisitor extends ClassCodeExpressionTransformer {
     }
 
     private String getAccessorName(String name) {
-        return inLeftExpression ? getSetterName(name) : "get" + capitalize(name);
+        return (inLeftExpression ? "set" : "get") + capitalize(name);
+    }
+
+    private Expression findStaticPropertyAccessorByFullName(ClassNode staticImportType, String accessorName) {
+        Expression argumentList = inLeftExpression ? new ArgumentListExpression(EmptyExpression.INSTANCE) : ArgumentListExpression.EMPTY_ARGUMENTS;
+        Expression accessorExpr = findStaticMethod(staticImportType, accessorName, argumentList);
+        // TODO: GROOVY-9382, GROOVY-10133
+        return accessorExpr;
     }
 
     private Expression findStaticPropertyAccessorGivenArgs(ClassNode staticImportType, String propName, Expression args) {
-        // TODO validate args?
-        return findStaticPropertyAccessor(staticImportType, propName);
+        return findStaticPropertyAccessor(staticImportType, propName); // TODO: validate args?
     }
 
     private Expression findStaticPropertyAccessor(ClassNode staticImportType, String propName) {
         String accessorName = getAccessorName(propName);
         Expression accessor = findStaticPropertyAccessorByFullName(staticImportType, accessorName);
-        if (accessor == null && accessorName.startsWith("get")) {
+        if (accessor == null && !inLeftExpression) {
             accessor = findStaticPropertyAccessorByFullName(staticImportType, "is" + accessorName.substring(3));
         }
         if (accessor == null && hasStaticProperty(staticImportType, propName)) {
-            // args will be replaced
             if (inLeftExpression)
-                accessor = newStaticMethodCallX(staticImportType, accessorName, ArgumentListExpression.EMPTY_ARGUMENTS);
+                accessor = newStaticMethodCallX(staticImportType, accessorName, ArgumentListExpression.EMPTY_ARGUMENTS); // <-- will be replaced
             else
                 accessor = newStaticPropertyX(staticImportType, propName);
         }
         return accessor;
     }
 
-    private Expression findStaticPropertyAccessorByFullName(ClassNode staticImportType, String accessorMethodName) {
-        // anything will do as we only check size == 1
-        ArgumentListExpression dummyArgs = new ArgumentListExpression();
-        dummyArgs.addExpression(EmptyExpression.INSTANCE);
-        return findStaticMethod(staticImportType, accessorMethodName, (inLeftExpression ? dummyArgs : ArgumentListExpression.EMPTY_ARGUMENTS));
+    private Expression findStaticPropertyOrField(ClassNode staticImportType, String variableName) {
+        Expression expression = findStaticPropertyAccessor(staticImportType, variableName);
+        if (expression == null) {
+            if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) {
+                FieldNode field = getField(staticImportType, variableName);
+                if (field != null && field.isStatic())
+                    expression = newStaticPropertyX(staticImportType, variableName);
+            }
+        }
+        return expression;
     }
 
-    private static Expression findStaticField(ClassNode staticImportType, String fieldName) {
-        if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) {
-            FieldNode field = getField(staticImportType, fieldName);
-            if (field != null && field.isStatic())
-                return newStaticPropertyX(staticImportType, fieldName);
+    private Expression findStaticProperty(Map<String, ImportNode> staticImports, String accessorName) {
+        Expression expression = null;
+        ImportNode importNode = staticImports.get(accessorName);
+        if (importNode != null) { ClassNode importType = importNode.getType();
+            expression = findStaticPropertyAccessorByFullName(importType, importNode.getFieldName());
+            if (expression == null) { // perhaps the property accessor will be generated
+                String propertyName = getPropNameForAccessor(importNode.getFieldName());
+                if (hasStaticProperty(importType, propertyName)) {
+                    if (inLeftExpression) {
+                        expression = newStaticMethodCallX(importType, importNode.getFieldName(),
+                                ArgumentListExpression.EMPTY_ARGUMENTS); // <-- will be replaced
+                    } else {
+                        expression = newStaticPropertyX(importType, propertyName);
+                    }
+                }
+            }
         }
-        return null;
+        return expression;
     }
 
     private static Expression findStaticMethod(ClassNode staticImportType, String methodName, Expression args) {
diff --git a/src/test/Outer3.groovy b/src/test/Outer3.groovy
deleted file mode 100644
index ee2ffa6ccf..0000000000
--- a/src/test/Outer3.groovy
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- *  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.
- */
-class Outer3 {
-    // test class with no package and having a static inner class
-    static class Inner3 {}
-}
diff --git a/src/test/Outer4.groovy b/src/test/Outer4.groovy
deleted file mode 100644
index 52385dbdc3..0000000000
--- a/src/test/Outer4.groovy
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- *  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.
- */
-class Outer4 {
-    // test class with no package and having a static inner class
-    static class Inner4 {}
-}
diff --git a/src/test/groovy/StaticImportTest.groovy b/src/test/groovy/StaticImportTest.groovy
index f04699f545..409aa93090 100644
--- a/src/test/groovy/StaticImportTest.groovy
+++ b/src/test/groovy/StaticImportTest.groovy
@@ -318,6 +318,27 @@ final class StaticImportTest extends groovy.test.GroovyTestCase {
         }
     }
 
+    // GROOVY-8389, GROOVY-10329
+    void testStaticImportPropertyWithClosure() {
+        for (imports in ['import static Foo.bar; import static Foo.baz', 'import static Foo.*']) {
+            assertScript """$imports
+                class Foo {
+                    static Closure<String> bar = { -> 'property' }
+                    static Closure<String> baz = { -> 'property' }
+                }
+                String bar() {
+                    'method'
+                }
+                @groovy.transform.CompileStatic
+                def test() {
+                    bar() + ':' + baz()
+                }
+                String result = test()
+                assert result == 'method:property'
+            """
+        }
+    }
+
     // GROOVY-8389
     void testStaticImportMethodVsLocalMethod() {
         assertScript '''
@@ -423,6 +444,29 @@ final class StaticImportTest extends groovy.test.GroovyTestCase {
         }
     }
 
+    // GROOVY-7490, GROOVY-10329
+    void testStaticImportOfCallableProperty() {
+        for (imports in ['import static Pogo.callable_property', 'import static Pogo.*']) {
+            assertScript """$imports
+                class WithCall {
+                    String call(String input) {
+                        return input
+                    }
+                }
+                class Pogo {
+                    static final WithCall callable_property = new WithCall()
+                }
+
+                @groovy.transform.CompileStatic
+                def usage() {
+                    callable_property('works')
+                }
+                String result = usage()
+                assert result == 'works'
+            """
+        }
+    }
+
     // GROOVY-4145
     void testStaticPropertyImportedImplementedAsGetter() {
         assertScript '''
diff --git a/src/test/groovy/bugs/Groovy4145.groovy b/src/test/groovy/bugs/Groovy4145.groovy
deleted file mode 100644
index d45349055d..0000000000
--- a/src/test/groovy/bugs/Groovy4145.groovy
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- *  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
-
-class Groovy4145 {
-    static getFoo4145() {
-        return 3
-    } 
-}
\ No newline at end of file
diff --git a/src/test/groovy/bugs/Groovy8389Bug.groovy b/src/test/groovy/bugs/Groovy8389Bug.groovy
deleted file mode 100644
index 5443b27c0c..0000000000
--- a/src/test/groovy/bugs/Groovy8389Bug.groovy
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- *  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 groovy.test.GroovyTestCase
-
-class Groovy8389Bug extends GroovyTestCase {
-    void testLocalMethodInvoked() {
-        assertScript '''
-            import static A.bar
-            
-            class A {
-              static bar = "A"
-            }
-            
-            def bar() {
-              "bar"
-            }
-            
-            @groovy.transform.CompileStatic
-            def usage() {
-              bar()
-            }
-            
-            assert "bar" == usage() 
-        '''
-    }
-
-    void testLocalMethodInvoked2() {
-        assertScript '''
-            import static A.bar
-            
-            class A {
-              static bar = "A"
-            }
-            
-            static bar() {
-              "bar"
-            }
-            
-            @groovy.transform.CompileStatic
-            def usage() {
-              bar()
-            }
-            
-            assert "bar" == usage() 
-        '''
-    }
-
-    void testLocalMethodInvoked3() {
-        assertScript '''
-            import static A.baz
-            import static A.bar
-            
-            class A {
-              static bar() { "Abar" }
-              static baz() { "Abaz" }
-            }
-            
-            def bar() {
-              "bar"
-            }
-            
-            @groovy.transform.CompileStatic
-            def usage() {
-              "${bar()}${baz()}"
-            }
-            
-            assert usage() == 'barAbaz'
-        '''
-    }
-
-    void testLocalMethodInvoked4() {
-        assertScript '''
-            import static A.baz
-            import static A.bar
-            
-            class A {
-              static bar() { "Abar" }
-              static baz() { "Abaz" }
-            }
-            
-            static bar() {
-              "bar"
-            }
-            
-            @groovy.transform.CompileStatic
-            def usage() {
-              "${bar()}${baz()}"
-            }
-            
-            assert usage() == 'barAbaz'
-        '''
-    }
-}