use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingVisitor method visitMethodCallExpression.
@Override
public void visitMethodCallExpression(final MethodCallExpression call) {
String name = call.getMethodAsString();
if (name == null) {
addStaticTypeError("Cannot resolve dynamic method name at compile time", call.getMethod());
return;
}
if (extension.beforeMethodCall(call)) {
extension.afterMethodCall(call);
return;
}
typeCheckingContext.pushEnclosingMethodCall(call);
Expression objectExpression = call.getObjectExpression();
objectExpression.visit(this);
call.getMethod().visit(this);
ClassNode receiver = getType(objectExpression);
if (objectExpression instanceof ConstructorCallExpression) {
// GROOVY-10228
inferDiamondType((ConstructorCallExpression) objectExpression, receiver.getPlainNodeReference());
}
if (call.isSpreadSafe()) {
ClassNode componentType = inferComponentType(receiver, null);
if (componentType == null) {
addStaticTypeError("Spread-dot operator can only be used on iterable types", objectExpression);
} else {
MethodCallExpression subcall = callX(varX("item", componentType), name, call.getArguments());
subcall.setLineNumber(call.getLineNumber());
subcall.setColumnNumber(call.getColumnNumber());
subcall.setImplicitThis(call.isImplicitThis());
visitMethodCallExpression(subcall);
// inferred type should be a list of what sub-call returns
storeType(call, extension.buildListType(getType(subcall)));
storeTargetMethod(call, subcall.getNodeMetaData(DIRECT_METHOD_CALL_TARGET));
}
typeCheckingContext.popEnclosingMethodCall();
return;
}
Expression callArguments = call.getArguments();
ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(callArguments);
checkForbiddenSpreadArgument(argumentList);
// visit closures *after* the method has been chosen
visitMethodCallArguments(receiver, argumentList, false, null);
boolean isThisObjectExpression = isThisExpression(objectExpression);
boolean isCallOnClosure = false;
FieldNode fieldNode = null;
switch(name) {
case "call":
case "doCall":
if (!isThisObjectExpression) {
isCallOnClosure = receiver.equals(CLOSURE_TYPE);
break;
}
default:
if (isThisObjectExpression) {
ClassNode enclosingType = typeCheckingContext.getEnclosingClassNode();
fieldNode = enclosingType.getDeclaredField(name);
if (fieldNode != null && getType(fieldNode).equals(CLOSURE_TYPE) && !enclosingType.hasPossibleMethod(name, callArguments)) {
isCallOnClosure = true;
}
}
}
try {
ClassNode[] args = getArgumentTypes(argumentList);
boolean callArgsVisited = false;
if (isCallOnClosure) {
if (fieldNode != null) {
GenericsType[] genericsTypes = getType(fieldNode).getGenericsTypes();
if (genericsTypes != null) {
Parameter[] parameters = fieldNode.getNodeMetaData(CLOSURE_ARGUMENTS);
if (parameters != null) {
typeCheckClosureCall(callArguments, args, parameters);
}
ClassNode closureReturnType = genericsTypes[0].getType();
storeType(call, closureReturnType);
}
} else if (objectExpression instanceof VariableExpression) {
Variable variable = findTargetVariable((VariableExpression) objectExpression);
if (variable instanceof ASTNode) {
Parameter[] parameters = ((ASTNode) variable).getNodeMetaData(CLOSURE_ARGUMENTS);
if (parameters != null) {
typeCheckClosureCall(callArguments, args, parameters);
}
ClassNode type = getType((ASTNode) variable);
if (type.equals(CLOSURE_TYPE)) {
// GROOVY-10098, et al.
GenericsType[] genericsTypes = type.getGenericsTypes();
if (genericsTypes != null && genericsTypes.length == 1 && genericsTypes[0].getLowerBound() == null) {
type = genericsTypes[0].getType();
} else {
type = OBJECT_TYPE;
}
}
if (type != null) {
storeType(call, type);
}
}
} else if (objectExpression instanceof ClosureExpression) {
// we can get actual parameters directly
Parameter[] parameters = ((ClosureExpression) objectExpression).getParameters();
if (parameters != null) {
typeCheckClosureCall(callArguments, args, parameters);
}
ClassNode type = getInferredReturnType(objectExpression);
if (type != null) {
storeType(call, type);
}
}
int nArgs = 0;
if (callArguments instanceof ArgumentListExpression) {
nArgs = ((ArgumentListExpression) callArguments).getExpressions().size();
}
storeTargetMethod(call, nArgs == 0 ? CLOSURE_CALL_NO_ARG : nArgs == 1 ? CLOSURE_CALL_ONE_ARG : CLOSURE_CALL_VARGS);
} else {
// method call receivers are :
// - possible "with" receivers
// - the actual receiver as found in the method call expression
// - any of the potential receivers found in the instanceof temporary table
// in that order
List<Receiver<String>> receivers = new ArrayList<>();
addReceivers(receivers, makeOwnerList(objectExpression), call.isImplicitThis());
List<MethodNode> mn = null;
Receiver<String> chosenReceiver = null;
for (Receiver<String> currentReceiver : receivers) {
ClassNode receiverType = currentReceiver.getType();
mn = findMethod(receiverType, name, args);
// all methods are either static or declared by the current receiver or a superclass
if (!mn.isEmpty() && currentReceiver.getData() == null && (isThisObjectExpression || call.isImplicitThis()) && (typeCheckingContext.isInStaticContext || (receiverType.getModifiers() & Opcodes.ACC_STATIC) != 0)) {
// we create separate method lists just to be able to print out
// a nice error message to the user
// a method is accessible if it is static, or if we are not in a static context and it is
// declared by the current receiver or a superclass
List<MethodNode> accessibleMethods = new LinkedList<>();
List<MethodNode> inaccessibleMethods = new LinkedList<>();
for (final MethodNode node : mn) {
if (node.isStatic() || (!typeCheckingContext.isInStaticContext && implementsInterfaceOrIsSubclassOf(receiverType, node.getDeclaringClass()))) {
accessibleMethods.add(node);
} else {
inaccessibleMethods.add(node);
}
}
mn = accessibleMethods;
if (accessibleMethods.isEmpty()) {
// choose an arbitrary method to display an error message
MethodNode node = inaccessibleMethods.get(0);
addStaticTypeError("Non-static method " + prettyPrintTypeName(node.getDeclaringClass()) + "#" + node.getName() + " cannot be called from static context", call);
}
}
if (!mn.isEmpty()) {
chosenReceiver = currentReceiver;
break;
}
}
if (mn.isEmpty() && isThisObjectExpression && call.isImplicitThis() && typeCheckingContext.getEnclosingClosure() != null) {
mn = CLOSURE_TYPE.getDeclaredMethods(name);
if (!mn.isEmpty()) {
chosenReceiver = Receiver.make(CLOSURE_TYPE);
objectExpression.removeNodeMetaData(INFERRED_TYPE);
}
}
if (mn.isEmpty()) {
mn = extension.handleMissingMethod(receiver, name, argumentList, args, call);
}
if (mn.isEmpty()) {
addNoMatchingMethodError(receiver, name, args, call);
} else {
if (areCategoryMethodCalls(mn, name, args)) {
addCategoryMethodCallError(call);
}
mn = disambiguateMethods(mn, chosenReceiver != null ? chosenReceiver.getType() : null, args, call);
if (mn.size() == 1) {
MethodNode targetMethodCandidate = mn.get(0);
ClassNode declaringClass = targetMethodCandidate.getDeclaringClass();
if (!targetMethodCandidate.isStatic() && !isClassType(declaringClass) && objectExpression instanceof ClassExpression && call.getNodeMetaData(DYNAMIC_RESOLUTION) == null) {
addStaticTypeError("Non-static method " + prettyPrintTypeName(declaringClass) + "#" + targetMethodCandidate.getName() + " cannot be called from static context", call);
} else if (targetMethodCandidate.isAbstract() && isSuperExpression(objectExpression)) {
// GROOVY-10341
addStaticTypeError("Abstract method " + toMethodParametersString(targetMethodCandidate.getName(), extractTypesFromParameters(targetMethodCandidate.getParameters())) + " cannot be called directly", call);
}
if (chosenReceiver == null) {
chosenReceiver = Receiver.make(declaringClass);
}
// note second pass here to differentiate from extension that sets type
boolean mergeType = (call.getNodeMetaData(INFERRED_TYPE) != null);
storeTargetMethod(call, targetMethodCandidate);
visitMethodCallArguments(chosenReceiver.getType(), argumentList, true, targetMethodCandidate);
callArgsVisited = true;
ClassNode returnType = getType(targetMethodCandidate);
if (isUsingGenericsOrIsArrayUsingGenerics(returnType)) {
ClassNode irtg = inferReturnTypeGenerics(chosenReceiver.getType(), targetMethodCandidate, callArguments, call.getGenericsTypes());
if (irtg != null && implementsInterfaceOrIsSubclassOf(irtg, returnType))
returnType = irtg;
}
// GROOVY-6091: use of "delegate" or "getDelegate()" does not make use of @DelegatesTo metadata
if (targetMethodCandidate == GET_DELEGATE && typeCheckingContext.getEnclosingClosure() != null) {
DelegationMetadata md = getDelegationMetadata(typeCheckingContext.getEnclosingClosure().getClosureExpression());
if (md != null) {
returnType = md.getType();
} else {
returnType = typeCheckingContext.getEnclosingClassNode();
}
}
// GROOVY-7106, GROOVY-7274, GROOVY-8909, GROOVY-8961, GROOVY-9734, GROOVY-9844, GROOVY-9915, et al.
Parameter[] parameters = targetMethodCandidate.getParameters();
if (chosenReceiver.getType().getGenericsTypes() != null && !targetMethodCandidate.isStatic() && !(targetMethodCandidate instanceof ExtensionMethodNode)) {
Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(chosenReceiver.getType(), targetMethodCandidate, argumentList);
parameters = Arrays.stream(parameters).map(p -> new Parameter(applyGenericsContext(context, p.getType()), p.getName())).toArray(Parameter[]::new);
}
resolvePlaceholdersFromImplicitTypeHints(args, argumentList, parameters);
if (typeCheckMethodsWithGenericsOrFail(chosenReceiver.getType(), args, targetMethodCandidate, call)) {
String data = chosenReceiver.getData();
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(IMPLICIT_RECEIVER, data);
}
receiver = chosenReceiver.getType();
if (mergeType || call.getNodeMetaData(INFERRED_TYPE) == null)
storeType(call, adjustWithTraits(targetMethodCandidate, receiver, args, returnType));
if (objectExpression instanceof VariableExpression && ((VariableExpression) objectExpression).isClosureSharedVariable()) {
// if the object expression is a closure shared variable, we will have to perform a second pass
typeCheckingContext.secondPassExpressions.add(new SecondPassExpression<>(call, args));
}
} else {
call.removeNodeMetaData(DIRECT_METHOD_CALL_TARGET);
}
} else {
addAmbiguousErrorMessage(mn, name, args, call);
}
}
}
// adjust typing for explicit math methods which have special handling; operator variants handled elsewhere
if (args.length == 1 && isNumberType(args[0]) && isNumberType(receiver) && NUMBER_OPS.containsKey(name)) {
ClassNode resultType = getMathResultType(NUMBER_OPS.get(name), receiver, args[0], name);
if (resultType != null) {
storeType(call, resultType);
}
}
MethodNode target = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
if (!callArgsVisited) {
visitMethodCallArguments(receiver, argumentList, true, target);
}
if (target != null) {
checkClosureMetadata(argumentList.getExpressions(), target.getParameters());
}
} finally {
typeCheckingContext.popEnclosingMethodCall();
extension.afterMethodCall(call);
}
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingVisitor method inferClosureParameterTypes.
/**
* Performs type inference on closure argument types whenever code like this
* is found: <code>foo.collect { it.toUpperCase() }</code>.
* <p>
* In this case the type checker tries to find if the {@code collect} method
* has its {@link Closure} argument annotated with {@link ClosureParams}. If
* so, then additional type inference can be performed and the type of
* {@code it} may be inferred.
*
* @param receiver
* @param arguments
* @param expression closure or lambda expression for which the argument types should be inferred
* @param target parameter which may provide {@link ClosureParams} annotation or SAM type
* @param method method that declares {@code target}
*/
protected void inferClosureParameterTypes(final ClassNode receiver, final Expression arguments, final ClosureExpression expression, final Parameter target, final MethodNode method) {
List<AnnotationNode> annotations = target.getAnnotations(CLOSUREPARAMS_CLASSNODE);
if (annotations != null && !annotations.isEmpty()) {
for (AnnotationNode annotation : annotations) {
Expression value = annotation.getMember("value");
Expression options = annotation.getMember("options");
Expression conflictResolver = annotation.getMember("conflictResolutionStrategy");
doInferClosureParameterTypes(receiver, arguments, expression, method, value, conflictResolver, options);
}
} else if (isSAMType(target.getOriginType())) {
// SAM-type coercion
Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
GenericsType[] typeParameters = method instanceof ConstructorNode ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
if (typeParameters != null) {
boolean typeParametersResolved = false;
// first check for explicit type arguments
Expression emc = typeCheckingContext.getEnclosingMethodCall();
if (emc instanceof MethodCallExpression) {
MethodCallExpression mce = (MethodCallExpression) emc;
if (mce.getArguments() == arguments) {
GenericsType[] typeArguments = mce.getGenericsTypes();
if (typeArguments != null) {
int n = typeParameters.length;
if (n == typeArguments.length) {
typeParametersResolved = true;
for (int i = 0; i < n; i += 1) {
context.put(new GenericsTypeName(typeParameters[i].getName()), typeArguments[i]);
}
}
}
}
}
if (!typeParametersResolved) {
// check for implicit type arguments
int i = -1;
Parameter[] p = method.getParameters();
for (Expression argument : (ArgumentListExpression) arguments) {
i += 1;
if (argument instanceof ClosureExpression || isNullConstant(argument))
continue;
ClassNode pType = p[Math.min(i, p.length - 1)].getType();
Map<GenericsTypeName, GenericsType> gc = new HashMap<>();
extractGenericsConnections(gc, wrapTypeIfNecessary(getType(argument)), pType);
gc.forEach((key, gt) -> {
for (GenericsType tp : typeParameters) {
if (tp.getName().equals(key.getName())) {
// TODO: merge
context.putIfAbsent(key, gt);
break;
}
}
});
}
for (GenericsType tp : typeParameters) {
context.computeIfAbsent(new GenericsTypeName(tp.getName()), x -> fullyResolve(tp, context));
}
}
}
ClassNode[] samParamTypes = GenericsUtils.parameterizeSAM(applyGenericsContext(context, target.getType())).getV1();
ClassNode[] paramTypes = expression.getNodeMetaData(CLOSURE_ARGUMENTS);
if (paramTypes == null) {
int n;
Parameter[] p = expression.getParameters();
if (p == null) {
// zero parameters
paramTypes = ClassNode.EMPTY_ARRAY;
} else if ((n = p.length) == 0) {
// implicit parameter(s)
paramTypes = samParamTypes;
} else {
// TODO: error for length mismatch
paramTypes = Arrays.copyOf(samParamTypes, n);
for (int i = 0; i < Math.min(n, samParamTypes.length); i += 1) {
checkParamType(p[i], paramTypes[i], i == n - 1, expression instanceof LambdaExpression);
}
}
expression.putNodeMetaData(CLOSURE_ARGUMENTS, paramTypes);
}
}
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingVisitor method typeCheckMethodsWithGenericsOrFail.
protected boolean typeCheckMethodsWithGenericsOrFail(final ClassNode receiver, final ClassNode[] arguments, final MethodNode candidateMethod, final Expression location) {
if (!typeCheckMethodsWithGenerics(receiver, arguments, candidateMethod)) {
ClassNode r = receiver, at[] = arguments;
MethodNode m = candidateMethod;
if (candidateMethod instanceof ExtensionMethodNode) {
m = ((ExtensionMethodNode) candidateMethod).getExtensionMethodNode();
r = m.getDeclaringClass();
at = new ClassNode[arguments.length + 1];
// object expression is first argument
at[0] = receiver;
System.arraycopy(arguments, 0, at, 1, arguments.length);
}
Map<GenericsTypeName, GenericsType> spec = extractPlaceHoldersVisibleToDeclaration(r, m, null);
// class params in bounds
GenericsType[] gt = applyGenericsContext(spec, m.getGenericsTypes());
GenericsUtils.extractPlaceholders(makeClassSafe0(OBJECT_TYPE, gt), spec);
Parameter[] parameters = m.getParameters();
ClassNode[] paramTypes = new ClassNode[parameters.length];
for (int i = 0, n = parameters.length; i < n; i += 1) {
paramTypes[i] = fullyResolveType(parameters[i].getType(), spec);
// GROOVY-10010: check for List<String> parameter and ["foo","$bar"] argument
if (i < at.length && hasGStringStringError(paramTypes[i], at[i], location)) {
return false;
}
}
addStaticTypeError("Cannot call " + (gt == null ? "" : GenericsUtils.toGenericTypesString(gt)) + prettyPrintTypeName(r) + "#" + toMethodParametersString(m.getName(), paramTypes) + " with arguments " + formatArgumentList(at), location);
return false;
}
return true;
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingVisitor method visitMethodPointerExpression.
@Override
public void visitMethodPointerExpression(final MethodPointerExpression expression) {
super.visitMethodPointerExpression(expression);
Expression nameExpr = expression.getMethodName();
if (nameExpr instanceof ConstantExpression && isStringType(getType(nameExpr))) {
String nameText = nameExpr.getText();
if ("new".equals(nameText)) {
ClassNode receiverType = getType(expression.getExpression());
if (isClassClassNodeWrappingConcreteType(receiverType)) {
storeType(expression, wrapClosureType(receiverType));
}
return;
}
List<Receiver<String>> receivers = new ArrayList<>();
addReceivers(receivers, makeOwnerList(expression.getExpression()), false);
ClassNode receiverType = null;
List<MethodNode> candidates = EMPTY_METHODNODE_LIST;
for (Receiver<String> currentReceiver : receivers) {
receiverType = wrapTypeIfNecessary(currentReceiver.getType());
candidates = findMethodsWithGenerated(receiverType, nameText);
if (isBeingCompiled(receiverType))
candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
candidates.addAll(findDGMMethodsForClassNode(getSourceUnit().getClassLoader(), receiverType, nameText));
candidates = filterMethodsByVisibility(candidates, typeCheckingContext.getEnclosingClassNode());
if (!candidates.isEmpty()) {
break;
}
}
if (candidates.isEmpty()) {
candidates = extension.handleMissingMethod(getType(expression.getExpression()), nameText, null, null, null);
} else if (candidates.size() > 1) {
candidates = extension.handleAmbiguousMethods(candidates, expression);
}
if (!candidates.isEmpty()) {
Map<GenericsTypeName, GenericsType> gts = GenericsUtils.extractPlaceholders(receiverType);
candidates.stream().map(candidate -> applyGenericsContext(gts, candidate.getReturnType())).reduce(WideningCategories::lowestUpperBound).ifPresent(returnType -> {
storeType(expression, wrapClosureType(returnType));
});
expression.putNodeMetaData(MethodNode.class, candidates);
} else if (!(expression instanceof MethodReferenceExpression)) {
ClassNode type = wrapTypeIfNecessary(getType(expression.getExpression()));
if (isClassClassNodeWrappingConcreteType(type))
type = type.getGenericsTypes()[0].getType();
addStaticTypeError("Cannot find matching method " + prettyPrintTypeName(type) + "#" + nameText + ". Please check if the declared type is correct and if the method exists.", nameExpr);
}
}
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingVisitor method extractPlaceHoldersVisibleToDeclaration.
private static Map<GenericsTypeName, GenericsType> extractPlaceHoldersVisibleToDeclaration(final ClassNode receiver, final MethodNode method, final Expression argument) {
Map<GenericsTypeName, GenericsType> result;
if (method.isStatic()) {
result = new HashMap<>();
} else {
ClassNode declaring = method.getDeclaringClass();
if (argument instanceof ArgumentListExpression) {
// resolve extension method class
List<Expression> arguments = ((ArgumentListExpression) argument).getExpressions();
if (!arguments.isEmpty()) {
ClassNode cn = arguments.get(0).getNodeMetaData(ExtensionMethodDeclaringClass.class);
if (cn != null)
declaring = cn;
}
}
result = extractPlaceHolders(receiver, declaring);
if (!result.isEmpty())
Optional.ofNullable(method.getGenericsTypes()).ifPresent(methodGenerics -> Arrays.stream(methodGenerics).map(gt -> new GenericsTypeName(gt.getName())).forEach(result::remove));
}
return result;
}
Aggregations