use of groovy.lang.DelegatesTo in project groovy by apache.
the class StaticTypeCheckingVisitor method ensureValidSetter.
/**
* Given a binary expression corresponding to an assignment, will check that
* the type of the RHS matches one of the possible setters and if not, throw
* a type checking error.
*
* @param expression the assignment expression
* @param leftExpression left expression of the assignment
* @param rightExpression right expression of the assignment
* @param setterInfo possible setters
* @return {@code false} if valid setter found or {@code true} if type checking error created
*/
private boolean ensureValidSetter(final Expression expression, final Expression leftExpression, final Expression rightExpression, final SetterInfo setterInfo) {
// for expressions like foo = { ... }
// we know that the RHS type is a closure
// but we must check if the binary expression is an assignment
// because we need to check if a setter uses @DelegatesTo
VariableExpression receiver = varX("%", setterInfo.receiverType);
// for "x op= y" expression, find type as if it was "x = x op y"
Expression valueExpression = rightExpression;
if (isCompoundAssignment(expression)) {
Token op = ((BinaryExpression) expression).getOperation();
if (op.getType() == ELVIS_EQUAL) {
// GROOVY-10419: "x ?= y"
valueExpression = elvisX(leftExpression, rightExpression);
} else {
op = Token.newSymbol(TokenUtil.removeAssignment(op.getType()), op.getStartLine(), op.getStartColumn());
valueExpression = binX(leftExpression, op, rightExpression);
}
}
Function<Expression, MethodNode> setterCall = right -> {
MethodCallExpression call = callX(receiver, setterInfo.name, right);
call.setImplicitThis(false);
visitMethodCallExpression(call);
return call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
};
Function<MethodNode, ClassNode> setterType = setter -> {
ClassNode type = setter.getParameters()[0].getOriginType();
if (!setter.isStatic() && !(setter instanceof ExtensionMethodNode) && GenericsUtils.hasUnresolvedGenerics(type)) {
type = applyGenericsContext(extractPlaceHolders(setterInfo.receiverType, setter.getDeclaringClass()), type);
}
return type;
};
MethodNode methodTarget = setterCall.apply(valueExpression);
if (methodTarget == null && !isCompoundAssignment(expression)) {
// if no direct match, try implicit conversion
for (MethodNode setter : setterInfo.setters) {
ClassNode lType = setterType.apply(setter);
ClassNode rType = getDeclaredOrInferredType(valueExpression);
if (checkCompatibleAssignmentTypes(lType, rType, valueExpression, false)) {
methodTarget = setterCall.apply(castX(lType, valueExpression));
if (methodTarget != null) {
break;
}
}
}
}
if (methodTarget != null) {
for (MethodNode setter : setterInfo.setters) {
if (setter == methodTarget) {
leftExpression.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, methodTarget);
// clear assumption
leftExpression.removeNodeMetaData(INFERRED_TYPE);
storeType(leftExpression, setterType.apply(methodTarget));
break;
}
}
return false;
} else {
ClassNode firstSetterType = setterType.apply(setterInfo.setters.get(0));
addAssignmentError(firstSetterType, getType(valueExpression), expression);
return true;
}
}
Aggregations