You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2019/10/27 15:43:52 UTC
[groovy] 03/03: GROOVY-7996: add test cases and roll back initial
changes
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 1b9501f7788d7c767fc09e85f2850b3c9de29766
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Oct 27 10:43:20 2019 -0500
GROOVY-7996: add test cases and roll back initial changes
GROOVY-9288: resolved by rollback
---
.../transform/stc/StaticTypeCheckingVisitor.java | 232 ++++++++-------------
src/test/groovy/bugs/Groovy7996.groovy | 183 ++++++++++++++++
src/test/groovy/bugs/Groovy7996Bug.groovy | 90 --------
3 files changed, 273 insertions(+), 232 deletions(-)
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 6be103c..263463a 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -41,6 +41,7 @@ import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.DynamicVariable;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.GenericsType;
+import org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
@@ -94,6 +95,7 @@ import org.codehaus.groovy.ast.stmt.TryCatchStatement;
import org.codehaus.groovy.ast.stmt.WhileStatement;
import org.codehaus.groovy.ast.tools.GenericsUtils;
import org.codehaus.groovy.ast.tools.WideningCategories;
+import org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode;
import org.codehaus.groovy.classgen.ReturnAdder;
import org.codehaus.groovy.classgen.Verifier;
import org.codehaus.groovy.classgen.asm.InvocationWriter;
@@ -175,7 +177,6 @@ import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
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;
@@ -188,7 +189,6 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
import static org.codehaus.groovy.ast.tools.GenericsUtils.findActualTypeByGenericsPlaceholderName;
import static org.codehaus.groovy.ast.tools.GenericsUtils.makeDeclaringAndActualGenericsTypeMap;
import static org.codehaus.groovy.ast.tools.GenericsUtils.toGenericTypesString;
-import static org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode;
import static org.codehaus.groovy.ast.tools.WideningCategories.isBigDecCategory;
import static org.codehaus.groovy.ast.tools.WideningCategories.isBigIntCategory;
import static org.codehaus.groovy.ast.tools.WideningCategories.isDouble;
@@ -271,7 +271,19 @@ import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.typeCh
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.typeCheckMethodsWithGenerics;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.CLOSURE_ARGUMENTS;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.CONSTRUCTED_LAMBDA_EXPRESSION;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.DECLARATION_INFERRED_TYPE;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.DELEGATION_METADATA;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.DIRECT_METHOD_CALL_TARGET;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.DYNAMIC_RESOLUTION;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.IMPLICIT_RECEIVER;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_RETURN_TYPE;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PV_FIELDS_ACCESS;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PV_FIELDS_MUTATION;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PV_METHODS_ACCESS;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.READONLY_PROPERTY;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.SUPER_MOP_METHOD_REQUIRED;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.TYPE;
/**
* The main class code visitor responsible for static type checking. It will perform various inspections like checking
@@ -524,32 +536,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
return;
}
}
- StaticTypesMarker marker = lhsOfAssignment ? StaticTypesMarker.PV_FIELDS_MUTATION : StaticTypesMarker.PV_FIELDS_ACCESS;
+ StaticTypesMarker marker = lhsOfAssignment ? PV_FIELDS_MUTATION : PV_FIELDS_ACCESS;
addPrivateFieldOrMethodAccess(source, declaringClass, marker, fn);
}
}
- /**
- * Checks valid cases for accessing a field from an inner class.
- */
- private String checkOrMarkInnerPropertyOwnerAccess(PropertyExpression source, boolean lhsOfAssignment, String delegationData) {
- // check for reference to method, closure, for loop, try with, or catch block parameter from a non-nested closure
- if (typeCheckingContext.getEnclosingClosureStack().size() == 1 && !"this".equals(source.getPropertyAsString()) &&
- !isParameterOrVariableReference(source.getObjectExpression())) {
- delegationData = "owner";
- source.getObjectExpression().putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData);
- }
- return delegationData;
- }
-
- private static boolean isParameterOrVariableReference(Expression objectExpr) {
- if (objectExpr instanceof VariableExpression) {
- Variable accessedVar = ((VariableExpression) objectExpr).getAccessedVariable();
- return (accessedVar instanceof Parameter || accessedVar instanceof VariableExpression);
- }
- return false;
- }
-
private MethodNode findValidGetter(ClassNode classNode, String name) {
MethodNode getterMethod = classNode.getGetterMethod(name);
if (getterMethod != null && (getterMethod.isPublic() || getterMethod.isProtected())) {
@@ -575,13 +566,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
packageName = "";
}
if ((Modifier.isPrivate(mods) && sameModule)) {
- addPrivateFieldOrMethodAccess(source, declaringClass, StaticTypesMarker.PV_METHODS_ACCESS, mn);
+ addPrivateFieldOrMethodAccess(source, declaringClass, PV_METHODS_ACCESS, mn);
} else if (Modifier.isProtected(mods) && !packageName.equals(enclosingClassNode.getPackageName())
&& !implementsInterfaceOrIsSubclassOf(enclosingClassNode, declaringClass)) {
ClassNode cn = enclosingClassNode;
while ((cn = cn.getOuterClass()) != null) {
if (implementsInterfaceOrIsSubclassOf(cn, declaringClass)) {
- addPrivateFieldOrMethodAccess(source, cn, StaticTypesMarker.PV_METHODS_ACCESS, mn);
+ addPrivateFieldOrMethodAccess(source, cn, PV_METHODS_ACCESS, mn);
break;
}
}
@@ -596,13 +587,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
VariableExpression var = (VariableExpression) objectExpression;
if (var.isSuperExpression()) {
ClassNode current = typeCheckingContext.getEnclosingClassNode();
- LinkedList<MethodNode> list = current.getNodeMetaData(StaticTypesMarker.SUPER_MOP_METHOD_REQUIRED);
+ LinkedList<MethodNode> list = current.getNodeMetaData(SUPER_MOP_METHOD_REQUIRED);
if (list == null) {
list = new LinkedList<MethodNode>();
- current.putNodeMetaData(StaticTypesMarker.SUPER_MOP_METHOD_REQUIRED, list);
+ current.putNodeMetaData(SUPER_MOP_METHOD_REQUIRED, list);
}
list.add(directCallTarget);
- call.putNodeMetaData(StaticTypesMarker.SUPER_MOP_METHOD_REQUIRED, current);
+ call.putNodeMetaData(SUPER_MOP_METHOD_REQUIRED, current);
}
}
}
@@ -673,7 +664,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (tryVariableExpressionAsProperty(vexp, vexp.getName())) {
// IMPLICIT_RECEIVER is handled elsewhere
// however other access needs to be fixed for private access
- if (vexp.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER) == null) {
+ if (vexp.getNodeMetaData(IMPLICIT_RECEIVER) == null) {
ClassNode owner = (ClassNode) vexp.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER);
if (owner != null) {
FieldNode veFieldNode = owner.getField(vexp.getName());
@@ -735,7 +726,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ClassNode inferredType = getInferredTypeFromTempInfo(variable, variable.getNodeMetaData(INFERRED_TYPE));
// instanceof applies, stash away the type, reusing key used elsewhere
if (inferredType != null && !inferredType.getName().equals("java.lang.Object")) {
- vexp.putNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE, inferredType);
+ vexp.putNodeMetaData(INFERRED_RETURN_TYPE, inferredType);
}
}
}
@@ -763,10 +754,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
vexp.copyNodeMetaData(implicitThis);
vexp.putNodeMetaData(INFERRED_TYPE, previousIt);
storeType(vexp, getType(pe));
- Object val = pe.getNodeMetaData(StaticTypesMarker.READONLY_PROPERTY);
- if (val != null) vexp.putNodeMetaData(StaticTypesMarker.READONLY_PROPERTY, val);
- val = pe.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER);
- if (val != null) vexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, val);
+ Object val = pe.getNodeMetaData(READONLY_PROPERTY);
+ if (val != null) vexp.putNodeMetaData(READONLY_PROPERTY, val);
+ val = pe.getNodeMetaData(IMPLICIT_RECEIVER);
+ if (val != null) vexp.putNodeMetaData(IMPLICIT_RECEIVER, val);
return true;
}
return false;
@@ -872,7 +863,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (op == KEYWORD_IN || op == COMPARE_NOT_IN) {
// in case of the "in" operator, the receiver and the arguments are reversed
// so we use the reversedExpression and get the target method from it
- storeTargetMethod(expression, (MethodNode) reversedBinaryExpression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
+ storeTargetMethod(expression, (MethodNode) reversedBinaryExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET));
} else if (op == LEFT_SQUARE_BRACKET
&& leftExpression instanceof VariableExpression
&& leftExpression.getNodeMetaData(INFERRED_TYPE) == null) {
@@ -1064,7 +1055,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
MethodCallExpression call = callX(ve, setterInfo.name, newRightExpression);
call.setImplicitThis(false);
visitMethodCallExpression(call);
- MethodNode directSetterCandidate = call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+ MethodNode directSetterCandidate = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
if (directSetterCandidate == null) {
// this may happen if there's a setter of type boolean/String/Class, and that we are using the property
// notation AND that the RHS is not a boolean/String/Class
@@ -1074,7 +1065,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
call = callX(ve, setterInfo.name, castX(type, newRightExpression));
call.setImplicitThis(false);
visitMethodCallExpression(call);
- directSetterCandidate = call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+ directSetterCandidate = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
if (directSetterCandidate != null) {
break;
}
@@ -1084,7 +1075,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (directSetterCandidate != null) {
for (MethodNode setter : setterInfo.setters) {
if (setter == directSetterCandidate) {
- leftExpression.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, directSetterCandidate);
+ leftExpression.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, directSetterCandidate);
storeType(leftExpression, getType(newRightExpression));
break;
}
@@ -1268,7 +1259,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
private boolean addedReadOnlyPropertyError(Expression expr) {
// if expr is of READONLY_PROPERTY_RETURN type, then it means we are on a missing property
- if (expr.getNodeMetaData(StaticTypesMarker.READONLY_PROPERTY) == null) return false;
+ if (expr.getNodeMetaData(READONLY_PROPERTY) == null) return false;
String name;
if (expr instanceof VariableExpression) {
name = ((VariableExpression) expr).getName();
@@ -1318,7 +1309,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ClassNode[] args = getArgumentTypes(argList);
MethodNode methodNode = checkGroovyStyleConstructor(leftRedirect, args, assignmentExpression);
if (methodNode != null) {
- rightExpression.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, methodNode);
+ rightExpression.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, methodNode);
}
} else if (!implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, leftRedirect)
&& implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, LIST_TYPE)
@@ -1391,7 +1382,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ClassNode leftRedirect = leftExpressionType.redirect();
// see if instanceof applies
if (rightExpression instanceof VariableExpression && hasInferredReturnType(rightExpression) && assignmentExpression.getOperation().getType() == EQUAL) {
- inferredRightExpressionType = rightExpression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE);
+ inferredRightExpressionType = rightExpression.getNodeMetaData(INFERRED_RETURN_TYPE);
}
ClassNode wrappedRHS = adjustTypeForSpreading(inferredRightExpressionType, leftExpression);
@@ -1580,16 +1571,16 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
boolean foundGetterOrSetter = false;
- List<Receiver<String>> receivers = new LinkedList<Receiver<String>>();
+ List<Receiver<String>> receivers = new LinkedList<>();
List<Receiver<String>> owners = makeOwnerList(objectExpression);
addReceivers(receivers, owners, pexp.isImplicitThis());
String capName = capitalize(propertyName);
boolean isAttributeExpression = pexp instanceof AttributeExpression;
- HashSet<ClassNode> handledNodes = new HashSet<ClassNode>();
+ HashSet<ClassNode> handledNodes = new HashSet<>();
for (Receiver<String> receiver : receivers) {
ClassNode testClass = receiver.getType();
- LinkedList<ClassNode> queue = new LinkedList<ClassNode>();
+ LinkedList<ClassNode> queue = new LinkedList<>();
queue.add(testClass);
if (isPrimitiveType(testClass)) {
queue.add(getWrapper(testClass));
@@ -1600,7 +1591,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
handledNodes.add(current);
Set<ClassNode> allInterfaces = current.getAllInterfaces();
for (ClassNode intf : allInterfaces) {
- //TODO: apply right generics here!
+ // TODO: apply right generics here!
queue.add(GenericsUtils.parameterizeType(current, intf));
}
@@ -1617,7 +1608,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
FieldNode field = current.getDeclaredField(propertyName);
field = allowStaticAccessToMember(field, staticOnly);
if (storeField(field, isAttributeExpression, pexp, current, visitor, receiver.getData(), !readMode)) {
- pexp.removeNodeMetaData(StaticTypesMarker.READONLY_PROPERTY);
+ pexp.removeNodeMetaData(READONLY_PROPERTY);
return true;
}
@@ -1625,7 +1616,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
&& (objectExpressionType.equals(current) || (objectExpressionType.isDerivedFrom(current) && hasAccessToField(field, objectExpressionType)));
if (storeField(field, isThisExpression, pexp, receiver.getType(), visitor, receiver.getData(), !readMode)) {
- pexp.removeNodeMetaData(StaticTypesMarker.READONLY_PROPERTY);
+ pexp.removeNodeMetaData(READONLY_PROPERTY);
return true;
}
@@ -1643,7 +1634,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
PropertyNode propertyNode = current.getProperty(propertyName);
propertyNode = allowStaticAccessToMember(propertyNode, staticOnly);
- //prefer explicit getter or setter over property if receiver is not 'this'
+ // prefer explicit getter or setter over property if receiver is not 'this'
boolean checkGetterOrSetter = !isThisExpression || propertyNode == null;
if (readMode && checkGetterOrSetter) {
@@ -1651,10 +1642,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ClassNode cn = inferReturnTypeGenerics(current, getter, ArgumentListExpression.EMPTY_ARGUMENTS);
storeInferredTypeForPropertyExpression(pexp, cn);
storeTargetMethod(pexp, getter);
- pexp.removeNodeMetaData(StaticTypesMarker.READONLY_PROPERTY);
+ pexp.removeNodeMetaData(READONLY_PROPERTY);
String delegationData = receiver.getData();
if (delegationData != null)
- pexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData);
+ pexp.putNodeMetaData(IMPLICIT_RECEIVER, delegationData);
return true;
}
} else if (!readMode && checkGetterOrSetter) {
@@ -1670,8 +1661,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
}
- //TODO: apply generics on parameter[0]?
-// storeType(pexp, setter.getParameters()[0].getType());
SetterInfo info = new SetterInfo(current, setterName, setters);
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
if (enclosingBinaryExpression != null) {
@@ -1679,11 +1668,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
String delegationData = receiver.getData();
if (delegationData != null) {
- pexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData);
+ pexp.putNodeMetaData(IMPLICIT_RECEIVER, delegationData);
}
return true;
} else if (getter != null && propertyNode == null) {
- pexp.putNodeMetaData(StaticTypesMarker.READONLY_PROPERTY, true);
+ pexp.putNodeMetaData(READONLY_PROPERTY, Boolean.TRUE);
}
}
foundGetterOrSetter = foundGetterOrSetter || !setters.isEmpty() || getter != null;
@@ -1693,28 +1682,28 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (storeField(field, true, pexp, current, visitor, receiver.getData(), !readMode)) return true;
// if the property expression is an attribute expression (o.@attr), then
// we stop now, otherwise we must check the parent class
- if (/*!isAttributeExpression && */current.getSuperClass() != null) {
+ if (current.getSuperClass() != null) {
queue.add(current.getUnresolvedSuperClass());
}
}
- // GROOVY-5568, the property may be defined by DGM
- List<ClassNode> dgmReceivers = new ArrayList<ClassNode>(2);
+ // GROOVY-5568: the property may be defined by DGM
+ List<ClassNode> dgmReceivers = new ArrayList<>(2);
dgmReceivers.add(testClass);
if (isPrimitiveType(testClass)) dgmReceivers.add(getWrapper(testClass));
for (ClassNode dgmReceiver : dgmReceivers) {
List<MethodNode> methods = findDGMMethodsByNameAndArguments(getTransformLoader(), dgmReceiver, "get" + capName, ClassNode.EMPTY_ARRAY);
- for (MethodNode m : findDGMMethodsByNameAndArguments(getTransformLoader(), dgmReceiver, "is" + capName, ClassNode.EMPTY_ARRAY)) {
- if (Boolean_TYPE.equals(getWrapper(m.getReturnType()))) methods.add(m);
+ for (MethodNode method : findDGMMethodsByNameAndArguments(getTransformLoader(), dgmReceiver, "is" + capName, ClassNode.EMPTY_ARRAY)) {
+ if (Boolean_TYPE.equals(getWrapper(method.getReturnType()))) methods.add(method);
}
if (!methods.isEmpty()) {
- List<MethodNode> methodNodes = chooseBestMethod(dgmReceiver, methods, ClassNode.EMPTY_ARRAY);
- if (methodNodes.size() == 1) {
- MethodNode getter = methodNodes.get(0);
+ List<MethodNode> bestMethods = chooseBestMethod(dgmReceiver, methods, ClassNode.EMPTY_ARRAY);
+ if (bestMethods.size() == 1) {
+ MethodNode getter = bestMethods.get(0);
if (visitor != null) {
visitor.visitMethod(getter);
}
- ClassNode cn = inferReturnTypeGenerics(dgmReceiver, getter, ArgumentListExpression.EMPTY_ARGUMENTS);
- storeInferredTypeForPropertyExpression(pexp, cn);
+ ClassNode returnType = inferReturnTypeGenerics(dgmReceiver, getter, ArgumentListExpression.EMPTY_ARGUMENTS);
+ storeInferredTypeForPropertyExpression(pexp, returnType);
if (readMode) storeTargetMethod(pexp, getter);
return true;
}
@@ -1737,7 +1726,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
storeType(pexp, propertyType);
String delegationData = receiver.getData();
- if (delegationData != null) pexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData);
+ if (delegationData != null) pexp.putNodeMetaData(IMPLICIT_RECEIVER, delegationData);
return true;
}
return foundGetterOrSetter;
@@ -1876,23 +1865,20 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
private boolean storeField(FieldNode field, boolean returnTrueIfFieldExists, PropertyExpression expressionToStoreOn, ClassNode receiver, ClassCodeVisitorSupport visitor, String delegationData, boolean lhsOfAssignment) {
if (field == null || !returnTrueIfFieldExists) return false;
if (visitor != null) visitor.visitField(field);
- storeWithResolve(field.getOriginType(), receiver, field.getDeclaringClass(), field.isStatic(), expressionToStoreOn);
checkOrMarkPrivateAccess(expressionToStoreOn, field, lhsOfAssignment);
- if (field != null && !field.isStatic() && !field.isPrivate() && !"delegate".equals(delegationData)) {
- delegationData = checkOrMarkInnerPropertyOwnerAccess(expressionToStoreOn, lhsOfAssignment, delegationData);
- }
+ storeWithResolve(field.getOriginType(), receiver, field.getDeclaringClass(), field.isStatic(), expressionToStoreOn);
if (delegationData != null) {
- expressionToStoreOn.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData);
+ expressionToStoreOn.putNodeMetaData(IMPLICIT_RECEIVER, delegationData);
}
return true;
}
- private boolean storeProperty(PropertyNode propertyNode, PropertyExpression expressionToStoreOn, ClassNode receiver, ClassCodeVisitorSupport visitor, String delegationData) {
- if (propertyNode == null) return false;
- if (visitor != null) visitor.visitProperty(propertyNode);
- storeWithResolve(propertyNode.getOriginType(), receiver, propertyNode.getDeclaringClass(), propertyNode.isStatic(), expressionToStoreOn);
+ private boolean storeProperty(PropertyNode property, PropertyExpression expressionToStoreOn, ClassNode receiver, ClassCodeVisitorSupport visitor, String delegationData) {
+ if (property == null) return false;
+ if (visitor != null) visitor.visitProperty(property);
+ storeWithResolve(property.getOriginType(), receiver, property.getDeclaringClass(), property.isStatic(), expressionToStoreOn);
if (delegationData != null) {
- expressionToStoreOn.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData);
+ expressionToStoreOn.putNodeMetaData(IMPLICIT_RECEIVER, delegationData);
}
return true;
}
@@ -1909,44 +1895,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
- @Deprecated
- protected SetterInfo hasSetter(final PropertyExpression pexp) {
- String propertyName = pexp.getPropertyAsString();
- if (propertyName == null) return null;
-
- Expression objectExpression = pexp.getObjectExpression();
- List<Receiver<String>> receivers = new LinkedList<Receiver<String>>();
- List<Receiver<String>> owners = makeOwnerList(objectExpression);
- addReceivers(receivers, owners, pexp.isImplicitThis());
-
- String capName = capitalize(propertyName);
- boolean isAttributeExpression = pexp instanceof AttributeExpression;
-
- for (Receiver<String> receiver : receivers) {
- ClassNode testClass = receiver.getType();
- LinkedList<ClassNode> queue = new LinkedList<ClassNode>();
- queue.add(testClass);
- if (testClass.isInterface()) {
- queue.addAll(testClass.getAllInterfaces());
- }
- while (!queue.isEmpty()) {
- ClassNode current = queue.removeFirst();
- current = current.redirect();
- // check that a setter also exists
- String setterName = "set" + capName;
- List<MethodNode> setterMethods = findSetters(current, setterName, false);
- if (!setterMethods.isEmpty()) {
-// storeType(pexp, setterMethod.getParameters()[0].getType());
- return new SetterInfo(current, setterName, setterMethods);
- }
- if (!isAttributeExpression && current.getSuperClass() != null) {
- queue.add(current.getSuperClass());
- }
- }
- }
- return null;
- }
-
@Override
public void visitProperty(PropertyNode node) {
final boolean osc = typeCheckingContext.isInStaticContext;
@@ -2253,7 +2201,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
// handle instanceof cases
if ((expression instanceof VariableExpression) && hasInferredReturnType(expression)) {
- type = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE);
+ type = expression.getNodeMetaData(INFERRED_RETURN_TYPE);
}
MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
if (enclosingMethod != null && typeCheckingContext.getEnclosingClosure() == null) {
@@ -2491,7 +2439,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
protected DelegationMetadata getDelegationMetadata(final ClosureExpression expression) {
- return (DelegationMetadata) expression.getNodeMetaData(StaticTypesMarker.DELEGATION_METADATA);
+ return (DelegationMetadata) expression.getNodeMetaData(DELEGATION_METADATA);
}
protected void restoreVariableExpressionMetadata(final Map<VariableExpression, ListHashMap> typesBeforeVisit) {
@@ -2696,7 +2644,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
addAmbiguousErrorMessage(mn, name, args, call);
}
if (!callArgsVisited) {
- visitMethodCallArguments(receiver, argumentList, true, (MethodNode) call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
+ visitMethodCallArguments(receiver, argumentList, true, (MethodNode) call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET));
}
}
} finally {
@@ -2723,7 +2671,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
addStaticTypeError("Expected parameter type: " + receiver.toString(false) + " but was: " + param.getType().redirect().toString(false), param);
}
}
- closure.putNodeMetaData(StaticTypesMarker.DELEGATION_METADATA, new DelegationMetadata(
+ closure.putNodeMetaData(DELEGATION_METADATA, new DelegationMetadata(
receiver,
Closure.DELEGATE_FIRST,
typeCheckingContext.delegationMetadata
@@ -2766,8 +2714,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
expression.visit(this);
- if (expression.getNodeMetaData(StaticTypesMarker.DELEGATION_METADATA) != null) {
- expression.removeNodeMetaData(StaticTypesMarker.DELEGATION_METADATA);
+ if (expression.getNodeMetaData(DELEGATION_METADATA) != null) {
+ expression.removeNodeMetaData(DELEGATION_METADATA);
}
}
}
@@ -3232,7 +3180,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
+ ") without @DelegatesTo.Target because generic argument types are not available at runtime", value);
}
// temporarily store the delegation strategy and the delegate type
- expression.putNodeMetaData(StaticTypesMarker.DELEGATION_METADATA, new DelegationMetadata(value.getType(), stInt, typeCheckingContext.delegationMetadata));
+ expression.putNodeMetaData(DELEGATION_METADATA, new DelegationMetadata(value.getType(), stInt, typeCheckingContext.delegationMetadata));
} else if (type != null && !"".equals(type.getText()) && type instanceof ConstantExpression) {
String typeString = type.getText();
ClassNode[] resolved = GenericsUtils.parseClassNodesFromString(
@@ -3245,7 +3193,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (resolved != null) {
if (resolved.length == 1) {
resolved = resolveGenericsFromTypeHint(receiver, arguments, mn, resolved);
- expression.putNodeMetaData(StaticTypesMarker.DELEGATION_METADATA, new DelegationMetadata(resolved[0], stInt, typeCheckingContext.delegationMetadata));
+ expression.putNodeMetaData(DELEGATION_METADATA, new DelegationMetadata(resolved[0], stInt, typeCheckingContext.delegationMetadata));
} else {
addStaticTypeError("Incorrect type hint found in method " + (mn), type);
}
@@ -3287,13 +3235,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
}
- expression.putNodeMetaData(StaticTypesMarker.DELEGATION_METADATA, new DelegationMetadata(actualType, stInt, typeCheckingContext.delegationMetadata));
+ expression.putNodeMetaData(DELEGATION_METADATA, new DelegationMetadata(actualType, stInt, typeCheckingContext.delegationMetadata));
break;
}
}
}
}
- if (expression.getNodeMetaData(StaticTypesMarker.DELEGATION_METADATA) == null) {
+ if (expression.getNodeMetaData(DELEGATION_METADATA) == null) {
addError("Not enough arguments found for a @DelegatesTo method call. Please check that you either use an explicit class or @DelegatesTo.Target with a correct id", arguments);
}
}
@@ -3390,7 +3338,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
listNode.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(subcallReturnType))});
storeType(call, listNode);
// store target method
- storeTargetMethod(call, (MethodNode) subcall.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
+ storeTargetMethod(call, (MethodNode) subcall.getNodeMetaData(DIRECT_METHOD_CALL_TARGET));
typeCheckingContext.popEnclosingMethodCall();
return;
}
@@ -3544,7 +3492,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (mn.size() == 1) {
MethodNode directMethodCallCandidate = mn.get(0);
- if (call.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION) == null &&
+ if (call.getNodeMetaData(DYNAMIC_RESOLUTION) == null &&
!directMethodCallCandidate.isStatic() && objectExpression instanceof ClassExpression &&
!"java.lang.Class".equals(directMethodCallCandidate.getDeclaringClass().getName())) {
ClassNode owner = directMethodCallCandidate.getDeclaringClass();
@@ -3586,7 +3534,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (data != null) {
// the method which has been chosen is supposed to be a call on delegate or owner
// so we store the information so that the static compiler may reuse it
- call.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, data);
+ call.putNodeMetaData(IMPLICIT_RECEIVER, data);
}
// if the object expression is a closure shared variable, we will have to perform a second pass
if (objectExpression instanceof VariableExpression) {
@@ -3617,7 +3565,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
// now that a method has been chosen, we are allowed to visit the closures
if (!callArgsVisited) {
- MethodNode mn = (MethodNode) call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+ MethodNode mn = (MethodNode) call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
visitMethodCallArguments(receiver, argumentList, true, mn);
// GROOVY-6219
if (mn != null) {
@@ -3630,7 +3578,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (CLOSURE_TYPE.equals(pType) && CLOSURE_TYPE.equals(aType)) {
if (!isAssignableTo(aType, pType)) {
addNoMatchingMethodError(receiver, name, getArgumentTypes(argumentList), call);
- call.removeNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+ call.removeNodeMetaData(DIRECT_METHOD_CALL_TARGET);
}
}
}
@@ -3657,7 +3605,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
return;
}
- MethodNode selectedMethod = call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+ MethodNode selectedMethod = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
if (null == selectedMethod) return;
Parameter[] parameters = selectedMethod.getParameters();
@@ -3861,7 +3809,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
protected void storeTargetMethod(final Expression call, final MethodNode directMethodCallCandidate) {
- call.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, directMethodCallCandidate);
+ call.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, directMethodCallCandidate);
checkOrMarkPrivateAccess(call, directMethodCallCandidate);
checkSuperCallFromClosure(call, directMethodCallCandidate);
extension.onMethodSelection(call, directMethodCallCandidate);
@@ -4066,7 +4014,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
@Override
protected void afterSwitchConditionExpressionVisited(SwitchStatement statement) {
Expression conditionExpression = statement.getExpression();
- conditionExpression.putNodeMetaData(StaticTypesMarker.TYPE, getType(conditionExpression));
+ conditionExpression.putNodeMetaData(TYPE, getType(conditionExpression));
}
@Override
@@ -4178,10 +4126,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ClassNode typeOfTrue = getType(trueExpression);
// handle instanceof cases
if (hasInferredReturnType(falseExpression)) {
- typeOfFalse = falseExpression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE);
+ typeOfFalse = falseExpression.getNodeMetaData(INFERRED_RETURN_TYPE);
}
if (hasInferredReturnType(trueExpression)) {
- typeOfTrue = trueExpression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE);
+ typeOfTrue = trueExpression.getNodeMetaData(INFERRED_RETURN_TYPE);
}
// TODO consider moving next two statements "up a level", i.e. have just one more widely invoked
// check but determine no -ve consequences first
@@ -4246,7 +4194,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
private static boolean hasInferredReturnType(Expression expression) {
- ClassNode type = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE);
+ ClassNode type = expression.getNodeMetaData(INFERRED_RETURN_TYPE);
return type != null && !type.getName().equals("java.lang.Object");
}
@@ -4291,11 +4239,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
// o = new Object() // and eventually an object !
// in that case, the INFERRED_TYPE corresponds to the current inferred type, while
// DECLARATION_INFERRED_TYPE is the type which should be used for the initial type declaration
- ClassNode oldDIT = (ClassNode) exp.getNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE);
+ ClassNode oldDIT = (ClassNode) exp.getNodeMetaData(DECLARATION_INFERRED_TYPE);
if (oldDIT != null) {
- exp.putNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE, cn == null ? oldDIT : lowestUpperBound(oldDIT, cn));
+ exp.putNodeMetaData(DECLARATION_INFERRED_TYPE, cn == null ? oldDIT : lowestUpperBound(oldDIT, cn));
} else {
- exp.putNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE, cn == null ? null : lowestUpperBound(oldValue, cn));
+ exp.putNodeMetaData(DECLARATION_INFERRED_TYPE, cn == null ? null : lowestUpperBound(oldValue, cn));
}
}
if (exp instanceof VariableExpression) {
@@ -5004,7 +4952,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
return result;
}
} else if (exp instanceof MethodCall) {
- MethodNode target = (MethodNode) exp.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+ MethodNode target = (MethodNode) exp.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
if (target != null) {
return getType(target);
}
@@ -5097,7 +5045,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (!(node instanceof ClosureExpression)) {
throw new IllegalArgumentException("Storing inferred return type is only allowed on closures but found " + node.getClass());
}
- return (ClassNode) node.putNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE, type);
+ return (ClassNode) node.putNodeMetaData(INFERRED_RETURN_TYPE, type);
}
/**
@@ -5108,7 +5056,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
* @return the inferred type, as stored on node metadata.
*/
protected ClassNode getInferredReturnType(final ASTNode exp) {
- return (ClassNode) exp.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE);
+ return (ClassNode) exp.getNodeMetaData(INFERRED_RETURN_TYPE);
}
protected ClassNode inferListExpressionType(final ListExpression list) {
@@ -5665,7 +5613,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
List<ClassNode> classNodes = typeCheckingContext.closureSharedVariablesAssignmentTypes.get(var);
if (classNodes != null && classNodes.size() > 1) {
ClassNode lub = lowestUpperBound(classNodes);
- MethodNode methodNode = (MethodNode) call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+ MethodNode methodNode = (MethodNode) call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
// we must check that such a method exists on the LUB
Parameter[] parameters = methodNode.getParameters();
ClassNode[] params = extractTypesFromParameters(parameters);
diff --git a/src/test/groovy/bugs/Groovy7996.groovy b/src/test/groovy/bugs/Groovy7996.groovy
new file mode 100644
index 0000000..fc0872f
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy7996.groovy
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.bugs
+
+import groovy.test.NotYetImplemented
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy7996 {
+
+ @Test @NotYetImplemented
+ void testFieldAccessFromClosure1() {
+ assertScript '''
+ class Foo {
+ def build(@DelegatesTo(value=Foo, strategy=Closure.DELEGATE_FIRST) Closure block) {
+ this.with(block)
+ }
+
+ def propertyMissing(String name) {
+ 'whatever'
+ }
+ }
+
+ @groovy.transform.CompileStatic
+ class Bar {
+ protected List bar = []
+
+ boolean doStuff() {
+ Foo foo = new Foo()
+ foo.build {
+ bar.isEmpty() // "ClassCastException: java.lang.String cannot be cast to java.util.List"
+ }
+ }
+ }
+
+ assert new Bar().doStuff()
+ '''
+ }
+
+ @Test
+ void testFieldAccessFromClosure2() {
+ assertScript '''
+ class Foo {
+ def build(@DelegatesTo(value=Foo, strategy=Closure.DELEGATE_FIRST) Closure block) {
+ this.with(block)
+ }
+
+ def propertyMissing(String name) {
+ 'whatever'
+ }
+ }
+
+ @groovy.transform.CompileStatic
+ class Bar {
+ protected List bar = []
+
+ boolean doStuff() {
+ Foo foo = new Foo()
+ foo.build {
+ owner.bar.isEmpty()
+ }
+ }
+ }
+
+ assert new Bar().doStuff()
+ '''
+ }
+
+ @Test
+ void testFieldAccessFromClosure3() {
+ assertScript '''
+ class Foo {
+ def build(@DelegatesTo(value=Foo, strategy=Closure.DELEGATE_FIRST) Closure block) {
+ this.with(block)
+ }
+
+ def propertyMissing(String name) {
+ 'whatever'
+ }
+ }
+
+ @groovy.transform.CompileStatic
+ class Bar {
+ protected List bar = []
+
+ boolean doStuff() {
+ Foo foo = new Foo()
+ foo.build {
+ thisObject.bar.isEmpty()
+ }
+ }
+ }
+
+ assert new Bar().doStuff()
+ '''
+ }
+
+ @Test
+ void testFieldAccessFromClosure4() {
+ assertScript '''
+ class Foo {
+ def build(@DelegatesTo(value=Foo, strategy=Closure.OWNER_FIRST) Closure block) {
+ block.delegate = this
+ return block.call()
+ }
+
+ def propertyMissing(String name) {
+ 'whatever'
+ }
+ }
+
+ @groovy.transform.CompileStatic
+ class Bar {
+ protected List bar = []
+
+ boolean doStuff() {
+ Foo foo = new Foo()
+ foo.build {
+ bar.isEmpty()
+ }
+ }
+ }
+
+ assert new Bar().doStuff()
+ '''
+ }
+
+ @Test // GROOVY-7687
+ void testFieldAccessFromNestedClosure1() {
+ assertScript '''
+ @groovy.transform.CompileStatic
+ class BugTest {
+ static class Foo {
+ public List<String> messages = ['hello', 'world']
+ }
+
+ void interactions(Foo foo, @DelegatesTo(Foo) Closure closure) {
+ closure.delegate = foo
+ closure()
+ }
+
+ void execute() {
+ interactions(new Foo()) {
+ messages.each { it.contains('o') }
+ }
+ }
+ }
+ new BugTest().execute()
+ '''
+ }
+
+ @Test // GROOVY-8073
+ void testDelegatePropertyAccessFromClosure() {
+ assertScript '''
+ @groovy.transform.CompileStatic
+ class Main {
+ static main(args) {
+ def map = [a: 1, b: 2]
+ map.with {
+ assert a == 1
+ }
+ }
+ }
+ '''
+ }
+}
diff --git a/src/test/groovy/bugs/Groovy7996Bug.groovy b/src/test/groovy/bugs/Groovy7996Bug.groovy
deleted file mode 100644
index 12452e3..0000000
--- a/src/test/groovy/bugs/Groovy7996Bug.groovy
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package groovy.bugs
-
-import groovy.test.GroovyTestCase
-
-class Groovy7996Bug extends GroovyTestCase {
- void testPropertyAccessFromInnerClass() {
- assertScript '''
- class Foo7996 {
- Object propertyMissing(String name) {
- return "stuff"
- }
-
- def build(Closure callable) {
- this.with(callable)
- }
- }
-
- @groovy.transform.CompileStatic
- class Bar7996 {
- protected List bar = []
-
- boolean doStuff() {
- Foo7996 foo = new Foo7996()
- foo.build {
- bar.isEmpty()
- }
- }
- }
-
- assert new Bar7996().doStuff()
- '''
- }
-
- // GROOVY-7687
- void testCompileStaticWithNestedClosuresBug() {
- assertScript '''
- @groovy.transform.CompileStatic
- class BugTest {
- static class Foo {
- public List<String> messages = Arrays.asList("hello", "world")
- }
-
- void interactions(Foo foo, @DelegatesTo(Foo) Closure closure) {
- closure.delegate = foo
- closure()
- }
-
- void execute() {
- interactions(new Foo()) {
- messages.each{ it.contains('o') }
- }
- }
- }
- new BugTest().execute()
- '''
- }
-
- // GROOVY-8073
- void testCompileStaticMapInsideWithBug() {
- assertScript '''
- @groovy.transform.CompileStatic
- class Main {
- static void main(String[] args) {
- def map = [a: 1, b: 2]
- map.with {
- assert a == 1
- }
- }
- }
- '''
- }
-}