use of org.codehaus.groovy.transform.stc.StaticTypesMarker.TYPE in project groovy by apache.
the class StaticTypeCheckingVisitor method checkGroovyConstructorMap.
protected void checkGroovyConstructorMap(final Expression receiver, final ClassNode receiverType, final MapExpression mapExpression) {
// workaround for map-style checks putting setter info on wrong AST nodes
typeCheckingContext.pushEnclosingBinaryExpression(null);
for (MapEntryExpression entryExpression : mapExpression.getMapEntryExpressions()) {
Expression keyExpression = entryExpression.getKeyExpression();
if (!(keyExpression instanceof ConstantExpression)) {
addStaticTypeError("Dynamic keys in map-style constructors are unsupported in static type checking", keyExpression);
} else {
String pName = keyExpression.getText();
AtomicReference<ClassNode> pType = new AtomicReference<>();
if (!existsProperty(propX(varX("_", receiverType), pName), false, new PropertyLookupVisitor(pType))) {
addStaticTypeError("No such property: " + pName + " for class: " + prettyPrintTypeName(receiverType), receiver);
} else {
ClassNode targetType = Optional.ofNullable(receiverType.getSetterMethod(getSetterName(pName), false)).map(setter -> setter.getParameters()[0].getType()).orElseGet(pType::get);
Expression valueExpression = entryExpression.getValueExpression();
ClassNode valueType = getType(valueExpression);
ClassNode resultType = getResultType(targetType, ASSIGN, valueType, assignX(keyExpression, valueExpression, entryExpression));
if (!checkCompatibleAssignmentTypes(targetType, resultType, valueExpression) && !extension.handleIncompatibleAssignment(targetType, valueType, entryExpression)) {
addAssignmentError(targetType, valueType, entryExpression);
}
}
}
}
typeCheckingContext.popEnclosingBinaryExpression();
}
use of org.codehaus.groovy.transform.stc.StaticTypesMarker.TYPE in project groovy by apache.
the class StaticTypeCheckingVisitor method getLiteralResultType.
/**
* For "{@code List<Type> x = [...]}" or "{@code Set<Type> y = [...]}", etc.
* the literal may be composed of sub-types of {@code Type}. In these cases,
* {@code ArrayList<Type>} is an appropriate result type for the expression.
*/
private static ClassNode getLiteralResultType(final ClassNode targetType, final ClassNode sourceType, final ClassNode baseType) {
ClassNode resultType = sourceType.equals(baseType) ? sourceType : GenericsUtils.parameterizeType(sourceType, baseType.getPlainNodeReference());
if (targetType.getGenericsTypes() != null && !GenericsUtils.buildWildcardType(targetType).isCompatibleWith(resultType)) {
BiPredicate<GenericsType, GenericsType> isEqualOrSuper = (target, source) -> {
if (target.isCompatibleWith(source.getType())) {
return true;
}
if (!target.isPlaceholder() && !target.isWildcard()) {
return GenericsUtils.buildWildcardType(getCombinedBoundType(target)).isCompatibleWith(source.getType());
}
return false;
};
GenericsType[] lgt = targetType.getGenericsTypes(), rgt = resultType.getGenericsTypes();
if (IntStream.range(0, lgt.length).allMatch(i -> isEqualOrSuper.test(lgt[i], rgt[i]))) {
resultType = GenericsUtils.parameterizeType(targetType, baseType.getPlainNodeReference());
}
}
return resultType;
}
use of org.codehaus.groovy.transform.stc.StaticTypesMarker.TYPE in project groovy by apache.
the class StaticTypeCheckingVisitor method inferReturnTypeGenerics.
/**
* If a method call returns a parameterized type, then perform additional
* inference on the return type, so that the type gets actual type arguments.
* For example, the method {@code Arrays.asList(T...)} is parameterized with
* {@code T}, which can be deduced type arguments or call arguments.
*
* @param method the method node
* @param arguments the method call arguments
* @param receiver the object expression type
* @param explicitTypeHints type arguments (optional), for example {@code Collections.<String>emptyList()}
*/
protected ClassNode inferReturnTypeGenerics(final ClassNode receiver, final MethodNode method, final Expression arguments, final GenericsType[] explicitTypeHints) {
ClassNode returnType = method instanceof ConstructorNode ? method.getDeclaringClass() : method.getReturnType();
if (!GenericsUtils.hasUnresolvedGenerics(returnType)) {
// GROOVY-7538: replace "Type<?>" with "Type<? extends/super X>" for any "Type<T extends/super X>"
if (getGenericsWithoutArray(returnType) != null)
returnType = boundUnboundedWildcards(returnType);
return returnType;
}
if (method instanceof ExtensionMethodNode) {
ArgumentListExpression args = getExtensionArguments(receiver, method, arguments);
MethodNode extension = ((ExtensionMethodNode) method).getExtensionMethodNode();
return inferReturnTypeGenerics(receiver, extension, args, explicitTypeHints);
}
Map<GenericsTypeName, GenericsType> context = method.isStatic() || method instanceof ConstructorNode ? null : extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
GenericsType[] methodGenericTypes = method instanceof ConstructorNode ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
if (methodGenericTypes != null) {
Map<GenericsTypeName, GenericsType> resolvedPlaceholders = new HashMap<>();
for (GenericsType gt : methodGenericTypes) resolvedPlaceholders.put(new GenericsTypeName(gt.getName()), gt);
applyGenericsConnections(extractGenericsConnectionsFromArguments(methodGenericTypes, Arrays.stream(method.getParameters()).map(param -> new Parameter(applyGenericsContext(context, param.getType()), param.getName())).toArray(Parameter[]::new), arguments, explicitTypeHints), resolvedPlaceholders);
returnType = applyGenericsContext(resolvedPlaceholders, returnType);
}
if (context != null) {
returnType = applyGenericsContext(context, returnType);
if (receiver.getGenericsTypes() == null && receiver.redirect().getGenericsTypes() != null && GenericsUtils.hasUnresolvedGenerics(returnType)) {
// GROOVY-10049: do not return "Stream<E>" for raw type "List#stream()"
returnType = returnType.getPlainNodeReference();
}
}
// 3) resolve bounds of type parameters from calling context
returnType = applyGenericsContext(extractGenericsParameterMapOfThis(typeCheckingContext), returnType);
return returnType;
}
use of org.codehaus.groovy.transform.stc.StaticTypesMarker.TYPE in project groovy by apache.
the class StaticTypeCheckingVisitor method extractGenericsConnectionsFromArguments.
/**
* Resolves type parameters declared by method from type or call arguments.
*/
private Map<GenericsTypeName, GenericsType> extractGenericsConnectionsFromArguments(final GenericsType[] methodGenericTypes, final Parameter[] parameters, final Expression arguments, final GenericsType[] explicitTypeHints) {
Map<GenericsTypeName, GenericsType> resolvedPlaceholders = new HashMap<>();
if (explicitTypeHints != null) {
// resolve type parameters from type arguments
int n = methodGenericTypes.length;
if (n == explicitTypeHints.length) {
for (int i = 0; i < n; i += 1) {
resolvedPlaceholders.put(new GenericsTypeName(methodGenericTypes[i].getName()), explicitTypeHints[i]);
}
}
} else if (parameters.length > 0) {
// resolve type parameters from call arguments
List<Expression> expressions = InvocationWriter.makeArgumentList(arguments).getExpressions();
boolean isVargs = isVargs(parameters);
int nArguments = expressions.size();
int nParams = parameters.length;
if (isVargs ? nArguments >= nParams - 1 : nArguments == nParams) {
for (int i = 0; i < nArguments; i += 1) {
if (isNullConstant(expressions.get(i)))
// GROOVY-9984: skip null
continue;
ClassNode paramType = parameters[Math.min(i, nParams - 1)].getType();
ClassNode argumentType = getDeclaredOrInferredType(expressions.get(i));
if (GenericsUtils.hasUnresolvedGenerics(paramType)) {
// if supplying array param with multiple arguments or single non-array argument, infer using element type
if (isVargs && (i >= nParams || (i == nParams - 1 && (nArguments > nParams || !argumentType.isArray())))) {
paramType = paramType.getComponentType();
}
if (isClosureWithType(argumentType)) {
MethodNode sam = findSAM(paramType);
if (sam != null) {
// adapt closure to functional interface or other single-abstract-method class
argumentType = convertClosureTypeToSAMType(expressions.get(i), argumentType, sam, paramType);
}
}
Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
extractGenericsConnections(connections, wrapTypeIfNecessary(argumentType), paramType);
connections.forEach((gtn, gt) -> resolvedPlaceholders.merge(gtn, gt, (gt1, gt2) -> {
// GROOVY-10339: incorporate another witness
return getCombinedGenericsType(gt1, gt2);
}));
}
}
}
// in case of "<T, U extends Type<T>>" we can learn about "T" from resolved "U"
Map<GenericsTypeName, GenericsType> connections = Arrays.stream(methodGenericTypes).collect(toMap(gt -> new GenericsTypeName(gt.getName()), gt -> gt, (v, x) -> v));
extractGenericsConnectionsForSuperClassAndInterfaces(connections, resolvedPlaceholders);
}
for (GenericsType gt : methodGenericTypes) {
// GROOVY-8409, GROOVY-10067, et al.: provide "no type witness" mapping for param
resolvedPlaceholders.computeIfAbsent(new GenericsTypeName(gt.getName()), gtn -> {
GenericsType xxx = new GenericsType(ClassHelper.makeWithoutCaching("#"), applyGenericsContext(resolvedPlaceholders, gt.getUpperBounds()), applyGenericsContext(resolvedPlaceholders, gt.getLowerBound()));
xxx.getType().setRedirect(gt.getType().redirect());
xxx.putNodeMetaData(GenericsType.class, gt);
xxx.setName("#" + gt.getName());
xxx.setPlaceholder(true);
return xxx;
});
}
return resolvedPlaceholders;
}
Aggregations