groovy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pa...@apache.org
Subject [groovy] 02/02: GROOVY-9079: STC: NPE in StaticTypeCheckingVisitor.inferSAMTypeGenericsInAssignment
Date Fri, 12 Apr 2019 04:00:59 GMT
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 4244569bb17d841d7df3828af4321a6e5dcc505a
Author: Paul King <paulk@asert.com.au>
AuthorDate: Fri Apr 12 14:00:44 2019 +1000

    GROOVY-9079: STC: NPE in StaticTypeCheckingVisitor.inferSAMTypeGenericsInAssignment
    
    Includes some minor refactoring and fix for @AutoFinal
---
 .../codehaus/groovy/ast/tools/ClosureUtils.java    |   15 +
 .../codehaus/groovy/control/ResolveVisitor.java    |   18 +-
 .../groovy/control/StaticImportVisitor.java        | 1155 ++++++++++----------
 .../transform/AutoFinalASTTransformation.java      |    3 +-
 .../transform/CategoryASTTransformation.java       |   19 +-
 .../transformers/ClosureExpressionTransformer.java |   11 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   |   35 +-
 .../groovy/transform/stc/ClosuresSTCTest.groovy    |   16 +
 .../AutoFinalTransformBlackBoxTest.groovy          |   16 +
 9 files changed, 665 insertions(+), 623 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/ClosureUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/ClosureUtils.java
index 06d4995..da07990 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/ClosureUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/ClosureUtils.java
@@ -19,6 +19,7 @@
 package org.codehaus.groovy.ast.tools;
 
 import groovy.lang.Closure;
+import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.expr.ClosureExpression;
 import org.codehaus.groovy.control.io.ReaderSource;
 /**
@@ -66,4 +67,18 @@ public class ClosureUtils {
         return typeName.equals("java.lang.String");
     }
 
+    /**
+     * @return true if the ClosureExpression has an implicit 'it' parameter
+     */
+    public static boolean hasImplicitParameter(ClosureExpression ce) {
+        return ce.getParameters() != null && ce.getParameters().length == 0;
+    }
+
+    /**
+     * @return the parameters for the ClosureExpression
+     */
+    public static Parameter[] getParametersSafe(ClosureExpression ce) {
+        return ce.getParameters() != null ? ce.getParameters() : Parameter.EMPTY_ARRAY;
+    }
+
 }
diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index ae2e133..aeca559 100644
--- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
@@ -81,6 +81,7 @@ import java.util.Set;
 
 import static org.codehaus.groovy.ast.CompileUnit.ConstructedOuterNestedClassNode;
 import static org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
+import static org.codehaus.groovy.ast.tools.ClosureUtils.getParametersSafe;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.inSamePackage;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.isDefaultVisibility;
 
@@ -1232,17 +1233,14 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
     protected Expression transformClosureExpression(ClosureExpression ce) {
         boolean oldInClosure = inClosure;
         inClosure = true;
-        Parameter[] paras = ce.getParameters();
-        if (paras != null) {
-            for (Parameter para : paras) {
-                ClassNode t = para.getType();
-                resolveOrFail(t, ce);
-                visitAnnotations(para);
-                if (para.hasInitialExpression()) {
-                    para.setInitialExpression(transform(para.getInitialExpression()));
-                }
-                visitAnnotations(para);
+        for (Parameter para : getParametersSafe(ce)) {
+            ClassNode t = para.getType();
+            resolveOrFail(t, ce);
+            visitAnnotations(para);
+            if (para.hasInitialExpression()) {
+                para.setInitialExpression(transform(para.getInitialExpression()));
             }
+            visitAnnotations(para);
         }
 
         Statement code = ce.getCode();
diff --git a/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java b/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
index 3283d2b..b1a1e56 100644
--- a/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
@@ -1,578 +1,577 @@
-/*
- *  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 org.codehaus.groovy.control;
-
-import org.codehaus.groovy.ast.AnnotatedNode;
-import org.codehaus.groovy.ast.AnnotationNode;
-import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
-import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.DynamicVariable;
-import org.codehaus.groovy.ast.FieldNode;
-import org.codehaus.groovy.ast.ImportNode;
-import org.codehaus.groovy.ast.MethodNode;
-import org.codehaus.groovy.ast.ModuleNode;
-import org.codehaus.groovy.ast.Parameter;
-import org.codehaus.groovy.ast.Variable;
-import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;
-import org.codehaus.groovy.ast.expr.ArgumentListExpression;
-import org.codehaus.groovy.ast.expr.BinaryExpression;
-import org.codehaus.groovy.ast.expr.ClassExpression;
-import org.codehaus.groovy.ast.expr.ClosureExpression;
-import org.codehaus.groovy.ast.expr.ConstantExpression;
-import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
-import org.codehaus.groovy.ast.expr.EmptyExpression;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.MapEntryExpression;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
-import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
-import org.codehaus.groovy.ast.expr.PropertyExpression;
-import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
-import org.codehaus.groovy.ast.expr.TupleExpression;
-import org.codehaus.groovy.ast.expr.VariableExpression;
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.syntax.Types;
-
-import java.util.List;
-import java.util.Map;
-
-import static org.apache.groovy.ast.tools.ClassNodeUtils.getPropNameForAccessor;
-import static org.apache.groovy.ast.tools.ClassNodeUtils.hasPossibleStaticMethod;
-import static org.apache.groovy.ast.tools.ClassNodeUtils.hasPossibleStaticProperty;
-import static org.apache.groovy.ast.tools.ClassNodeUtils.hasStaticProperty;
-import static org.apache.groovy.ast.tools.ClassNodeUtils.isInnerClass;
-import static org.apache.groovy.ast.tools.ClassNodeUtils.isValidAccessorName;
-import static org.apache.groovy.ast.tools.ClassNodeUtils.getField;
-import static org.apache.groovy.ast.tools.ExpressionUtils.transformInlineConstants;
-import static org.apache.groovy.util.BeanUtils.capitalize;
-
-/**
- * Visitor to resolve constants and method calls from static Imports
- */
-public class StaticImportVisitor extends ClassCodeExpressionTransformer {
-    private ClassNode currentClass;
-    private MethodNode currentMethod;
-    private SourceUnit source;
-    private boolean inSpecialConstructorCall;
-    private boolean inClosure;
-    private boolean inPropertyExpression;
-    private Expression foundConstant;
-    private Expression foundArgs;
-    private boolean inAnnotation;
-    private boolean inLeftExpression;
-
-    public void visitClass(ClassNode node, SourceUnit source) {
-        this.currentClass = node;
-        this.source = source;
-        super.visitClass(node);
-    }
-
-    @Override
-    protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
-        this.currentMethod = node;
-        super.visitConstructorOrMethod(node, isConstructor);
-        this.currentMethod = null;
-    }
-
-    @Override
-    public void visitAnnotations(AnnotatedNode node) {
-        boolean oldInAnnotation = inAnnotation;
-        inAnnotation = true;
-        super.visitAnnotations(node);
-        inAnnotation = oldInAnnotation;
-    }
-
-    public Expression transform(Expression exp) {
-        if (exp == null) return null;
-        if (exp.getClass() == VariableExpression.class) {
-            return transformVariableExpression((VariableExpression) exp);
-        }
-        if (exp.getClass() == BinaryExpression.class) {
-            return transformBinaryExpression((BinaryExpression) exp);
-        }
-        if (exp.getClass() == PropertyExpression.class) {
-            return transformPropertyExpression((PropertyExpression) exp);
-        }
-        if (exp.getClass() == MethodCallExpression.class) {
-            return transformMethodCallExpression((MethodCallExpression) exp);
-        }
-        if (exp.getClass() == ClosureExpression.class) {
-            return transformClosureExpression((ClosureExpression) exp);
-        }
-        if (exp.getClass() == ConstructorCallExpression.class) {
-            return transformConstructorCallExpression((ConstructorCallExpression) exp);
-        }
-        if (exp.getClass() == ArgumentListExpression.class) {
-            Expression result = exp.transformExpression(this);
-            if (inPropertyExpression) {
-                foundArgs = result;
-            }
-            return result;
-        }
-        if (exp instanceof ConstantExpression) {
-            Expression result = exp.transformExpression(this);
-            if (inPropertyExpression) {
-                foundConstant = result;
-            }
-            if (inAnnotation && exp instanceof AnnotationConstantExpression) {
-                ConstantExpression ce = (ConstantExpression) result;
-                if (ce.getValue() instanceof AnnotationNode) {
-                    // replicate a little bit of AnnotationVisitor here
-                    // because we can't wait until later to do this
-                    AnnotationNode an = (AnnotationNode) ce.getValue();
-                    Map<String, Expression> attributes = an.getMembers();
-                    for (Map.Entry<String, Expression> entry : attributes.entrySet()) {
-                        Expression attrExpr = transform(entry.getValue());
-                        entry.setValue(attrExpr);
-                    }
-
-                }
-            }
-            return result;
-        }
-        return exp.transformExpression(this);
-    }
-
-    // if you have a Bar class with a static foo property, and this:
-    //   import static Bar.foo as baz
-    // then this constructor (not normal usage of statics):
-    //   new Bar(baz:1)
-    // will become:
-    //   new Bar(foo:1)
-
-    private Expression transformMapEntryExpression(MapEntryExpression me, ClassNode constructorCallType) {
-        Expression key = me.getKeyExpression();
-        Expression value = me.getValueExpression();
-        ModuleNode module = currentClass.getModule();
-        if (module != null && key instanceof ConstantExpression) {
-            Map<String, ImportNode> importNodes = module.getStaticImports();
-            if (importNodes.containsKey(key.getText())) {
-                ImportNode importNode = importNodes.get(key.getText());
-                if (importNode.getType().equals(constructorCallType)) {
-                    String newKey = importNode.getFieldName();
-                    return new MapEntryExpression(new ConstantExpression(newKey), value.transformExpression(this));
-                }
-            }
-        }
-        return me;
-    }
-
-    protected Expression transformBinaryExpression(BinaryExpression be) {
-        int type = be.getOperation().getType();
-        boolean oldInLeftExpression;
-        Expression right = transform(be.getRightExpression());
-        be.setRightExpression(right);
-        Expression left;
-        if (type == Types.EQUAL && be.getLeftExpression() instanceof VariableExpression) {
-            oldInLeftExpression = inLeftExpression;
-            inLeftExpression = true;
-            left = transform(be.getLeftExpression());
-            inLeftExpression = oldInLeftExpression;
-            if (left instanceof StaticMethodCallExpression) {
-                StaticMethodCallExpression smce = (StaticMethodCallExpression) left;
-                StaticMethodCallExpression result = new StaticMethodCallExpression(smce.getOwnerType(), smce.getMethod(), right);
-                setSourcePosition(result, be);
-                return result;
-            }
-        } else {
-            left = transform(be.getLeftExpression());
-        }
-        be.setLeftExpression(left);
-        return be;
-    }
-
-    protected Expression transformVariableExpression(VariableExpression ve) {
-        Variable v = ve.getAccessedVariable();
-        if (v instanceof DynamicVariable) {
-            Expression result = findStaticFieldOrPropAccessorImportFromModule(v.getName());
-            if (result != null) {
-                setSourcePosition(result, ve);
-                if (inAnnotation) {
-                    result = transformInlineConstants(result);
-                }
-                return result;
-            }
-        } else if (v instanceof FieldNode) {
-            if (inSpecialConstructorCall) { // GROOVY-8819
-                FieldNode fn = (FieldNode) v;
-                ClassNode declaringClass = fn.getDeclaringClass();
-                if (fn.isStatic() && currentClass.isDerivedFrom(declaringClass)) {
-                    Expression result = new PropertyExpression(new ClassExpression(declaringClass), v.getName());
-                    result.setSourcePosition(ve);
-
-                    return result;
-                }
-            }
-        }
-        return ve;
-    }
-
-    /**
-     * Set the source position of toSet including its property expression if it has one.
-     *
-     * @param toSet resulting node
-     * @param origNode original node
-     */
-    private static void setSourcePosition(Expression toSet, Expression origNode) {
-        toSet.setSourcePosition(origNode);
-        if (toSet instanceof PropertyExpression) {
-            ((PropertyExpression) toSet).getProperty().setSourcePosition(origNode);
-        }
-    }
-
-    protected Expression transformMethodCallExpression(MethodCallExpression mce) {
-        Expression args = transform(mce.getArguments());
-        Expression method = transform(mce.getMethod());
-        Expression object = transform(mce.getObjectExpression());
-        boolean isExplicitThisOrSuper = false;
-        boolean isExplicitSuper = false;
-        if (object instanceof VariableExpression) {
-            VariableExpression ve = (VariableExpression) object;
-            isExplicitThisOrSuper = !mce.isImplicitThis() && (ve.isThisExpression() || ve.isSuperExpression());
-            isExplicitSuper = ve.isSuperExpression();
-        }
-
-        if (mce.isImplicitThis() || isExplicitThisOrSuper) {
-            if (mce.isImplicitThis()) {
-                if (null == currentClass.tryFindPossibleMethod(mce.getMethodAsString(), args)) {
-
-                    Expression ret = findStaticMethodImportFromModule(method, args);
-                    if (ret != null) {
-                        setSourcePosition(ret, mce);
-                        return ret;
-                    }
-                    if (method instanceof ConstantExpression && !inLeftExpression) {
-                        // could be a closure field
-                        String methodName = (String) ((ConstantExpression) method).getValue();
-                        ret = findStaticFieldOrPropAccessorImportFromModule(methodName);
-                        if (ret != null) {
-                            ret = new MethodCallExpression(ret, "call", args);
-                            setSourcePosition(ret, mce);
-                            return ret;
-                        }
-                    }
-                }
-            } else if (currentMethod!=null && currentMethod.isStatic() && isExplicitSuper) {
-                MethodCallExpression ret = new MethodCallExpression(new ClassExpression(currentClass.getSuperClass()), method, args);
-                setSourcePosition(ret, mce);
-                return ret;
-            }
-
-            if (method instanceof ConstantExpression) {
-                ConstantExpression ce = (ConstantExpression) method;
-                Object value = ce.getValue();
-                if (value instanceof String) {
-                    boolean foundInstanceMethod = false;
-                    String methodName = (String) value;
-                    boolean inInnerClass = isInnerClass(currentClass);
-                    if (currentMethod != null && !currentMethod.isStatic()) {
-                        if (currentClass.hasPossibleMethod(methodName, args)) {
-                            foundInstanceMethod = true;
-                        }
-                    }
-                    boolean lookForPossibleStaticMethod = !methodName.equals("call");
-                    lookForPossibleStaticMethod &= !foundInstanceMethod;
-                    lookForPossibleStaticMethod |= inSpecialConstructorCall;
-                    lookForPossibleStaticMethod &= !inInnerClass;
-                    if (!inClosure && lookForPossibleStaticMethod &&
-                            (hasPossibleStaticMethod(currentClass, methodName, args, true))
-                            || hasPossibleStaticProperty(currentClass, methodName)) {
-                        StaticMethodCallExpression smce = new StaticMethodCallExpression(currentClass, methodName, args);
-                        setSourcePosition(smce, mce);
-                        return smce;
-                    }
-                    if (!inClosure && inInnerClass && inSpecialConstructorCall && mce.isImplicitThis() && !foundInstanceMethod) {
-                        if (currentClass.getOuterClass().hasPossibleMethod(methodName, args)) {
-                            object = new PropertyExpression(new ClassExpression(currentClass.getOuterClass()), new ConstantExpression("this"));
-                        } else if (hasPossibleStaticMethod(currentClass.getOuterClass(), methodName, args, true)
-                                || hasPossibleStaticProperty(currentClass.getOuterClass(), methodName)) {
-                            StaticMethodCallExpression smce = new StaticMethodCallExpression(currentClass.getOuterClass(), methodName, args);
-                            setSourcePosition(smce, mce);
-                            return smce;
-                        }
-                    }
-
-                    if (mce.isImplicitThis() && lookForPossibleStaticMethod && hasPossibleStaticMethod(currentClass, methodName, args, true)) {
-                        StaticMethodCallExpression result = new StaticMethodCallExpression(currentClass, methodName, args);
-                        result.setSourcePosition(mce);
-                        return result;
-                    }
-                }
-            }
-        }
-
-        MethodCallExpression result = new MethodCallExpression(object, method, args);
-        result.setSafe(mce.isSafe());
-        result.setImplicitThis(mce.isImplicitThis());
-        result.setSpreadSafe(mce.isSpreadSafe());
-        result.setMethodTarget(mce.getMethodTarget());
-        // GROOVY-6757
-        result.setGenericsTypes(mce.getGenericsTypes());
-        setSourcePosition(result, mce);
-        return result;
-    }
-
-    protected Expression transformConstructorCallExpression(ConstructorCallExpression cce) {
-        inSpecialConstructorCall = cce.isSpecialCall();
-        Expression expression = cce.getArguments();
-        if (expression instanceof TupleExpression) {
-            TupleExpression tuple = (TupleExpression) expression;
-            if (tuple.getExpressions().size() == 1) {
-                expression = tuple.getExpression(0);
-                if (expression instanceof NamedArgumentListExpression) {
-                    NamedArgumentListExpression namedArgs = (NamedArgumentListExpression) expression;
-                    List<MapEntryExpression> entryExpressions = namedArgs.getMapEntryExpressions();
-                    for (int i = 0; i < entryExpressions.size(); i++) {
-                        entryExpressions.set(i, (MapEntryExpression) transformMapEntryExpression(entryExpressions.get(i), cce.getType()));
-                    }
-                }
-            }
-        }
-        Expression ret = cce.transformExpression(this);
-        inSpecialConstructorCall = false;
-        return ret;
-    }
-
-    protected Expression transformClosureExpression(ClosureExpression ce) {
-        boolean oldInClosure = inClosure;
-        inClosure = true;
-        if (ce.getParameters() != null) {
-            for (Parameter p : ce.getParameters()) {
-                if (p.hasInitialExpression()) {
-                    p.setInitialExpression(transform(p.getInitialExpression()));
-                }
-            }
-        }
-        Statement code = ce.getCode();
-        if (code != null) code.visit(this);
-        inClosure = oldInClosure;
-        return ce;
-    }
-
-    protected Expression transformPropertyExpression(PropertyExpression pe) {
-        if (currentMethod!=null && currentMethod.isStatic()
-                && pe.getObjectExpression() instanceof VariableExpression
-                && ((VariableExpression) pe.getObjectExpression()).isSuperExpression()) {
-            PropertyExpression pexp = new PropertyExpression(
-                    new ClassExpression(currentClass.getSuperClass()),
-                    transform(pe.getProperty())
-            );
-            pexp.setSourcePosition(pe);
-            return pexp;
-        }
-        boolean oldInPropertyExpression = inPropertyExpression;
-        Expression oldFoundArgs = foundArgs;
-        Expression oldFoundConstant = foundConstant;
-        inPropertyExpression = true;
-        foundArgs = null;
-        foundConstant = null;
-        Expression objectExpression = transform(pe.getObjectExpression());
-        boolean candidate = false;
-        if (objectExpression instanceof MethodCallExpression) {
-            candidate = ((MethodCallExpression)objectExpression).isImplicitThis();
-        }
-
-        if (foundArgs != null && foundConstant != null && candidate) {
-            Expression result = findStaticMethodImportFromModule(foundConstant, foundArgs);
-            if (result != null) {
-                objectExpression = result;
-                objectExpression.setSourcePosition(pe);
-            }
-        }
-        inPropertyExpression = oldInPropertyExpression;
-        foundArgs = oldFoundArgs;
-        foundConstant = oldFoundConstant;
-        pe.setObjectExpression(objectExpression);
-        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);
-        // 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);
-            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());
-            if (expression != null) return expression;
-        }
-        // look for one of these:
-        //   import static MyClass.*
-        // when resolving prop 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);
-            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;
-    }
-
-    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;
-        Expression expression;
-        Object value = ce.getValue();
-        // skip non-Strings, e.g. Integer
-        if (!(value instanceof String)) return null;
-        final String name = (String) value;
-        // 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);
-            expression = findStaticMethod(importNode.getType(), importNode.getFieldName(), args);
-            if (expression != null) return expression;
-            expression = findStaticPropertyAccessorGivenArgs(importNode.getType(), getPropNameForAccessor(importNode.getFieldName()), args);
-            if (expression != null) {
-                return new StaticMethodCallExpression(importNode.getType(), importNode.getFieldName(), args);
-            }
-        }
-        // 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();
-                String importMember = importNode.getFieldName();
-                expression = findStaticMethod(importClass, prefix(name) + capitalize(importMember), args);
-                if (expression != null) return expression;
-                expression = findStaticPropertyAccessorGivenArgs(importClass, importMember, args);
-                if (expression != null) {
-                    return new StaticMethodCallExpression(importClass, 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);
-            if (expression != null) 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 new StaticMethodCallExpression(starImportType, name, args);
-                }
-            }
-        }
-        return null;
-    }
-
-    private static String prefix(String name) {
-        return name.startsWith("is") ? "is" : name.substring(0, 3);
-    }
-
-    private String getAccessorName(String name) {
-        return (inLeftExpression ? "set" : "get") + capitalize(name);
-    }
-
-    private Expression findStaticPropertyAccessorGivenArgs(ClassNode staticImportType, String propName, Expression args) {
-        // TODO validate args?
-        return findStaticPropertyAccessor(staticImportType, propName);
-    }
-
-    private Expression findStaticPropertyAccessor(ClassNode staticImportType, String propName) {
-        String accessorName = getAccessorName(propName);
-        Expression accessor = findStaticPropertyAccessorByFullName(staticImportType, accessorName);
-        if (accessor == null && accessorName.startsWith("get")) {
-            accessor = findStaticPropertyAccessorByFullName(staticImportType, "is" + accessorName.substring(3));
-        }
-        if (accessor == null && hasStaticProperty(staticImportType, propName)) {
-            // args will be replaced
-            if (inLeftExpression)
-                accessor = new StaticMethodCallExpression(staticImportType, accessorName, ArgumentListExpression.EMPTY_ARGUMENTS);
-            else
-                accessor = new PropertyExpression(new ClassExpression(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 static Expression findStaticField(ClassNode staticImportType, String fieldName) {
-        if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) {
-            FieldNode field = getField(staticImportType, fieldName);
-            if (field != null && field.isStatic())
-                return new PropertyExpression(new ClassExpression(staticImportType), fieldName);
-        }
-        return null;
-    }
-
-    private static Expression findStaticMethod(ClassNode staticImportType, String methodName, Expression args) {
-        if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) {
-            if (staticImportType.hasPossibleStaticMethod(methodName, args)) {
-                return new StaticMethodCallExpression(staticImportType, methodName, args);
-            }
-        }
-        return null;
-    }
-
-    protected SourceUnit getSourceUnit() {
-        return source;
-    }
-}
+/*
+ *  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 org.codehaus.groovy.control;
+
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.DynamicVariable;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.ImportNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.EmptyExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.syntax.Types;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.groovy.ast.tools.ClassNodeUtils.getPropNameForAccessor;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.hasPossibleStaticMethod;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.hasPossibleStaticProperty;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.hasStaticProperty;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.isInnerClass;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.isValidAccessorName;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.getField;
+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;
+
+/**
+ * Visitor to resolve constants and method calls from static Imports
+ */
+public class StaticImportVisitor extends ClassCodeExpressionTransformer {
+    private ClassNode currentClass;
+    private MethodNode currentMethod;
+    private SourceUnit source;
+    private boolean inSpecialConstructorCall;
+    private boolean inClosure;
+    private boolean inPropertyExpression;
+    private Expression foundConstant;
+    private Expression foundArgs;
+    private boolean inAnnotation;
+    private boolean inLeftExpression;
+
+    public void visitClass(ClassNode node, SourceUnit source) {
+        this.currentClass = node;
+        this.source = source;
+        super.visitClass(node);
+    }
+
+    @Override
+    protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
+        this.currentMethod = node;
+        super.visitConstructorOrMethod(node, isConstructor);
+        this.currentMethod = null;
+    }
+
+    @Override
+    public void visitAnnotations(AnnotatedNode node) {
+        boolean oldInAnnotation = inAnnotation;
+        inAnnotation = true;
+        super.visitAnnotations(node);
+        inAnnotation = oldInAnnotation;
+    }
+
+    public Expression transform(Expression exp) {
+        if (exp == null) return null;
+        if (exp.getClass() == VariableExpression.class) {
+            return transformVariableExpression((VariableExpression) exp);
+        }
+        if (exp.getClass() == BinaryExpression.class) {
+            return transformBinaryExpression((BinaryExpression) exp);
+        }
+        if (exp.getClass() == PropertyExpression.class) {
+            return transformPropertyExpression((PropertyExpression) exp);
+        }
+        if (exp.getClass() == MethodCallExpression.class) {
+            return transformMethodCallExpression((MethodCallExpression) exp);
+        }
+        if (exp.getClass() == ClosureExpression.class) {
+            return transformClosureExpression((ClosureExpression) exp);
+        }
+        if (exp.getClass() == ConstructorCallExpression.class) {
+            return transformConstructorCallExpression((ConstructorCallExpression) exp);
+        }
+        if (exp.getClass() == ArgumentListExpression.class) {
+            Expression result = exp.transformExpression(this);
+            if (inPropertyExpression) {
+                foundArgs = result;
+            }
+            return result;
+        }
+        if (exp instanceof ConstantExpression) {
+            Expression result = exp.transformExpression(this);
+            if (inPropertyExpression) {
+                foundConstant = result;
+            }
+            if (inAnnotation && exp instanceof AnnotationConstantExpression) {
+                ConstantExpression ce = (ConstantExpression) result;
+                if (ce.getValue() instanceof AnnotationNode) {
+                    // replicate a little bit of AnnotationVisitor here
+                    // because we can't wait until later to do this
+                    AnnotationNode an = (AnnotationNode) ce.getValue();
+                    Map<String, Expression> attributes = an.getMembers();
+                    for (Map.Entry<String, Expression> entry : attributes.entrySet()) {
+                        Expression attrExpr = transform(entry.getValue());
+                        entry.setValue(attrExpr);
+                    }
+
+                }
+            }
+            return result;
+        }
+        return exp.transformExpression(this);
+    }
+
+    // if you have a Bar class with a static foo property, and this:
+    //   import static Bar.foo as baz
+    // then this constructor (not normal usage of statics):
+    //   new Bar(baz:1)
+    // will become:
+    //   new Bar(foo:1)
+
+    private Expression transformMapEntryExpression(MapEntryExpression me, ClassNode constructorCallType) {
+        Expression key = me.getKeyExpression();
+        Expression value = me.getValueExpression();
+        ModuleNode module = currentClass.getModule();
+        if (module != null && key instanceof ConstantExpression) {
+            Map<String, ImportNode> importNodes = module.getStaticImports();
+            if (importNodes.containsKey(key.getText())) {
+                ImportNode importNode = importNodes.get(key.getText());
+                if (importNode.getType().equals(constructorCallType)) {
+                    String newKey = importNode.getFieldName();
+                    return new MapEntryExpression(new ConstantExpression(newKey), value.transformExpression(this));
+                }
+            }
+        }
+        return me;
+    }
+
+    protected Expression transformBinaryExpression(BinaryExpression be) {
+        int type = be.getOperation().getType();
+        boolean oldInLeftExpression;
+        Expression right = transform(be.getRightExpression());
+        be.setRightExpression(right);
+        Expression left;
+        if (type == Types.EQUAL && be.getLeftExpression() instanceof VariableExpression) {
+            oldInLeftExpression = inLeftExpression;
+            inLeftExpression = true;
+            left = transform(be.getLeftExpression());
+            inLeftExpression = oldInLeftExpression;
+            if (left instanceof StaticMethodCallExpression) {
+                StaticMethodCallExpression smce = (StaticMethodCallExpression) left;
+                StaticMethodCallExpression result = new StaticMethodCallExpression(smce.getOwnerType(), smce.getMethod(), right);
+                setSourcePosition(result, be);
+                return result;
+            }
+        } else {
+            left = transform(be.getLeftExpression());
+        }
+        be.setLeftExpression(left);
+        return be;
+    }
+
+    protected Expression transformVariableExpression(VariableExpression ve) {
+        Variable v = ve.getAccessedVariable();
+        if (v instanceof DynamicVariable) {
+            Expression result = findStaticFieldOrPropAccessorImportFromModule(v.getName());
+            if (result != null) {
+                setSourcePosition(result, ve);
+                if (inAnnotation) {
+                    result = transformInlineConstants(result);
+                }
+                return result;
+            }
+        } else if (v instanceof FieldNode) {
+            if (inSpecialConstructorCall) { // GROOVY-8819
+                FieldNode fn = (FieldNode) v;
+                ClassNode declaringClass = fn.getDeclaringClass();
+                if (fn.isStatic() && currentClass.isDerivedFrom(declaringClass)) {
+                    Expression result = new PropertyExpression(new ClassExpression(declaringClass), v.getName());
+                    result.setSourcePosition(ve);
+
+                    return result;
+                }
+            }
+        }
+        return ve;
+    }
+
+    /**
+     * Set the source position of toSet including its property expression if it has one.
+     *
+     * @param toSet resulting node
+     * @param origNode original node
+     */
+    private static void setSourcePosition(Expression toSet, Expression origNode) {
+        toSet.setSourcePosition(origNode);
+        if (toSet instanceof PropertyExpression) {
+            ((PropertyExpression) toSet).getProperty().setSourcePosition(origNode);
+        }
+    }
+
+    protected Expression transformMethodCallExpression(MethodCallExpression mce) {
+        Expression args = transform(mce.getArguments());
+        Expression method = transform(mce.getMethod());
+        Expression object = transform(mce.getObjectExpression());
+        boolean isExplicitThisOrSuper = false;
+        boolean isExplicitSuper = false;
+        if (object instanceof VariableExpression) {
+            VariableExpression ve = (VariableExpression) object;
+            isExplicitThisOrSuper = !mce.isImplicitThis() && (ve.isThisExpression() || ve.isSuperExpression());
+            isExplicitSuper = ve.isSuperExpression();
+        }
+
+        if (mce.isImplicitThis() || isExplicitThisOrSuper) {
+            if (mce.isImplicitThis()) {
+                if (null == currentClass.tryFindPossibleMethod(mce.getMethodAsString(), args)) {
+
+                    Expression ret = findStaticMethodImportFromModule(method, args);
+                    if (ret != null) {
+                        setSourcePosition(ret, mce);
+                        return ret;
+                    }
+                    if (method instanceof ConstantExpression && !inLeftExpression) {
+                        // could be a closure field
+                        String methodName = (String) ((ConstantExpression) method).getValue();
+                        ret = findStaticFieldOrPropAccessorImportFromModule(methodName);
+                        if (ret != null) {
+                            ret = new MethodCallExpression(ret, "call", args);
+                            setSourcePosition(ret, mce);
+                            return ret;
+                        }
+                    }
+                }
+            } else if (currentMethod!=null && currentMethod.isStatic() && isExplicitSuper) {
+                MethodCallExpression ret = new MethodCallExpression(new ClassExpression(currentClass.getSuperClass()), method, args);
+                setSourcePosition(ret, mce);
+                return ret;
+            }
+
+            if (method instanceof ConstantExpression) {
+                ConstantExpression ce = (ConstantExpression) method;
+                Object value = ce.getValue();
+                if (value instanceof String) {
+                    boolean foundInstanceMethod = false;
+                    String methodName = (String) value;
+                    boolean inInnerClass = isInnerClass(currentClass);
+                    if (currentMethod != null && !currentMethod.isStatic()) {
+                        if (currentClass.hasPossibleMethod(methodName, args)) {
+                            foundInstanceMethod = true;
+                        }
+                    }
+                    boolean lookForPossibleStaticMethod = !methodName.equals("call");
+                    lookForPossibleStaticMethod &= !foundInstanceMethod;
+                    lookForPossibleStaticMethod |= inSpecialConstructorCall;
+                    lookForPossibleStaticMethod &= !inInnerClass;
+                    if (!inClosure && lookForPossibleStaticMethod &&
+                            (hasPossibleStaticMethod(currentClass, methodName, args, true))
+                            || hasPossibleStaticProperty(currentClass, methodName)) {
+                        StaticMethodCallExpression smce = new StaticMethodCallExpression(currentClass, methodName, args);
+                        setSourcePosition(smce, mce);
+                        return smce;
+                    }
+                    if (!inClosure && inInnerClass && inSpecialConstructorCall && mce.isImplicitThis() && !foundInstanceMethod) {
+                        if (currentClass.getOuterClass().hasPossibleMethod(methodName, args)) {
+                            object = new PropertyExpression(new ClassExpression(currentClass.getOuterClass()), new ConstantExpression("this"));
+                        } else if (hasPossibleStaticMethod(currentClass.getOuterClass(), methodName, args, true)
+                                || hasPossibleStaticProperty(currentClass.getOuterClass(), methodName)) {
+                            StaticMethodCallExpression smce = new StaticMethodCallExpression(currentClass.getOuterClass(), methodName, args);
+                            setSourcePosition(smce, mce);
+                            return smce;
+                        }
+                    }
+
+                    if (mce.isImplicitThis() && lookForPossibleStaticMethod && hasPossibleStaticMethod(currentClass, methodName, args, true)) {
+                        StaticMethodCallExpression result = new StaticMethodCallExpression(currentClass, methodName, args);
+                        result.setSourcePosition(mce);
+                        return result;
+                    }
+                }
+            }
+        }
+
+        MethodCallExpression result = new MethodCallExpression(object, method, args);
+        result.setSafe(mce.isSafe());
+        result.setImplicitThis(mce.isImplicitThis());
+        result.setSpreadSafe(mce.isSpreadSafe());
+        result.setMethodTarget(mce.getMethodTarget());
+        // GROOVY-6757
+        result.setGenericsTypes(mce.getGenericsTypes());
+        setSourcePosition(result, mce);
+        return result;
+    }
+
+    protected Expression transformConstructorCallExpression(ConstructorCallExpression cce) {
+        inSpecialConstructorCall = cce.isSpecialCall();
+        Expression expression = cce.getArguments();
+        if (expression instanceof TupleExpression) {
+            TupleExpression tuple = (TupleExpression) expression;
+            if (tuple.getExpressions().size() == 1) {
+                expression = tuple.getExpression(0);
+                if (expression instanceof NamedArgumentListExpression) {
+                    NamedArgumentListExpression namedArgs = (NamedArgumentListExpression) expression;
+                    List<MapEntryExpression> entryExpressions = namedArgs.getMapEntryExpressions();
+                    for (int i = 0; i < entryExpressions.size(); i++) {
+                        entryExpressions.set(i, (MapEntryExpression) transformMapEntryExpression(entryExpressions.get(i), cce.getType()));
+                    }
+                }
+            }
+        }
+        Expression ret = cce.transformExpression(this);
+        inSpecialConstructorCall = false;
+        return ret;
+    }
+
+    protected Expression transformClosureExpression(ClosureExpression ce) {
+        boolean oldInClosure = inClosure;
+        inClosure = true;
+        for (Parameter p : getParametersSafe(ce)) {
+            if (p.hasInitialExpression()) {
+                p.setInitialExpression(transform(p.getInitialExpression()));
+            }
+        }
+        Statement code = ce.getCode();
+        if (code != null) code.visit(this);
+        inClosure = oldInClosure;
+        return ce;
+    }
+
+    protected Expression transformPropertyExpression(PropertyExpression pe) {
+        if (currentMethod!=null && currentMethod.isStatic()
+                && pe.getObjectExpression() instanceof VariableExpression
+                && ((VariableExpression) pe.getObjectExpression()).isSuperExpression()) {
+            PropertyExpression pexp = new PropertyExpression(
+                    new ClassExpression(currentClass.getSuperClass()),
+                    transform(pe.getProperty())
+            );
+            pexp.setSourcePosition(pe);
+            return pexp;
+        }
+        boolean oldInPropertyExpression = inPropertyExpression;
+        Expression oldFoundArgs = foundArgs;
+        Expression oldFoundConstant = foundConstant;
+        inPropertyExpression = true;
+        foundArgs = null;
+        foundConstant = null;
+        Expression objectExpression = transform(pe.getObjectExpression());
+        boolean candidate = false;
+        if (objectExpression instanceof MethodCallExpression) {
+            candidate = ((MethodCallExpression)objectExpression).isImplicitThis();
+        }
+
+        if (foundArgs != null && foundConstant != null && candidate) {
+            Expression result = findStaticMethodImportFromModule(foundConstant, foundArgs);
+            if (result != null) {
+                objectExpression = result;
+                objectExpression.setSourcePosition(pe);
+            }
+        }
+        inPropertyExpression = oldInPropertyExpression;
+        foundArgs = oldFoundArgs;
+        foundConstant = oldFoundConstant;
+        pe.setObjectExpression(objectExpression);
+        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);
+        // 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);
+            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());
+            if (expression != null) return expression;
+        }
+        // look for one of these:
+        //   import static MyClass.*
+        // when resolving prop 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);
+            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;
+    }
+
+    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;
+        Expression expression;
+        Object value = ce.getValue();
+        // skip non-Strings, e.g. Integer
+        if (!(value instanceof String)) return null;
+        final String name = (String) value;
+        // 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);
+            expression = findStaticMethod(importNode.getType(), importNode.getFieldName(), args);
+            if (expression != null) return expression;
+            expression = findStaticPropertyAccessorGivenArgs(importNode.getType(), getPropNameForAccessor(importNode.getFieldName()), args);
+            if (expression != null) {
+                return new StaticMethodCallExpression(importNode.getType(), importNode.getFieldName(), args);
+            }
+        }
+        // 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();
+                String importMember = importNode.getFieldName();
+                expression = findStaticMethod(importClass, prefix(name) + capitalize(importMember), args);
+                if (expression != null) return expression;
+                expression = findStaticPropertyAccessorGivenArgs(importClass, importMember, args);
+                if (expression != null) {
+                    return new StaticMethodCallExpression(importClass, 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);
+            if (expression != null) 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 new StaticMethodCallExpression(starImportType, name, args);
+                }
+            }
+        }
+        return null;
+    }
+
+    private static String prefix(String name) {
+        return name.startsWith("is") ? "is" : name.substring(0, 3);
+    }
+
+    private String getAccessorName(String name) {
+        return (inLeftExpression ? "set" : "get") + capitalize(name);
+    }
+
+    private Expression findStaticPropertyAccessorGivenArgs(ClassNode staticImportType, String propName, Expression args) {
+        // TODO validate args?
+        return findStaticPropertyAccessor(staticImportType, propName);
+    }
+
+    private Expression findStaticPropertyAccessor(ClassNode staticImportType, String propName) {
+        String accessorName = getAccessorName(propName);
+        Expression accessor = findStaticPropertyAccessorByFullName(staticImportType, accessorName);
+        if (accessor == null && accessorName.startsWith("get")) {
+            accessor = findStaticPropertyAccessorByFullName(staticImportType, "is" + accessorName.substring(3));
+        }
+        if (accessor == null && hasStaticProperty(staticImportType, propName)) {
+            // args will be replaced
+            if (inLeftExpression)
+                accessor = new StaticMethodCallExpression(staticImportType, accessorName, ArgumentListExpression.EMPTY_ARGUMENTS);
+            else
+                accessor = new PropertyExpression(new ClassExpression(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 static Expression findStaticField(ClassNode staticImportType, String fieldName) {
+        if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) {
+            FieldNode field = getField(staticImportType, fieldName);
+            if (field != null && field.isStatic())
+                return new PropertyExpression(new ClassExpression(staticImportType), fieldName);
+        }
+        return null;
+    }
+
+    private static Expression findStaticMethod(ClassNode staticImportType, String methodName, Expression args) {
+        if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) {
+            if (staticImportType.hasPossibleStaticMethod(methodName, args)) {
+                return new StaticMethodCallExpression(staticImportType, methodName, args);
+            }
+        }
+        return null;
+    }
+
+    protected SourceUnit getSourceUnit() {
+        return source;
+    }
+}
diff --git a/src/main/java/org/codehaus/groovy/transform/AutoFinalASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
index 3717d24..176b51e 100644
--- a/src/main/java/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/AutoFinalASTTransformation.java
@@ -39,6 +39,7 @@ import java.util.Iterator;
 import java.util.List;
 
 import static org.codehaus.groovy.ast.ClassHelper.make;
+import static org.codehaus.groovy.ast.tools.ClosureUtils.getParametersSafe;
 
 /**
  * Handles generation of code for the {@link AutoFinal} annotation.
@@ -65,7 +66,7 @@ public class AutoFinalASTTransformation extends AbstractASTTransformation {
                 if (expression.isSynthetic()) {
                     return;
                 }
-                Parameter[] origParams = expression.getParameters();
+                Parameter[] origParams = getParametersSafe(expression);
                 for (Parameter p : origParams) {
                     p.setModifiers(p.getModifiers() | Modifier.FINAL);
                 }
diff --git a/src/main/java/org/codehaus/groovy/transform/CategoryASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/CategoryASTTransformation.java
index c56a7b5..443db581 100644
--- a/src/main/java/org/codehaus/groovy/transform/CategoryASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/CategoryASTTransformation.java
@@ -51,6 +51,10 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
+import static org.codehaus.groovy.ast.tools.ClosureUtils.getParametersSafe;
+import static org.codehaus.groovy.ast.tools.ClosureUtils.hasImplicitParameter;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
 
 /**
  * Handles generation of code for the @Category annotation.
@@ -140,7 +144,7 @@ public class CategoryASTTransformation implements ASTTransformation, Opcodes {
 
             @Override
             public void visitClosureExpression(ClosureExpression ce) {
-                addVariablesToStack(ce.getParameters());
+                addVariablesToStack(getParametersSafe(ce));
                 super.visitClosureExpression(ce);
                 varStack.removeLast();
             }
@@ -205,15 +209,10 @@ public class CategoryASTTransformation implements ASTTransformation, Opcodes {
                 } else if (exp instanceof ClosureExpression) {
                     ClosureExpression ce = (ClosureExpression) exp;
                     ce.getVariableScope().putReferencedLocalVariable((Parameter) parameter.get());
-                    Parameter[] params = ce.getParameters();
-                    if (params == null) {
-                        params = Parameter.EMPTY_ARRAY;
-                    } else if (params.length == 0) {
-                        params = new Parameter[]{
-                                new Parameter(ClassHelper.OBJECT_TYPE, "it")
-                        };
-                    }
-                    addVariablesToStack(params);
+                    addVariablesToStack(
+                            hasImplicitParameter(ce)
+                                    ? params(param(ClassHelper.OBJECT_TYPE, "it"))
+                                    : getParametersSafe(ce));
                     ce.getCode().visit(this);
                     varStack.removeLast();
                 }
diff --git a/src/main/java/org/codehaus/groovy/transform/sc/transformers/ClosureExpressionTransformer.java b/src/main/java/org/codehaus/groovy/transform/sc/transformers/ClosureExpressionTransformer.java
index a5b33f8..8be6416 100644
--- a/src/main/java/org/codehaus/groovy/transform/sc/transformers/ClosureExpressionTransformer.java
+++ b/src/main/java/org/codehaus/groovy/transform/sc/transformers/ClosureExpressionTransformer.java
@@ -23,6 +23,8 @@ import org.codehaus.groovy.ast.expr.ClosureExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.stmt.Statement;
 
+import static org.codehaus.groovy.ast.tools.ClosureUtils.getParametersSafe;
+
 public class ClosureExpressionTransformer {
     private final StaticCompilationTransformer transformer;
 
@@ -31,12 +33,9 @@ public class ClosureExpressionTransformer {
     }
 
     Expression transformClosureExpression(final ClosureExpression expr) {
-        Parameter[] parameters = expr.getParameters();        
-        if (parameters!=null) {
-            for (Parameter parameter : parameters) {
-                if (parameter.hasInitialExpression()) {
-                    parameter.setInitialExpression(transformer.transform(parameter.getInitialExpression()));
-                }
+        for (Parameter parameter : getParametersSafe(expr)) {
+            if (parameter.hasInitialExpression()) {
+                parameter.setInitialExpression(transformer.transform(parameter.getInitialExpression()));
             }
         }
         Statement code = expr.getCode();
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 2fe787a..0f2b3c3 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -175,6 +175,7 @@ import static org.codehaus.groovy.ast.ClassHelper.make;
 import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.void_WRAPPER_TYPE;
 import static org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
+import static org.codehaus.groovy.ast.tools.ClosureUtils.getParametersSafe;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.binX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
@@ -831,14 +832,14 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
                 Expression constructedRightExpression = rightExpression;
 
-                boolean isMrExprRHS = rightExpression instanceof MethodReferenceExpression && ClassHelper.isFunctionalInterface(lType);
-                if (isMrExprRHS) {
+                boolean isMethodRefRHS = rightExpression instanceof MethodReferenceExpression && ClassHelper.isFunctionalInterface(lType);
+                if (isMethodRefRHS) {
                     constructedRightExpression = constructLambdaExpressionForMethodReference(lType);
                 }
 
                 inferParameterAndReturnTypesOfClosureOnRHS(lType, constructedRightExpression, op);
 
-                if (isMrExprRHS) {
+                if (isMethodRefRHS) {
                     LambdaExpression lambdaExpression = (LambdaExpression) constructedRightExpression;
                     ClassNode[] argumentTypes =
                             Arrays.stream(lambdaExpression.getParameters())
@@ -1002,7 +1003,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 Tuple2<ClassNode[], ClassNode> typeInfo = GenericsUtils.parameterizeSAM(lType);
                 ClassNode[] paramTypes = typeInfo.getV1();
                 ClosureExpression closureExpression = ((ClosureExpression) rightExpression);
-                Parameter[] closureParameters = closureExpression.getParameters();
+                Parameter[] closureParameters = getParametersSafe(closureExpression);
 
                 if (paramTypes.length == closureParameters.length) {
                     for (int i = 0, n = closureParameters.length; i < n; i++) {
@@ -2452,11 +2453,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         // restore original metadata
         restoreVariableExpressionMetadata(typesBeforeVisit);
         typeCheckingContext.isInStaticContext = oldStaticContext;
-        Parameter[] parameters = expression.getParameters();
-        if (parameters != null) {
-            for (Parameter parameter : parameters) {
-                typeCheckingContext.controlStructureVariables.remove(parameter);
-            }
+        for (Parameter parameter : getParametersSafe(expression)) {
+            typeCheckingContext.controlStructureVariables.remove(parameter);
         }
     }
 
@@ -4520,15 +4518,16 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         // next we get the block parameter types and set the generics
         // information just like before
         // TODO: add vargs handling
-        Parameter[] closureParams = closureExpression.getParameters();
-        Parameter[] methodParams = sam.getParameters();
-        for (int i = 0; i < closureParams.length; i++) {
-            ClassNode fromClosure = closureParams[i].getType();
-            ClassNode fromMethod = methodParams[i].getType();
-            extractGenericsConnections(connections, fromClosure, fromMethod);
-        }
-        ClassNode result = applyGenericsContext(connections, samUsage.redirect());
-        return result;
+        if (closureExpression.isParameterSpecified()) {
+            Parameter[] closureParams = closureExpression.getParameters();
+            Parameter[] methodParams = sam.getParameters();
+            for (int i = 0; i < closureParams.length; i++) {
+                ClassNode fromClosure = closureParams[i].getType();
+                ClassNode fromMethod = methodParams[i].getType();
+                extractGenericsConnections(connections, fromClosure, fromMethod);
+            }
+        }
+        return applyGenericsContext(connections, samUsage.redirect());
     }
 
     protected static ClassNode getGroupOperationResultType(ClassNode a, ClassNode b) {
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index 102e1c5..108376d 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -32,6 +32,22 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    void testClosureWithoutArgumentsExplicit() {
+        // GROOVY-9079: no params to statically type check but shouldn't get NPE
+        assertScript '''
+            import groovy.transform.CompileStatic
+            import java.util.concurrent.Callable
+
+            @CompileStatic
+            String makeFoo() {
+                Callable<String> call = { -> 'foo' }
+                call()
+            }
+
+            assert makeFoo() == 'foo'
+        '''
+    }
+
     void testClosureWithArguments() {
         assertScript '''
             def printSum = { int a, int b -> print a+b }
diff --git a/src/test/org/codehaus/groovy/transform/AutoFinalTransformBlackBoxTest.groovy b/src/test/org/codehaus/groovy/transform/AutoFinalTransformBlackBoxTest.groovy
index b06e265..a1df9dc 100644
--- a/src/test/org/codehaus/groovy/transform/AutoFinalTransformBlackBoxTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/AutoFinalTransformBlackBoxTest.groovy
@@ -66,6 +66,22 @@ class AutoFinalTransformBlackBoxTest extends CompilableTestSupport {
         assertScript(script)
     }
 
+    @Test
+    void testNoargClosureSuccessfullyParsed() {
+        // GROOVY-9079: no params to make final but shouldn't get NPE
+        assertScript '''
+            import groovy.transform.AutoFinal
+            import java.util.concurrent.Callable
+
+            String makeFoo() {
+                @AutoFinal
+                Callable<String> call = { -> 'foo' }
+                call()
+            }
+
+            assert makeFoo() == 'foo'
+        '''
+    }
 
     void assertAutoFinalClassTestScript(final String paramName, final String classPart) {
         assertAutoFinalTestScriptWithAnnotation(paramName, classPart)


Mime
View raw message