From commits-return-9491-archive-asf-public=cust-asf.ponee.io@groovy.apache.org Mon Oct 28 16:50:46 2019 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [207.244.88.153]) by mx-eu-01.ponee.io (Postfix) with SMTP id 9582C180674 for ; Mon, 28 Oct 2019 17:50:45 +0100 (CET) Received: (qmail 55287 invoked by uid 500); 28 Oct 2019 16:50:44 -0000 Mailing-List: contact commits-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@groovy.apache.org Delivered-To: mailing list commits@groovy.apache.org Received: (qmail 55223 invoked by uid 99); 28 Oct 2019 16:50:44 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 28 Oct 2019 16:50:44 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id A56AE80626; Mon, 28 Oct 2019 16:50:44 +0000 (UTC) Date: Mon, 28 Oct 2019 16:50:47 +0000 To: "commits@groovy.apache.org" Subject: [groovy] 03/08: GROOVY-7996: add test cases and roll back initial changes MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit From: sunlan@apache.org In-Reply-To: <157228144413.15154.2176118564382887290@gitbox.apache.org> References: <157228144413.15154.2176118564382887290@gitbox.apache.org> X-Git-Host: gitbox.apache.org X-Git-Repo: groovy X-Git-Refname: refs/heads/GROOVY_3_0_X X-Git-Reftype: branch X-Git-Rev: 1af5ca8cf857d9065ea88e1c558ad39abbcada39 X-Git-NotificationType: diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated Message-Id: <20191028165044.A56AE80626@gitbox.apache.org> This is an automated email from the ASF dual-hosted git repository. sunlan pushed a commit to branch GROOVY_3_0_X in repository https://gitbox.apache.org/repos/asf/groovy.git commit 1af5ca8cf857d9065ea88e1c558ad39abbcada39 Author: Eric Milles AuthorDate: Sun Oct 27 10:43:20 2019 -0500 GROOVY-7996: add test cases and roll back initial changes GROOVY-9288: resolved by rollback (cherry picked from commit 1b9501f7788d7c767fc09e85f2850b3c9de29766) --- .../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 list = current.getNodeMetaData(StaticTypesMarker.SUPER_MOP_METHOD_REQUIRED); + LinkedList list = current.getNodeMetaData(SUPER_MOP_METHOD_REQUIRED); if (list == null) { list = new LinkedList(); - 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> receivers = new LinkedList>(); + List> receivers = new LinkedList<>(); List> owners = makeOwnerList(objectExpression); addReceivers(receivers, owners, pexp.isImplicitThis()); String capName = capitalize(propertyName); boolean isAttributeExpression = pexp instanceof AttributeExpression; - HashSet handledNodes = new HashSet(); + HashSet handledNodes = new HashSet<>(); for (Receiver receiver : receivers) { ClassNode testClass = receiver.getType(); - LinkedList queue = new LinkedList(); + LinkedList 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 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 dgmReceivers = new ArrayList(2); + // GROOVY-5568: the property may be defined by DGM + List dgmReceivers = new ArrayList<>(2); dgmReceivers.add(testClass); if (isPrimitiveType(testClass)) dgmReceivers.add(getWrapper(testClass)); for (ClassNode dgmReceiver : dgmReceivers) { List 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 methodNodes = chooseBestMethod(dgmReceiver, methods, ClassNode.EMPTY_ARRAY); - if (methodNodes.size() == 1) { - MethodNode getter = methodNodes.get(0); + List 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> receivers = new LinkedList>(); - List> owners = makeOwnerList(objectExpression); - addReceivers(receivers, owners, pexp.isImplicitThis()); - - String capName = capitalize(propertyName); - boolean isAttributeExpression = pexp instanceof AttributeExpression; - - for (Receiver receiver : receivers) { - ClassNode testClass = receiver.getType(); - LinkedList queue = new LinkedList(); - 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 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 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 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 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 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 - } - } - } - ''' - } -}