use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingSupport method applyGenericsContext.
private static GenericsType applyGenericsContext(final Map<GenericsTypeName, GenericsType> spec, final GenericsType gt) {
if (gt.isPlaceholder()) {
GenericsTypeName name = new GenericsTypeName(gt.getName());
GenericsType specType = spec.get(name);
if (specType != null)
return specType;
if (hasNonTrivialBounds(gt)) {
GenericsType newGT = new GenericsType(gt.getType(), applyGenericsContext(spec, gt.getUpperBounds()), applyGenericsContext(spec, gt.getLowerBound()));
newGT.setPlaceholder(true);
return newGT;
}
return gt;
} else if (gt.isWildcard()) {
GenericsType newGT = new GenericsType(gt.getType(), applyGenericsContext(spec, gt.getUpperBounds()), applyGenericsContext(spec, gt.getLowerBound()));
newGT.setWildcard(true);
return newGT;
}
ClassNode type = gt.getType();
ClassNode newType;
if (type.isArray()) {
newType = applyGenericsContext(spec, type.getComponentType()).makeArray();
} else {
if (type.getGenericsTypes() == null) {
return gt;
}
newType = type.getPlainNodeReference();
newType.setGenericsPlaceHolder(type.isGenericsPlaceHolder());
newType.setGenericsTypes(applyGenericsContext(spec, type.getGenericsTypes()));
}
return new GenericsType(newType);
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingSupport method parameterizeArguments.
/**
* Given a receiver and a method node, parameterize the method arguments using
* available generic type information.
*
* @param receiver the class
* @param m the method
* @return the parameterized arguments
*/
public static Parameter[] parameterizeArguments(final ClassNode receiver, final MethodNode m) {
Map<GenericsTypeName, GenericsType> genericFromReceiver = GenericsUtils.extractPlaceholders(receiver);
Map<GenericsTypeName, GenericsType> contextPlaceholders = extractGenericsParameterMapOfThis(m);
Parameter[] methodParameters = m.getParameters();
Parameter[] params = new Parameter[methodParameters.length];
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);
}
return params;
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingVisitor method visitBinaryExpression.
@Override
public void visitBinaryExpression(final BinaryExpression expression) {
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
typeCheckingContext.pushEnclosingBinaryExpression(expression);
try {
int op = expression.getOperation().getType();
Expression leftExpression = expression.getLeftExpression();
Expression rightExpression = expression.getRightExpression();
leftExpression.visit(this);
SetterInfo setterInfo = removeSetterInfo(leftExpression);
ClassNode lType = null;
if (setterInfo != null) {
if (ensureValidSetter(expression, leftExpression, rightExpression, setterInfo)) {
return;
}
lType = getType(leftExpression);
} else {
if (op != EQUAL && op != ELVIS_EQUAL) {
lType = getType(leftExpression);
} else {
lType = getOriginalDeclarationType(leftExpression);
if (isFunctionalInterface(lType)) {
processFunctionalInterfaceAssignment(lType, rightExpression);
} else if (isClosureWithType(lType) && rightExpression instanceof ClosureExpression) {
storeInferredReturnType(rightExpression, getCombinedBoundType(lType.getGenericsTypes()[0]));
}
}
rightExpression.visit(this);
}
ClassNode rType = isNullConstant(rightExpression) && !isPrimitiveType(lType) ? // null to primitive type is handled elsewhere
UNKNOWN_PARAMETER_TYPE : getInferredTypeFromTempInfo(rightExpression, getType(rightExpression));
ClassNode resultType;
if (op == KEYWORD_IN || op == COMPARE_NOT_IN) {
// for the "in" or "!in" operator, the receiver and the arguments are reversed
BinaryExpression reverseExpression = binX(rightExpression, expression.getOperation(), leftExpression);
resultType = getResultType(rType, op, lType, reverseExpression);
// GROOVY-10239
if (resultType == null)
resultType = boolean_TYPE;
storeTargetMethod(expression, reverseExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET));
} else {
resultType = getResultType(lType, op, rType, expression);
if (op == ELVIS_EQUAL) {
// TODO: Should this transform and visit be done before left and right are visited above?
Expression fullExpression = new ElvisOperatorExpression(leftExpression, rightExpression);
fullExpression.setSourcePosition(expression);
fullExpression.visit(this);
resultType = getType(fullExpression);
}
}
if (resultType == null) {
resultType = lType;
}
if (isArrayOp(op)) {
if (leftExpression instanceof VariableExpression) {
// GROOVY-6782
if (leftExpression.getNodeMetaData(INFERRED_TYPE) == null) {
leftExpression.removeNodeMetaData(INFERRED_RETURN_TYPE);
storeType(leftExpression, lType);
}
}
if (!lType.isArray() && enclosingBinaryExpression != null && enclosingBinaryExpression.getLeftExpression() == expression && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
// left hand side of a subscript assignment: map['foo'] = ...
Expression enclosingExpressionRHS = enclosingBinaryExpression.getRightExpression();
if (!(enclosingExpressionRHS instanceof ClosureExpression)) {
enclosingExpressionRHS.visit(this);
}
ClassNode[] arguments = { rType, getType(enclosingExpressionRHS) };
List<MethodNode> nodes = findMethod(lType.redirect(), "putAt", arguments);
if (nodes.size() == 1) {
typeCheckMethodsWithGenericsOrFail(lType, arguments, nodes.get(0), enclosingExpressionRHS);
} else if (nodes.isEmpty()) {
addNoMatchingMethodError(lType, "putAt", arguments, enclosingBinaryExpression);
}
}
}
boolean isEmptyDeclaration = (expression instanceof DeclarationExpression && (rightExpression instanceof EmptyExpression || rType == UNKNOWN_PARAMETER_TYPE));
if (!isEmptyDeclaration && isAssignment(op)) {
if (rightExpression instanceof ConstructorCallExpression)
inferDiamondType((ConstructorCallExpression) rightExpression, lType);
if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
// "completed" with generics type information available from the LHS
if (lType.equals(resultType)) {
if (!lType.isGenericsPlaceHolder())
resultType = lType;
} else if (!resultType.isGenericsPlaceHolder()) {
// GROOVY-10324
Map<GenericsTypeName, GenericsType> gt = new HashMap<>();
extractGenericsConnections(gt, resultType, resultType.redirect());
extractGenericsConnections(gt, lType, getNextSuperClass(resultType, lType));
// GROOVY-10235, et al.
resultType = applyGenericsContext(gt, resultType.redirect());
}
}
ClassNode originType = getOriginalDeclarationType(leftExpression);
typeCheckAssignment(expression, leftExpression, originType, rightExpression, resultType);
// check for implicit conversion like "String a = 123", "int[] b = [1,2,3]", "List c = [].stream()", etc.
if (!implementsInterfaceOrIsSubclassOf(wrapTypeIfNecessary(resultType), wrapTypeIfNecessary(originType))) {
resultType = originType;
} else if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) {
// retain primitive semantics
resultType = originType;
} else {
// GROOVY-7549: RHS type may not be accessible to enclosing class
int modifiers = resultType.getModifiers();
ClassNode enclosingType = typeCheckingContext.getEnclosingClassNode();
if (!Modifier.isPublic(modifiers) && !enclosingType.equals(resultType) && !getOutermost(enclosingType).equals(getOutermost(resultType)) && (Modifier.isPrivate(modifiers) || !Objects.equals(enclosingType.getPackageName(), resultType.getPackageName()))) {
// TODO: Find accesible type in hierarchy of resultType?
resultType = originType;
} else if (GenericsUtils.hasUnresolvedGenerics(resultType)) {
// GROOVY-9033, GROOVY-10089, et al.
Map<GenericsTypeName, GenericsType> enclosing = extractGenericsParameterMapOfThis(typeCheckingContext);
resultType = fullyResolveType(resultType, Optional.ofNullable(enclosing).orElseGet(Collections::emptyMap));
}
}
// track conditional assignment
if (leftExpression instanceof VariableExpression && typeCheckingContext.ifElseForWhileAssignmentTracker != null) {
Variable accessedVariable = ((VariableExpression) leftExpression).getAccessedVariable();
if (accessedVariable instanceof Parameter) {
accessedVariable = new ParameterVariableExpression((Parameter) accessedVariable);
}
if (accessedVariable instanceof VariableExpression) {
recordAssignment((VariableExpression) accessedVariable, resultType);
}
}
storeType(leftExpression, resultType);
// propagate closure parameter type information
if (leftExpression instanceof VariableExpression) {
if (rightExpression instanceof ClosureExpression) {
leftExpression.putNodeMetaData(CLOSURE_ARGUMENTS, ((ClosureExpression) rightExpression).getParameters());
} else if (rightExpression instanceof VariableExpression && ((VariableExpression) rightExpression).getAccessedVariable() instanceof Expression && ((Expression) ((VariableExpression) rightExpression).getAccessedVariable()).getNodeMetaData(CLOSURE_ARGUMENTS) != null) {
Variable targetVariable = findTargetVariable((VariableExpression) leftExpression);
if (targetVariable instanceof ASTNode) {
((ASTNode) targetVariable).putNodeMetaData(CLOSURE_ARGUMENTS, ((Expression) ((VariableExpression) rightExpression).getAccessedVariable()).getNodeMetaData(CLOSURE_ARGUMENTS));
}
}
}
} else if (op == KEYWORD_INSTANCEOF) /*|| op == COMPARE_NOT_INSTANCEOF*/
{
pushInstanceOfTypeInfo(leftExpression, rightExpression);
}
if (!isEmptyDeclaration) {
storeType(expression, resultType);
}
validateResourceInARM(expression, resultType);
// GROOVY-5874: if left expression is a closure shared variable, a second pass should be done
if (leftExpression instanceof VariableExpression && ((VariableExpression) leftExpression).isClosureSharedVariable()) {
typeCheckingContext.secondPassExpressions.add(new SecondPassExpression<>(expression));
}
} finally {
typeCheckingContext.popEnclosingBinaryExpression();
}
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingVisitor method convertClosureTypeToSAMType.
/**
* Converts a Closure type to the appropriate SAM type, which can be used to
* infer return type generics.
*
* @param expression the argument expression
* @param closureType the inferred type of {@code expression}
* @param samType the target type for the argument expression
* @return SAM type augmented using information from the argument expression
*/
private static ClassNode convertClosureTypeToSAMType(final Expression expression, final ClassNode closureType, final MethodNode sam, final ClassNode samType) {
Map<GenericsTypeName, GenericsType> samTypeConnections = GenericsUtils.extractPlaceholders(samType);
samTypeConnections.replaceAll((// GROOVY-9762, GROOVY-9803: reduce "? super T" to "T"
xx, // GROOVY-9762, GROOVY-9803: reduce "? super T" to "T"
gt) -> Optional.ofNullable(gt.getLowerBound()).map(GenericsType::new).orElse(gt));
ClassNode closureReturnType = closureType.getGenericsTypes()[0].getType();
Parameter[] parameters = sam.getParameters();
if (parameters.length > 0 && expression instanceof MethodPointerExpression && GenericsUtils.hasUnresolvedGenerics(closureReturnType)) {
// try to resolve referenced method type parameters in return type
MethodPointerExpression mp = (MethodPointerExpression) expression;
List<MethodNode> candidates = mp.getNodeMetaData(MethodNode.class);
if (candidates != null && !candidates.isEmpty()) {
ClassNode[] paramTypes = applyGenericsContext(samTypeConnections, extractTypesFromParameters(parameters));
ClassNode[] matchTypes = candidates.stream().map(candidate -> collateMethodReferenceParameterTypes(mp, candidate)).filter(candidate -> checkSignatureSuitability(candidate, paramTypes)).findFirst().orElse(// TODO: order signatures by param distance
null);
if (matchTypes != null) {
Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
for (int i = 0, n = parameters.length; i < n; i += 1) {
// SAM parameters should align with the referenced method's parameters
extractGenericsConnections(connections, paramTypes[i], matchTypes[i]);
}
// convert the method reference's generics into the SAM's generics domain
closureReturnType = applyGenericsContext(connections, closureReturnType);
// apply known generics connections to the SAM's placeholders in the return type
closureReturnType = applyGenericsContext(samTypeConnections, closureReturnType);
}
}
}
// the SAM's return type exactly corresponds to the inferred closure return type
extractGenericsConnections(samTypeConnections, closureReturnType, sam.getReturnType());
// repeat the same for each parameter given in the ClosureExpression
if (parameters.length > 0 && expression instanceof ClosureExpression) {
// TODO
return closureType;
}
return applyGenericsContext(samTypeConnections, samType.redirect());
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingVisitor method resolvePlaceholdersFromImplicitTypeHints.
/**
* Given method call like "m(Collections.emptyList())", the type of the call
* argument is {@code List<T>} without explicit type arguments. Knowning the
* method target of "m", {@code T} could be resolved.
*/
private void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals, final ArgumentListExpression argumentList, final Parameter[] parameterArray) {
int np = parameterArray.length;
for (int i = 0, n = actuals.length; np > 0 && i < n; i += 1) {
Expression a = argumentList.getExpression(i);
Parameter p = parameterArray[Math.min(i, np - 1)];
ClassNode at = actuals[i], pt = p.getOriginType();
if (!isUsingGenericsOrIsArrayUsingGenerics(pt))
continue;
if (i >= (np - 1) && pt.isArray() && !at.isArray())
pt = pt.getComponentType();
if (a instanceof ListExpression) {
actuals[i] = getLiteralResultType(pt, at, ArrayList_TYPE);
} else if (a instanceof MapExpression) {
actuals[i] = getLiteralResultType(pt, at, LinkedHashMap_TYPE);
} else if (a instanceof ConstructorCallExpression) {
// GROOVY-10086
inferDiamondType((ConstructorCallExpression) a, pt);
} else if (a instanceof TernaryExpression && at.getGenericsTypes() != null && at.getGenericsTypes().length == 0) {
// GROOVY-9983: double diamond scenario -- "m(flag ? new Type<>(...) : new Type<>(...))"
typeCheckingContext.pushEnclosingBinaryExpression(assignX(varX(p), a, a));
// re-visit with target type witness
a.visit(this);
typeCheckingContext.popEnclosingBinaryExpression();
actuals[i] = getType(a);
}
// check for method call with known target
if (!(a instanceof MethodCallExpression))
continue;
if (((MethodCallExpression) a).isUsingGenerics())
continue;
MethodNode aNode = a.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
if (aNode == null || aNode.getGenericsTypes() == null)
continue;
// and unknown generics
if (!GenericsUtils.hasUnresolvedGenerics(at))
continue;
while (!at.equals(pt) && !isObjectType(at) && !isGenericsPlaceHolderOrArrayOf(at)) {
at = applyGenericsContext(GenericsUtils.extractPlaceholders(at), getNextSuperClass(at, pt));
}
// try to resolve placeholder(s) in argument type using parameter type
Map<GenericsTypeName, GenericsType> linked = new HashMap<>();
Map<GenericsTypeName, GenericsType> source = GenericsUtils.extractPlaceholders(at);
Map<GenericsTypeName, GenericsType> target = GenericsUtils.extractPlaceholders(pt);
// connect E:T from source to E:Type from target
for (GenericsType placeholder : aNode.getGenericsTypes()) {
for (Map.Entry<GenericsTypeName, GenericsType> e : source.entrySet()) {
if (e.getValue().getNodeMetaData(GenericsType.class) == placeholder) {
Optional.ofNullable(target.get(e.getKey())).filter(gt -> isAssignableTo(gt.getType(), placeholder.getType())).ifPresent(gt -> linked.put(new GenericsTypeName(e.getValue().getName()), gt));
break;
}
}
}
actuals[i] = applyGenericsContext(linked, at);
}
}
Aggregations