This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY_2_5_X in repository https://gitbox.apache.org/repos/asf/groovy.git
commit ccfd6053d1b52488577bcdb2ee4693b1cf907f10 Author: Eric Milles <[email protected]> AuthorDate: Sun Mar 12 11:50:03 2023 -0500 sync with `3_0_X` --- .../codehaus/groovy/ast/tools/GenericsUtils.java | 22 +- .../classgen/asm/sc/StaticInvocationWriter.java | 17 +- .../classgen/asm/sc/StaticTypesCallSiteWriter.java | 6 +- .../codehaus/groovy/control/CompilationUnit.java | 19 +- .../codehaus/groovy/control/ProcessingUnit.java | 4 +- .../codehaus/groovy/control/ResolveVisitor.java | 2 +- .../transformers/BinaryExpressionTransformer.java | 3 +- .../transform/stc/StaticTypeCheckingSupport.java | 382 ++++++++++----------- .../transform/stc/StaticTypeCheckingVisitor.java | 2 +- .../transform/trait/TraitASTTransformation.java | 7 +- .../groovy/transform/stc/GenericsSTCTest.groovy | 18 +- 11 files changed, 235 insertions(+), 247 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java index 8c4906dcb0..9d0d5409b9 100644 --- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java +++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java @@ -402,28 +402,30 @@ public class GenericsUtils { newgTypes = new GenericsType[oldgTypes.length]; for (int i = 0; i < newgTypes.length; i++) { GenericsType oldgType = oldgTypes[i]; - if (oldgType.isPlaceholder()) { - if (genericsSpec.get(oldgType.getName()) != null) { - newgTypes[i] = new GenericsType(genericsSpec.get(oldgType.getName())); - } else { - newgTypes[i] = new GenericsType(ClassHelper.OBJECT_TYPE); - } - } else if (oldgType.isWildcard()) { - ClassNode oldLower = oldgType.getLowerBound(); - ClassNode lower = oldLower != null ? correctToGenericsSpecRecurse(genericsSpec, oldLower, exclusions) : null; + if (oldgType.isWildcard()) { ClassNode[] oldUpper = oldgType.getUpperBounds(); ClassNode[] upper = null; if (oldUpper != null) { + // correct "? extends T" or "? extends T & I" upper = new ClassNode[oldUpper.length]; for (int j = 0; j < oldUpper.length; j++) { upper[j] = correctToGenericsSpecRecurse(genericsSpec, oldUpper[j], exclusions); } } + ClassNode oldLower = oldgType.getLowerBound(); + ClassNode lower = null; + if (oldLower != null) { + // correct "? super T" + lower = correctToGenericsSpecRecurse(genericsSpec, oldLower, exclusions); + } GenericsType fixed = new GenericsType(oldgType.getType(), upper, lower); - fixed.setName(oldgType.getName()); fixed.setWildcard(true); newgTypes[i] = fixed; + } else if (oldgType.isPlaceholder()) { + // correct "T" + newgTypes[i] = new GenericsType(genericsSpec.getOrDefault(oldgType.getName(), ClassHelper.OBJECT_TYPE)); } else { + // correct "List<T>", etc. newgTypes[i] = new GenericsType(correctToGenericsSpecRecurse(genericsSpec, correctToGenericsSpec(genericsSpec, oldgType), exclusions)); } } diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java index af0b11f455..53aa8261db 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java @@ -22,6 +22,7 @@ import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.EnumConstantClassNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.GroovyCodeVisitor; import org.codehaus.groovy.ast.InnerClassNode; @@ -739,9 +740,9 @@ public class StaticInvocationWriter extends InvocationWriter { controller.getOperandStack().doGroovyCast(type); } if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(topOperand, type)) return; - controller.getMethodVisitor().visitTypeInsn(CHECKCAST, type.isArray() ? - BytecodeHelper.getTypeDescription(type) : - BytecodeHelper.getClassInternalName(type.getName())); + controller.getMethodVisitor().visitTypeInsn(CHECKCAST, type.isArray() + ? BytecodeHelper.getTypeDescription(type) + : BytecodeHelper.getClassInternalName(type.getName())); controller.getOperandStack().replace(type); } } @@ -758,9 +759,11 @@ public class StaticInvocationWriter extends InvocationWriter { type = ClassHelper.getWrapper(type); } ClassNode declaringClass = target.getDeclaringClass(); - if (type.getClass() != ClassNode.class - && type.getClass() != InnerClassNode.class - && type.getClass() != DecompiledClassNode.class) { + Class<?> typeClass = type.getClass(); + if (typeClass != ClassNode.class + && typeClass != InnerClassNode.class + && typeClass != DecompiledClassNode.class + && typeClass != EnumConstantClassNode.class) { type = declaringClass; // ex: LUB type } if (ClassHelper.OBJECT_TYPE.equals(declaringClass)) { @@ -780,7 +783,7 @@ public class StaticInvocationWriter extends InvocationWriter { } @Override - protected boolean makeCachedCall(Expression origin, ClassExpression sender, Expression receiver, Expression message, Expression arguments, MethodCallerMultiAdapter adapter, boolean safe, boolean spreadSafe, boolean implicitThis, boolean containsSpreadExpression) { + protected boolean makeCachedCall(final Expression origin, final ClassExpression sender, final Expression receiver, final Expression message, final Expression arguments, final MethodCallerMultiAdapter adapter, final boolean safe, final boolean spreadSafe, final boolean implicitThis, final boolean containsSpreadExpression) { return false; } } diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java index 89ea087fb6..16e3033809 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java @@ -146,9 +146,7 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes controller.getMethodVisitor().visitInsn(ARRAYLENGTH); controller.getOperandStack().replace(int_TYPE); return; - } else if ( - (receiverType.implementsInterface(COLLECTION_TYPE) - || COLLECTION_TYPE.equals(receiverType)) && ("size".equals(propertyName) || "length".equals(propertyName))) { + } else if (("size".equals(propertyName) || "length".equals(propertyName)) && isOrImplements(receiverType, COLLECTION_TYPE)) { MethodCallExpression expr = new MethodCallExpression( receiver, "size", @@ -164,7 +162,7 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes boolean isStaticProperty = receiver instanceof ClassExpression && (receiverType.isDerivedFrom(receiver.getType()) || receiverType.implementsInterface(receiver.getType())); - if (!isStaticProperty && (receiverType.implementsInterface(MAP_TYPE) || MAP_TYPE.equals(receiverType))) { + if (!isStaticProperty && isOrImplements(receiverType, MAP_TYPE)) { // for maps, replace map.foo with map.get('foo') writeMapDotProperty(receiver, propertyName, safe); return; diff --git a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java index 7500ab1615..88aa216779 100644 --- a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java +++ b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java @@ -459,7 +459,7 @@ public class CompilationUnit extends ProcessingUnit { */ public SourceUnit addSource(SourceUnit source) { String name = source.getName(); - source.setClassLoader(this.classLoader); + source.setClassLoader(getClassLoader()); for (SourceUnit su : queuedSources) { if (name.equals(su.getName())) return su; } @@ -681,7 +681,6 @@ public class CompilationUnit extends ProcessingUnit { resolveVisitor.setClassNodeResolver(classNodeResolver); resolveVisitor.startResolving(node, source); } - } }; @@ -697,11 +696,11 @@ public class CompilationUnit extends ProcessingUnit { private final SourceUnitOperation convert = new SourceUnitOperation() { public void call(SourceUnit source) throws CompilationFailedException { source.convert(); - CompilationUnit.this.ast.addModule(source.getAST()); - + // add module node to compile unit + getAST().addModule(source.getAST()); - if (CompilationUnit.this.progressCallback != null) { - CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase); + if (progressCallback != null) { + progressCallback.call(source, getPhase()); } } }; @@ -789,8 +788,8 @@ public class CompilationUnit extends ProcessingUnit { GroovyClassVisitor visitor = verifier; try { visitor.visitClass(classNode); - } catch (GroovyRuntimeException rpe) { - getErrorCollector().addError(new SyntaxException(rpe.getMessage(), rpe.getNode()), source); + } catch (GroovyRuntimeException gre) { + getErrorCollector().addError(new SyntaxException(gre.getMessage(), gre.getNode()), source); } visitor = new LabelVerifier(source); @@ -973,13 +972,13 @@ public class CompilationUnit extends ProcessingUnit { private static int getSuperClassCount(ClassNode element) { int count = 0; while (element != null) { - count++; + count += 1; element = element.getSuperClass(); } return count; } - private int getSuperInterfaceCount(ClassNode element) { + private static int getSuperInterfaceCount(ClassNode element) { int count = 1; ClassNode[] interfaces = element.getInterfaces(); for (ClassNode anInterface : interfaces) { diff --git a/src/main/java/org/codehaus/groovy/control/ProcessingUnit.java b/src/main/java/org/codehaus/groovy/control/ProcessingUnit.java index ed196db92b..77fc9f53a5 100644 --- a/src/main/java/org/codehaus/groovy/control/ProcessingUnit.java +++ b/src/main/java/org/codehaus/groovy/control/ProcessingUnit.java @@ -91,8 +91,8 @@ public abstract class ProcessingUnit { * Sets the class loader for use by this ProcessingUnit. */ public void setClassLoader(final GroovyClassLoader loader) { - // ClassLoaders should only be created inside a doPrivileged block in case - // this method is invoked by code that does not have security permissions. + // ClassLoaders should only be created inside doPrivileged block in case + // this method is invoked by code that does not have security permission this.classLoader = loader != null ? loader : AccessController.doPrivileged(new PrivilegedAction<GroovyClassLoader>() { public GroovyClassLoader run() { ClassLoader parent = Thread.currentThread().getContextClassLoader(); diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java index 915a242c28..2870129056 100644 --- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java +++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java @@ -1572,7 +1572,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer { GenericsTypeName gtn = new GenericsTypeName(name); ClassNode[] bounds = type.getUpperBounds(); boolean isWild = QUESTION_MARK.equals(name); - boolean toDealWithGenerics = 0 == level || (level > 0 && null != genericParameterNames.get(gtn)); + boolean toDealWithGenerics = 0 == level || (level > 0 && genericParameterNames.containsKey(gtn)); if (bounds != null) { boolean nameAdded = false; diff --git a/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java b/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java index c1b237ed92..1f22a82144 100644 --- a/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java +++ b/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java @@ -179,7 +179,6 @@ public class BinaryExpressionTransformer { } } } - boolean isAssignment = StaticTypeCheckingSupport.isAssignment(operationType); MethodCallExpression call; MethodNode node = (MethodNode) list[0]; String name = (String) list[1]; @@ -209,7 +208,7 @@ public class BinaryExpressionTransformer { call.setImplicitThis(false); } call.setSourcePosition(bin); - if (!isAssignment) return call; + if (!StaticTypeCheckingSupport.isAssignment(operationType)) return call; // case of +=, -=, /=, ... // the method represents the operation type only, and we must add an assignment return new BinaryExpression(left, Token.newSymbol("=", operation.getStartLine(), operation.getStartColumn()), call); diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java index cb024a3b4c..77440046dc 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java @@ -88,6 +88,7 @@ import static org.codehaus.groovy.ast.ClassHelper.GSTRING_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Integer_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Long_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Number_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.OBJECT; import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE; import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE; import static org.codehaus.groovy.ast.ClassHelper.Short_TYPE; @@ -164,7 +165,6 @@ public abstract class StaticTypeCheckingSupport { protected static final ClassNode Collection_TYPE = makeWithoutCaching(Collection.class); protected static final ClassNode Deprecated_TYPE = makeWithoutCaching(Deprecated.class); - protected static final ExtensionMethodCache EXTENSION_METHOD_CACHE = new ExtensionMethodCache(); protected static final Map<ClassNode, Integer> NUMBER_TYPES = Collections.unmodifiableMap( new HashMap<ClassNode, Integer>() { private static final long serialVersionUID = 8841951852732042766L; @@ -224,6 +224,7 @@ public abstract class StaticTypeCheckingSupport { * in an ambiguous method selection for similar methods. */ protected static final Comparator<MethodNode> DGM_METHOD_NODE_COMPARATOR = new Comparator<MethodNode>() { + @Override public int compare(final MethodNode o1, final MethodNode o2) { if (o1.getName().equals(o2.getName())) { Parameter[] o1ps = o1.getParameters(); @@ -247,7 +248,9 @@ public abstract class StaticTypeCheckingSupport { } }; - public static void clearExtensionMethodCache(ClassLoader loader) { + protected static final ExtensionMethodCache EXTENSION_METHOD_CACHE = new ExtensionMethodCache(); + + public static void clearExtensionMethodCache(final ClassLoader loader) { EXTENSION_METHOD_CACHE.cache.remove(loader); } @@ -257,7 +260,7 @@ public abstract class StaticTypeCheckingSupport { * @param expression an expression * @return true for array access expressions */ - protected static boolean isArrayAccessExpression(Expression expression) { + protected static boolean isArrayAccessExpression(final Expression expression) { return expression instanceof BinaryExpression && isArrayOp(((BinaryExpression) expression).getOperation().getType()); } @@ -265,18 +268,18 @@ public abstract class StaticTypeCheckingSupport { * Called on method call checks in order to determine if a method call corresponds to the * idiomatic o.with { ... } structure * - * @param name name of the method called - * @param callArguments arguments of the method + * @param name name of the method called + * @param arguments method call arguments * @return true if the name is "with" and arguments consist of a single closure */ - public static boolean isWithCall(final String name, final Expression callArguments) { - boolean isWithCall = "with".equals(name) && callArguments instanceof ArgumentListExpression; - if (isWithCall) { - ArgumentListExpression argList = (ArgumentListExpression) callArguments; - List<Expression> expressions = argList.getExpressions(); - isWithCall = expressions.size() == 1 && expressions.get(0) instanceof ClosureExpression; + public static boolean isWithCall(final String name, final Expression arguments) { + if ("with".equals(name) && arguments instanceof ArgumentListExpression) { + List<Expression> args = ((ArgumentListExpression) arguments).getExpressions(); + if (args.size() == 1 && args.get(0) instanceof ClosureExpression) { + return true; + } } - return isWithCall; + return false; } /** @@ -285,40 +288,40 @@ public abstract class StaticTypeCheckingSupport { * @param ve a variable expression * @return the target variable */ - protected static Variable findTargetVariable(VariableExpression ve) { - final Variable accessedVariable = ve.getAccessedVariable() != null ? ve.getAccessedVariable() : ve; - if (accessedVariable != ve) { - if (accessedVariable instanceof VariableExpression) + protected static Variable findTargetVariable(final VariableExpression ve) { + Variable accessedVariable = ve.getAccessedVariable(); + if (accessedVariable != null && accessedVariable != ve) { + if (accessedVariable instanceof VariableExpression) { return findTargetVariable((VariableExpression) accessedVariable); + } + return accessedVariable; } - return accessedVariable; + return ve; } - /** * @deprecated Use {@link #findDGMMethodsForClassNode(ClassLoader, ClassNode, String)} instead */ @Deprecated - protected static Set<MethodNode> findDGMMethodsForClassNode(ClassNode clazz, String name) { + protected static Set<MethodNode> findDGMMethodsForClassNode(final ClassNode clazz, final String name) { return findDGMMethodsForClassNode(MetaClassRegistryImpl.class.getClassLoader(), clazz, name); } - protected static Set<MethodNode> findDGMMethodsForClassNode(final ClassLoader loader, ClassNode clazz, String name) { + protected static Set<MethodNode> findDGMMethodsForClassNode(final ClassLoader loader, final ClassNode clazz, final String name) { TreeSet<MethodNode> accumulator = new TreeSet<MethodNode>(DGM_METHOD_NODE_COMPARATOR); findDGMMethodsForClassNode(loader, clazz, name, accumulator); return accumulator; } - /** * @deprecated Use {@link #findDGMMethodsForClassNode(ClassLoader, ClassNode, String, TreeSet)} instead */ @Deprecated - protected static void findDGMMethodsForClassNode(ClassNode clazz, String name, TreeSet<MethodNode> accumulator) { + protected static void findDGMMethodsForClassNode(final ClassNode clazz, final String name, final TreeSet<MethodNode> accumulator) { findDGMMethodsForClassNode(MetaClassRegistryImpl.class.getClassLoader(), clazz, name, accumulator); } - protected static void findDGMMethodsForClassNode(final ClassLoader loader, ClassNode clazz, String name, TreeSet<MethodNode> accumulator) { + protected static void findDGMMethodsForClassNode(final ClassLoader loader, final ClassNode clazz, final String name, final TreeSet<MethodNode> accumulator) { List<MethodNode> fromDGM = EXTENSION_METHOD_CACHE.getExtensionMethods(loader).get(clazz.getName()); if (fromDGM != null) { for (MethodNode node : fromDGM) { @@ -348,24 +351,25 @@ public abstract class StaticTypeCheckingSupport { /** * Checks that arguments and parameter types match. * - * @param params method parameters - * @param args type arguments * @return -1 if arguments do not match, 0 if arguments are of the exact type and > 0 when one or more argument is * not of the exact type but still match */ - public static int allParametersAndArgumentsMatch(Parameter[] params, ClassNode[] args) { - if (params == null) { - params = Parameter.EMPTY_ARRAY; + public static int allParametersAndArgumentsMatch(Parameter[] parameters, final ClassNode[] argumentTypes) { + if (parameters == null) { + parameters = Parameter.EMPTY_ARRAY; } int dist = 0; - if (args.length < params.length) return -1; + if (argumentTypes.length < parameters.length) { + return -1; + } // we already know there are at least params.length elements in both arrays - for (int i = 0; i < params.length; i++) { - ClassNode paramType = params[i].getType(); - ClassNode argType = args[i]; - if (!isAssignableTo(argType, paramType)) return -1; - else { - if (!paramType.equals(argType)) dist += getDistance(argType, paramType); + for (int i = 0, n = parameters.length; i < n; i += 1) { + ClassNode paramType = parameters[i].getType(); + ClassNode argType = argumentTypes[i]; + if (!isAssignableTo(argType, paramType)) { + return -1; + } else if (!paramType.equals(argType)) { + dist += getDistance(argType, paramType); } } return dist; @@ -375,19 +379,16 @@ public abstract class StaticTypeCheckingSupport { * Checks that arguments and parameter types match, expecting that the number of parameters is strictly greater * than the number of arguments, allowing possible inclusion of default parameters. * - * @param params method parameters - * @param args type arguments * @return -1 if arguments do not match, 0 if arguments are of the exact type and >0 when one or more argument is * not of the exact type but still match */ - static int allParametersAndArgumentsMatchWithDefaultParams(Parameter[] params, ClassNode[] args) { + static int allParametersAndArgumentsMatchWithDefaultParams(final Parameter[] parameters, final ClassNode[] argumentTypes) { int dist = 0; ClassNode ptype = null; - // we already know the lengths are equal - for (int i = 0, j = 0; i < params.length; i++) { - Parameter param = params[i]; + for (int i = 0, j = 0, n = parameters.length; i < n; i += 1) { + Parameter param = parameters[i]; ClassNode paramType = param.getType(); - ClassNode arg = j >= args.length ? null : args[j]; + ClassNode arg = (j >= argumentTypes.length ? null : argumentTypes[j]); if (arg == null || !isAssignableTo(arg, paramType)) { if (!param.hasInitialExpression() && (ptype == null || !ptype.equals(paramType))) { return -1; // no default value @@ -395,8 +396,10 @@ public abstract class StaticTypeCheckingSupport { // a default value exists, we can skip this param ptype = null; } else { - j++; - if (!paramType.equals(arg)) dist += getDistance(arg, paramType); + j += 1; + if (!paramType.equals(arg)) { + dist += getDistance(arg, paramType); + } if (param.hasInitialExpression()) { ptype = arg; } else { @@ -410,20 +413,18 @@ public abstract class StaticTypeCheckingSupport { /** * Checks that excess arguments match the vararg signature parameter. * - * @param params - * @param args * @return -1 if no match, 0 if all arguments matches the vararg type and >0 if one or more vararg argument is * assignable to the vararg type, but still not an exact match */ - static int excessArgumentsMatchesVargsParameter(Parameter[] params, ClassNode[] args) { + static int excessArgumentsMatchesVargsParameter(final Parameter[] parameters, final ClassNode[] argumentTypes) { // we already know parameter length is bigger zero and last is a vargs // the excess arguments are all put in an array for the vargs call // so check against the component type int dist = 0; - ClassNode vargsBase = params[params.length - 1].getType().getComponentType(); - for (int i = params.length; i < args.length; i++) { - if (!isAssignableTo(args[i], vargsBase)) return -1; - else dist += getClassDistance(vargsBase, args[i]); + ClassNode vargsBase = parameters[parameters.length - 1].getType().getComponentType(); + for (int i = parameters.length; i < argumentTypes.length; i += 1) { + if (!isAssignableTo(argumentTypes[i], vargsBase)) return -1; + else dist += getClassDistance(vargsBase, argumentTypes[i]); } return dist; } @@ -431,8 +432,6 @@ public abstract class StaticTypeCheckingSupport { /** * Checks if the last argument matches the vararg type. * - * @param params - * @param args * @return -1 if no match, 0 if the last argument is exactly the vararg type and 1 if of an assignable type */ static int lastArgMatchesVarg(final Parameter[] parameters, final ClassNode... argumentTypes) { @@ -454,15 +453,12 @@ public abstract class StaticTypeCheckingSupport { * Checks if a class node is assignable to another. This is used for example in * assignment checks where you want to verify that the assignment is valid. * - * @param type - * @param toBeAssignedTo * @return true if the class node is assignable to the other class node, false otherwise */ static boolean isAssignableTo(ClassNode type, ClassNode toBeAssignedTo) { - if (UNKNOWN_PARAMETER_TYPE == type) return true; - if (type == toBeAssignedTo) return true; - if (isPrimitiveType(toBeAssignedTo)) toBeAssignedTo = getWrapper(toBeAssignedTo); + if (type == toBeAssignedTo || type == UNKNOWN_PARAMETER_TYPE) return true; if (isPrimitiveType(type)) type = getWrapper(type); + if (isPrimitiveType(toBeAssignedTo)) toBeAssignedTo = getWrapper(toBeAssignedTo); if (NUMBER_TYPES.containsKey(type.redirect()) && NUMBER_TYPES.containsKey(toBeAssignedTo.redirect())) { return NUMBER_TYPES.get(type.redirect()) <= NUMBER_TYPES.get(toBeAssignedTo.redirect()); } @@ -478,9 +474,7 @@ public abstract class StaticTypeCheckingSupport { } if (implementsInterfaceOrIsSubclassOf(type, toBeAssignedTo)) { if (toBeAssignedTo.equals(OBJECT_TYPE)) return true; - if (toBeAssignedTo.isUsingGenerics()) { - // perform additional check on generics - // ? extends toBeAssignedTo + if (toBeAssignedTo.isUsingGenerics()) { // perform additional checks on generics GenericsType gt = GenericsUtils.buildWildcardType(toBeAssignedTo); return gt.isCompatibleWith(type); } @@ -492,33 +486,30 @@ public abstract class StaticTypeCheckingSupport { return false; } - static boolean isVargs(Parameter[] params) { - if (params.length == 0) return false; - if (params[params.length - 1].getType().isArray()) return true; - return false; + static boolean isVargs(final Parameter[] parameters) { + if (parameters == null || parameters.length == 0) return false; + return (parameters[parameters.length - 1].getType().isArray()); } - public static boolean isCompareToBoolean(int op) { - return op == COMPARE_GREATER_THAN || - op == COMPARE_GREATER_THAN_EQUAL || - op == COMPARE_LESS_THAN || - op == COMPARE_LESS_THAN_EQUAL; + public static boolean isCompareToBoolean(final int op) { + return op == COMPARE_LESS_THAN || op == COMPARE_LESS_THAN_EQUAL + || op == COMPARE_GREATER_THAN || op == COMPARE_GREATER_THAN_EQUAL; } - static boolean isArrayOp(int op) { + static boolean isArrayOp(final int op) { return op == LEFT_SQUARE_BRACKET; } - static boolean isBoolIntrinsicOp(int op) { + static boolean isBoolIntrinsicOp(final int op) { return op == LOGICAL_AND || op == LOGICAL_OR || op == COMPARE_NOT_IDENTICAL || op == COMPARE_IDENTICAL || op == MATCH_REGEX || op == KEYWORD_INSTANCEOF; } - static boolean isPowerOperator(int op) { + static boolean isPowerOperator(final int op) { return op == POWER || op == POWER_EQUAL; } - static String getOperationName(int op) { + static String getOperationName(final int op) { switch (op) { case COMPARE_EQUAL: case COMPARE_NOT_EQUAL: @@ -594,7 +585,7 @@ public abstract class StaticTypeCheckingSupport { } } - static boolean isShiftOperation(String name) { + static boolean isShiftOperation(final String name) { return "leftShift".equals(name) || "rightShift".equals(name) || "rightShiftUnsigned".equals(name); } @@ -603,7 +594,7 @@ public abstract class StaticTypeCheckingSupport { * operation "left op right" will have a result in the same type class In Groovy on numbers that is +,-,* as well as * their variants with equals. */ - static boolean isOperationInGroup(int op) { + static boolean isOperationInGroup(final int op) { switch (op) { case PLUS: case PLUS_EQUAL: @@ -617,7 +608,7 @@ public abstract class StaticTypeCheckingSupport { } } - static boolean isBitOperator(int op) { + static boolean isBitOperator(final int op) { switch (op) { case BITWISE_OR_EQUAL: case BITWISE_OR: @@ -631,7 +622,7 @@ public abstract class StaticTypeCheckingSupport { } } - public static boolean isAssignment(int op) { + public static boolean isAssignment(final int op) { switch (op) { case ASSIGN: case LOGICAL_OR_EQUAL: @@ -727,10 +718,9 @@ public abstract class StaticTypeCheckingSupport { return true; } - // if right is array, map or collection we try invoking the - // constructor + // if right is array, map or collection we try invoking the constructor if (allowConstructorCoercion && isGroovyConstructorCompatible(rightExpression)) { - //TODO: in case of the array we could maybe make a partial check + // TODO: in case of the array we could maybe make a partial check if (leftRedirect.isArray() && rightRedirect.isArray()) { return checkCompatibleAssignmentTypes(leftRedirect.getComponentType(), rightRedirect.getComponentType()); } else if (rightRedirect.isArray() && !leftRedirect.isArray()) { @@ -787,18 +777,15 @@ public abstract class StaticTypeCheckingSupport { * @return true if it's an Object, String, boolean, Boolean or Class. */ public static boolean isWildcardLeftHandSide(final ClassNode node) { - if (OBJECT_TYPE.equals(node) || - STRING_TYPE.equals(node) || - boolean_TYPE.equals(node) || - Boolean_TYPE.equals(node) || - CLASS_Type.equals(node)) { - return true; - } - return false; + return (OBJECT_TYPE.equals(node) + || STRING_TYPE.equals(node) + || boolean_TYPE.equals(node) + || Boolean_TYPE.equals(node) + || CLASS_Type.equals(node)); } - public static boolean isBeingCompiled(ClassNode node) { - return node.getCompileUnit() != null; + public static boolean isBeingCompiled(final ClassNode node) { + return (node.getCompileUnit() != null); } @Deprecated @@ -995,8 +982,7 @@ public abstract class StaticTypeCheckingSupport { if (c.equals(interfaceClass)) return 0; ClassNode[] interfaces = c.getInterfaces(); int max = -1; - for (int i = 0; i < interfaces.length; i++) { - final ClassNode anInterface = interfaces[i]; + for (ClassNode anInterface : interfaces) { int sub = getMaximumInterfaceDistance(anInterface, interfaceClass); // we need to keep the -1 to track the mismatch, a +1 // by any means could let it look like a direct match @@ -1046,25 +1032,8 @@ public abstract class StaticTypeCheckingSupport { * @param node the node to test * @return true if it is using any placeholder in generics types */ - public static boolean isUsingUncheckedGenerics(ClassNode node) { - if (node.isArray()) return isUsingUncheckedGenerics(node.getComponentType()); - if (node.isUsingGenerics()) { - GenericsType[] genericsTypes = node.getGenericsTypes(); - if (genericsTypes != null) { - for (GenericsType genericsType : genericsTypes) { - if (genericsType.isPlaceholder()) { - return true; - } else { - if (isUsingUncheckedGenerics(genericsType.getType())) { - return true; - } - } - } - } - } else { - return false; - } - return false; + public static boolean isUsingUncheckedGenerics(final ClassNode node) { + return GenericsUtils.hasUnresolvedGenerics(node); } /** @@ -1216,7 +1185,6 @@ public abstract class StaticTypeCheckingSupport { } private static Parameter[] makeRawTypes(Parameter[] params, Map<GenericsType, GenericsType> genericsPlaceholderAndTypeMap) { - Parameter[] newParam = new Parameter[params.length]; for (int i = 0; i < params.length; i++) { Parameter oldP = params[i]; @@ -1311,7 +1279,7 @@ public abstract class StaticTypeCheckingSupport { Map<GenericsTypeName, GenericsType> contextPlaceholders = extractGenericsParameterMapOfThis(m); Parameter[] methodParameters = m.getParameters(); Parameter[] params = new Parameter[methodParameters.length]; - for (int i = 0; i < methodParameters.length; i++) { + for (int i = 0, n = methodParameters.length; i < n; i += 1) { Parameter methodParameter = methodParameters[i]; ClassNode paramType = methodParameter.getType(); params[i] = buildParameter(genericFromReceiver, contextPlaceholders, methodParameter, paramType); @@ -1323,7 +1291,7 @@ public abstract class StaticTypeCheckingSupport { * Given a parameter, builds a new parameter for which the known generics placeholders are resolved. * * @param genericFromReceiver resolved generics from the receiver of the message - * @param placeholdersFromContext, resolved generics from the method context + * @param placeholdersFromContext resolved generics from the method context * @param methodParameter the method parameter for which we want to resolve generic types * @param paramType the (unresolved) type of the method parameter * @return a new parameter with the same name and type as the original one, but with resolved generic types @@ -1350,7 +1318,7 @@ public abstract class StaticTypeCheckingSupport { * @param cn a class node for which to check if it is using generics * @return true if the type (or component type) is using generics */ - public static boolean isUsingGenericsOrIsArrayUsingGenerics(ClassNode cn) { + public static boolean isUsingGenericsOrIsArrayUsingGenerics(final ClassNode cn) { if (cn.isArray()) { return isUsingGenericsOrIsArrayUsingGenerics(cn.getComponentType()); } @@ -1388,36 +1356,39 @@ public abstract class StaticTypeCheckingSupport { if (type.isArray()) { return fullyResolveType(type.getComponentType(), placeholders).makeArray(); } - if (type.isUsingGenerics()) { - if (type.isGenericsPlaceHolder()) { - GenericsType gt = placeholders.get(new GenericsTypeName(type.getUnresolvedName())); - if (gt != null) { - return gt.getType(); - } - ClassNode cn = extractType(type.asGenericsType()); // GROOVY-10756 - return cn != type ? cn : OBJECT_TYPE; // do not return placeholder - } else { - GenericsType[] gts = type.getGenericsTypes(); - if (gts != null) { final int n = gts.length; - GenericsType[] copy = new GenericsType[n]; - for (int i = 0; i < n; i += 1) { - GenericsType gt = gts[i]; - if (gt.isPlaceholder()) { - GenericsTypeName gtn = new GenericsTypeName(gt.getName()); - copy[i] = placeholders.containsKey(gtn) - ? placeholders.get(gtn) : extractType(gt).asGenericsType(); - } else { - copy[i] = fullyResolve(gt, placeholders); - } - } - gts = copy; + if (!type.isUsingGenerics()) { + return type; + } + if (type.isGenericsPlaceHolder()) { + GenericsType gt = placeholders.get(new GenericsTypeName(type.getUnresolvedName())); + if (gt != null) { + return gt.getType(); + } + ClassNode cn = extractType(type.asGenericsType()); // GROOVY-10756 + return cn != type ? cn : OBJECT_TYPE; // do not return placeholder + } + + GenericsType[] gts = type.getGenericsTypes(); + if (gts != null && gts.length > 0) { + gts = gts.clone(); + for (int i = 0, n = gts.length; i < n; i += 1) { + GenericsType gt = gts[i]; + if (gt.isPlaceholder()) { String name = gt.getName(); + gt = placeholders.get(new GenericsTypeName(name)); + if (gt == null) gt = extractType(gts[i]).asGenericsType(); + // GROOVY-10364: skip placeholder from the enclosing context + if (gt.isPlaceholder() && gt.getName().equals(name)) continue; + + gts[i] = gt; + } else { + gts[i] = fullyResolve(gt, placeholders); } - ClassNode cn = type.getPlainNodeReference(); - cn.setGenericsTypes(gts); - return cn; } } - return type; + + ClassNode cn = type.getPlainNodeReference(); + cn.setGenericsTypes(gts); + return cn; } /** @@ -1431,12 +1402,9 @@ public abstract class StaticTypeCheckingSupport { // called with null return !isPrimitiveType(parameterType); } - if (!isAssignableTo(argumentType, parameterType) && !lastArg) { - // incompatible assignment - return false; - } - if (!isAssignableTo(argumentType, parameterType) && lastArg) { - if (parameterType.isArray()) { + boolean isArrayParameter = parameterType.isArray(); + if (!isAssignableTo(argumentType, parameterType)) { + if (isArrayParameter && lastArg) { if (!isAssignableTo(argumentType, parameterType.getComponentType())) { return false; } @@ -1444,20 +1412,21 @@ public abstract class StaticTypeCheckingSupport { return false; } } - if (parameterType.isUsingGenerics() && argumentType.isUsingGenerics()) { - GenericsType gt = GenericsUtils.buildWildcardType(parameterType); - if (!gt.isCompatibleWith(argumentType)) { - boolean samCoercion = isSAMType(parameterType) && argumentType.equals(CLOSURE_TYPE); - if (!samCoercion) return false; - } - } else if (parameterType.isArray() && argumentType.isArray()) { + if (isArrayParameter && argumentType.isArray()) { // verify component type return typeCheckMethodArgumentWithGenerics(parameterType.getComponentType(), argumentType.getComponentType(), lastArg); - } else if (lastArg && parameterType.isArray()) { + + } else if (isArrayParameter && lastArg) { // verify component type, but if we reach that point, the only possibility is that the argument is // the last one of the call, so we're in the cast of a vargs call // (otherwise, we face a type checker bug) return typeCheckMethodArgumentWithGenerics(parameterType.getComponentType(), argumentType, lastArg); + + } else if (parameterType.isUsingGenerics() && argumentType.isUsingGenerics()) { + if (!GenericsUtils.buildWildcardType(parameterType).isCompatibleWith(argumentType)) { + boolean samCoercion = argumentType.equals(CLOSURE_TYPE) && isSAMType(parameterType); + if (!samCoercion) return false; + } } return true; } @@ -1547,9 +1516,8 @@ public abstract class StaticTypeCheckingSupport { if (lastArg && type.isArray() && type.getComponentType().isGenericsPlaceHolder() && !wrappedArgument.isArray() && wrappedArgument.isGenericsPlaceHolder()) { - // GROOVY-8090 handle generics varargs, e.g. `U x = ...; Arrays.asList(x)` + // GROOVY-8090: handle generics varargs, e.g. `U x = ...; Arrays.asList(x)` // we should connect the type of vararg(e.g. T is the type of T...) to the argument type - type = type.getComponentType(); } // the context we compare with in the end is the one of the callsite @@ -1615,29 +1583,28 @@ public abstract class StaticTypeCheckingSupport { return true; } - private static boolean compatibleConnection(GenericsType resolved, GenericsType connection) { - GenericsType gt = connection; - if (!connection.isWildcard()) gt = buildWildcardType(connection); - if (resolved.isPlaceholder() && resolved.getUpperBounds() != null && - resolved.getUpperBounds().length == 1 && !resolved.getUpperBounds()[0].isGenericsPlaceHolder() && - resolved.getUpperBounds()[0].getName().equals("java.lang.Object")) { + private static boolean compatibleConnection(final GenericsType resolved, final GenericsType connection) { + if (resolved.isPlaceholder() + && resolved.getUpperBounds() != null + && resolved.getUpperBounds().length == 1 + && !resolved.getUpperBounds()[0].isGenericsPlaceHolder() + && resolved.getUpperBounds()[0].getName().equals(OBJECT)) { return true; } ClassNode compareNode; if (hasNonTrivialBounds(resolved)) { compareNode = getCombinedBoundType(resolved); compareNode = compareNode.redirect().getPlainNodeReference(); + } else if (!resolved.isPlaceholder()) { + compareNode = resolved.getType().getPlainNodeReference(); } else { - if (!resolved.isPlaceholder()) { - compareNode = resolved.getType().getPlainNodeReference(); - } else { - return true; - } + return true; } + GenericsType gt = connection.isWildcard() ? connection : buildWildcardType(connection); return gt.isCompatibleWith(compareNode); } - private static void addMissingEntries(Map<GenericsTypeName, GenericsType> connections, Map<GenericsTypeName, GenericsType> resolved) { + private static void addMissingEntries(final Map<GenericsTypeName, GenericsType> connections, final Map<GenericsTypeName, GenericsType> resolved) { for (Map.Entry<GenericsTypeName, GenericsType> entry : connections.entrySet()) { if (resolved.containsKey(entry.getKey())) continue; GenericsType gt = entry.getValue(); @@ -1691,24 +1658,26 @@ public abstract class StaticTypeCheckingSupport { } private static ClassNode extractType(GenericsType gt) { + ClassNode cn; if (!gt.isPlaceholder()) { - return gt.getType(); - } - // For a placeholder, a type based on the generics type is used for the compatibility check, to match on - // the actual bounds and not the name of the placeholder. - ClassNode replacementType = gt.getType().redirect(); - if (gt.getType().getGenericsTypes() != null) { - GenericsType realGt = gt.getType().getGenericsTypes()[0]; - if (realGt.getLowerBound() != null) { - replacementType = realGt.getLowerBound(); - } else if (realGt.getUpperBounds() != null && realGt.getUpperBounds().length > 0) { - replacementType = realGt.getUpperBounds()[0]; + cn = gt.getType(); + } else { + // discard the placeholder + cn = gt.getType().redirect(); + + if (gt.getType().getGenericsTypes() != null) + gt = gt.getType().getGenericsTypes()[0]; + + if (gt.getLowerBound() != null) { + cn = gt.getLowerBound(); + } else if (gt.getUpperBounds() != null) { + cn = gt.getUpperBounds()[0]; } } - return replacementType; + return cn; } - private static boolean equalIncludingGenerics(GenericsType orig, GenericsType copy) { + private static boolean equalIncludingGenerics(final GenericsType orig, final GenericsType copy) { if (orig == copy) return true; if (orig.isPlaceholder() != copy.isPlaceholder()) return false; if (orig.isWildcard() != copy.isWildcard()) return false; @@ -1724,14 +1693,14 @@ public abstract class StaticTypeCheckingSupport { if ((upper1 == null) ^ (upper2 == null)) return false; if (upper1 != upper2) { if (upper1.length != upper2.length) return false; - for (int i = 0; i < upper1.length; i++) { + for (int i = 0, n = upper1.length; i < n; i += 1) { if (!equalIncludingGenerics(upper1[i], upper2[i])) return false; } } return true; } - private static boolean equalIncludingGenerics(ClassNode orig, ClassNode copy) { + private static boolean equalIncludingGenerics(final ClassNode orig, final ClassNode copy) { if (orig == copy) return true; if (orig.isGenericsPlaceHolder() != copy.isGenericsPlaceHolder()) return false; if (!orig.equals(copy)) return false; @@ -1740,7 +1709,7 @@ public abstract class StaticTypeCheckingSupport { if ((gt1 == null) ^ (gt2 == null)) return false; if (gt1 != gt2) { if (gt1.length != gt2.length) return false; - for (int i = 0; i < gt1.length; i++) { + for (int i = 0, n = gt1.length; i < n; i += 1) { if (!equalIncludingGenerics(gt1[i], gt2[i])) return false; } } @@ -1813,7 +1782,7 @@ public abstract class StaticTypeCheckingSupport { return corrected; } - private static void extractGenericsConnections(Map<GenericsTypeName, GenericsType> connections, GenericsType[] usage, GenericsType[] declaration) { + private static void extractGenericsConnections(final Map<GenericsTypeName, GenericsType> connections, final GenericsType[] usage, final GenericsType[] declaration) { // if declaration does not provide generics, there is no connection to make if (usage == null || declaration == null || declaration.length == 0) return; final int n; if ((n = usage.length) != declaration.length) return; @@ -1874,7 +1843,7 @@ public abstract class StaticTypeCheckingSupport { return newGTs; } - private static GenericsType applyGenericsContext(final Map<GenericsTypeName, GenericsType> spec, final GenericsType gt) { + private static GenericsType applyGenericsContext(Map<GenericsTypeName, GenericsType> spec, GenericsType gt) { if (gt.isPlaceholder()) { GenericsTypeName name = new GenericsTypeName(gt.getName()); GenericsType specType = spec.get(name); @@ -1962,8 +1931,8 @@ public abstract class StaticTypeCheckingSupport { return cn; } - static ClassNode getCombinedBoundType(GenericsType genericsType) { - //TODO: this method should really return some kind of meta ClassNode + static ClassNode getCombinedBoundType(final GenericsType genericsType) { + // TODO: this method should really return some kind of meta ClassNode // representing the combination of all bounds. The code here, just picks // something out to be able to proceed and is not actually correct if (hasNonTrivialBounds(genericsType)) { @@ -1973,7 +1942,7 @@ public abstract class StaticTypeCheckingSupport { return genericsType.getType(); } - private static Map<GenericsTypeName, GenericsType> getGenericsParameterMapOfThis(ClassNode cn) { + private static Map<GenericsTypeName, GenericsType> getGenericsParameterMapOfThis(final ClassNode cn) { if (cn == null) return null; Map<GenericsTypeName, GenericsType> map = null; if (cn.getEnclosingMethod() != null) { @@ -1991,7 +1960,7 @@ public abstract class StaticTypeCheckingSupport { * @param type A parameterized type * @return A parameterized type with more precise wildcards */ - static ClassNode boundUnboundedWildcards(ClassNode type) { + static ClassNode boundUnboundedWildcards(final ClassNode type) { if (type.isArray()) { return boundUnboundedWildcards(type.getComponentType()).makeArray(); } @@ -2003,19 +1972,18 @@ public abstract class StaticTypeCheckingSupport { return newType; } - private static GenericsType[] boundUnboundedWildcards(GenericsType[] usage, GenericsType[] declaration) { + private static GenericsType[] boundUnboundedWildcards(final GenericsType[] usage, final GenericsType[] declaration) { GenericsType[] newGts = new GenericsType[usage.length]; - for (int i = 0; i < usage.length; i++) { + for (int i = 0, n = usage.length; i < n; i += 1) { newGts[i] = boundUnboundedWildcard(usage[i], declaration[i]); } return newGts; } - private static GenericsType boundUnboundedWildcard(GenericsType gt, GenericsType spec) { + private static GenericsType boundUnboundedWildcard(final GenericsType gt, final GenericsType spec) { if (isUnboundedWildcard(gt)) { ClassNode base = makeWithoutCaching("?"); - // The bounds on the declared type are at least as good as the ones on an unbounded wildcard, since it has - // none! + // The bounds on the declared type are at least as good as the ones on an unbounded wildcard, since it has none! GenericsType newGt = new GenericsType(base, spec.getUpperBounds(), spec.getLowerBound()); newGt.setWildcard(true); return newGt; @@ -2032,7 +2000,7 @@ public abstract class StaticTypeCheckingSupport { return false; } - static Map<GenericsTypeName, GenericsType> extractGenericsParameterMapOfThis(TypeCheckingContext context) { + static Map<GenericsTypeName, GenericsType> extractGenericsParameterMapOfThis(final TypeCheckingContext context) { ClassNode cn = context.getEnclosingClassNode(); MethodNode mn = context.getEnclosingMethod(); // GROOVY-9570: find the innermost class or method @@ -2042,7 +2010,7 @@ public abstract class StaticTypeCheckingSupport { return extractGenericsParameterMapOfThis(mn); } - static Map<GenericsTypeName, GenericsType> extractGenericsParameterMapOfThis(MethodNode mn) { + static Map<GenericsTypeName, GenericsType> extractGenericsParameterMapOfThis(final MethodNode mn) { if (mn == null) return null; Map<GenericsTypeName, GenericsType> map; @@ -2055,7 +2023,7 @@ public abstract class StaticTypeCheckingSupport { return mergeGenerics(map, mn.getGenericsTypes()); } - private static Map<GenericsTypeName, GenericsType> mergeGenerics(Map<GenericsTypeName, GenericsType> current, GenericsType[] newGenerics) { + private static Map<GenericsTypeName, GenericsType> mergeGenerics(Map<GenericsTypeName, GenericsType> current, final GenericsType[] newGenerics) { if (newGenerics == null || newGenerics.length == 0) return current; if (current == null) current = new HashMap<GenericsTypeName, GenericsType>(); for (GenericsType gt : newGenerics) { @@ -2228,6 +2196,9 @@ public abstract class StaticTypeCheckingSupport { return node.getSuperClass() != null && isParameterizedWithString(node.getUnresolvedSuperClass()); } + /** + * Determines if node is a raw type or references any generics placeholders. + */ public static boolean missesGenericsTypes(ClassNode cn) { while (cn.isArray()) cn = cn.getComponentType(); GenericsType[] cnGenerics = cn.getGenericsTypes(); @@ -2236,15 +2207,16 @@ public abstract class StaticTypeCheckingSupport { } /** - * A helper method that can be used to evaluate expressions as found in annotation - * parameters. For example, it will evaluate a constant, be it referenced directly as - * an integer or as a reference to a field. + * Evaluates expressions as found in annotation parameters. For example, it + * will evaluate a constant, be it referenced directly as an integer or as a + * reference to a field. * <p> - * If this method throws an exception, then the expression cannot be evaluated on its own. + * If the expression cannot be evaluated on its own, an exception is thrown. * * @param expr the expression to be evaluated * @param config the compiler configuration * @return the result of the expression + * @throws GroovyBugError */ public static Object evaluateExpression(final Expression expr, final CompilerConfiguration config) { String className = "Expression$"+UUID.randomUUID().toString().replace('-', '$'); 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 50e83b5b21..9e4774bbaa 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -3337,7 +3337,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { Expression type = annotation.getMember("type"); Integer stInt = Closure.OWNER_FIRST; if (strategy != null) { - stInt = (Integer) evaluateExpression(castX(Integer_TYPE, strategy), typeCheckingContext.source.getConfiguration()); + stInt = (Integer) evaluateExpression(castX(Integer_TYPE, strategy), getSourceUnit().getConfiguration()); } if (value instanceof ClassExpression && !value.getType().equals(DELEGATES_TO_TARGET)) { if (genericTypeIndex != null) { diff --git a/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java index e218e17f02..19fc799e4f 100644 --- a/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java +++ b/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java @@ -225,8 +225,8 @@ public class TraitASTTransformation extends AbstractASTTransformation implements for (final MethodNode methodNode : methods) { boolean declared = methodNode.getDeclaringClass() == cNode; if (declared) { - if (!methodNode.isSynthetic() && (methodNode.isProtected() || methodNode.getModifiers()==0)) { - unit.addError(new SyntaxException("Cannot have protected/package private method in a trait (" + cNode.getName() + "#" + methodNode.getTypeDescriptor() + ")", + if (!methodNode.isSynthetic() && (methodNode.isProtected() || (!methodNode.isPrivate() && !methodNode.isPublic()))) { + unit.addError(new SyntaxException("Cannot have protected/package-private method in a trait (" + cNode.getName() + "#" + methodNode.getTypeDescriptor() + ")", methodNode.getLineNumber(), methodNode.getColumnNumber())); return null; } @@ -269,7 +269,6 @@ public class TraitASTTransformation extends AbstractASTTransformation implements // clear properties to avoid generation of methods cNode.getProperties().clear(); - // copy annotations copyClassAnnotations(cNode, helper); markAsGenerated(cNode, helper); @@ -570,7 +569,7 @@ public class TraitASTTransformation extends AbstractASTTransformation implements dummyField = new FieldNode( dummyFieldName, ACC_STATIC | ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC, - field.getOriginType(), + field.getOriginType().getPlainNodeReference(), fieldHelper, null ); diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy index 3c8ce122d7..31256795f4 100644 --- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy +++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy @@ -428,6 +428,23 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase { ''' } + @NotYetImplemented + void testReturnTypeInferenceWithMethodGenerics1y() { + assertScript ''' + interface Type { } + class Pogo implements Type { String name } + class CacheManager { + def <T extends Type> T fromCache(String key) { + new Pogo() + } + } + + def cacheManager = new CacheManager( ) + Pogo pogo = cacheManager.fromCache('') + pogo.name + ''' + } + // GROOVY-10067 @NotYetImplemented void testReturnTypeInferenceWithMethodGenerics11() { @@ -1461,7 +1478,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase { } // GROOVY-10343 - @NotYetImplemented void testDiamondInferenceFromConstructor27() { assertScript ''' class C<T1, T2 extends T1> {
