You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2019/04/12 04:00:59 UTC
[groovy] 02/02: GROOVY-9079: STC: NPE in
StaticTypeCheckingVisitor.inferSAMTypeGenericsInAssignment
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 <pa...@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)