use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class ResolveVisitor method resolveGenericsHeader.
private void resolveGenericsHeader(final GenericsType[] types, final GenericsType rootType, final int level) {
if (types == null)
return;
currentClass.setUsingGenerics(true);
List<Tuple2<ClassNode, GenericsType>> upperBoundsWithGenerics = new LinkedList<>();
List<Tuple2<ClassNode, ClassNode>> upperBoundsToResolve = new LinkedList<>();
for (GenericsType type : types) {
if (level > 0 && type.getName().equals(rootType.getName())) {
continue;
}
String name = type.getName();
ClassNode typeType = type.getType();
GenericsTypeName gtn = new GenericsTypeName(name);
boolean isWildcardGT = QUESTION_MARK.equals(name);
boolean dealWithGenerics = (level == 0 || (level > 0 && genericParameterNames.get(gtn) != null));
if (type.getUpperBounds() != null) {
boolean nameAdded = false;
for (ClassNode upperBound : type.getUpperBounds()) {
if (upperBound == null)
continue;
if (!isWildcardGT) {
if (!nameAdded || !resolve(typeType)) {
if (dealWithGenerics) {
type.setPlaceholder(true);
typeType.setRedirect(upperBound);
genericParameterNames.put(gtn, type);
nameAdded = true;
}
}
upperBoundsToResolve.add(tuple(upperBound, typeType));
}
if (upperBound.isUsingGenerics()) {
upperBoundsWithGenerics.add(tuple(upperBound, type));
}
}
} else if (!isWildcardGT) {
if (dealWithGenerics) {
type.setPlaceholder(true);
GenericsType last = genericParameterNames.put(gtn, type);
typeType.setRedirect(last != null ? last.getType().redirect() : ClassHelper.OBJECT_TYPE);
}
}
}
for (Tuple2<ClassNode, ClassNode> tp : upperBoundsToResolve) {
ClassNode upperBound = tp.getV1();
ClassNode classNode = tp.getV2();
resolveOrFail(upperBound, classNode);
}
for (Tuple2<ClassNode, GenericsType> tp : upperBoundsWithGenerics) {
ClassNode upperBound = tp.getV1();
GenericsType gt = tp.getV2();
resolveGenericsHeader(upperBound.getGenericsTypes(), 0 == level ? gt : rootType, level + 1);
}
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class ResolveVisitor method visitConstructorOrMethod.
@Override
protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) {
VariableScope oldScope = currentScope;
currentScope = node.getVariableScope();
Map<GenericsTypeName, GenericsType> oldPNames = genericParameterNames;
genericParameterNames = node.isStatic() && !Traits.isTrait(node.getDeclaringClass()) ? new HashMap<>() : new HashMap<>(genericParameterNames);
resolveGenericsHeader(node.getGenericsTypes());
Parameter[] paras = node.getParameters();
for (Parameter p : paras) {
p.setInitialExpression(transform(p.getInitialExpression()));
resolveOrFail(p.getType(), p.getType());
visitAnnotations(p);
}
resolveOrFail(node.getReturnType(), node);
if (node.getExceptions() != null) {
for (ClassNode t : node.getExceptions()) {
resolveOrFail(t, node);
}
}
MethodNode oldCurrentMethod = currentMethod;
currentMethod = node;
super.visitConstructorOrMethod(node, isConstructor);
currentMethod = oldCurrentMethod;
genericParameterNames = oldPNames;
currentScope = oldScope;
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingSupport method typeCheckMethodsWithGenerics.
private static boolean typeCheckMethodsWithGenerics(final ClassNode receiver, final ClassNode[] argumentTypes, final MethodNode candidateMethod, final boolean isExtensionMethod) {
Parameter[] parameters = candidateMethod.getParameters();
if (parameters.length == 0 || parameters.length > argumentTypes.length) {
// cannot check generic type arguments if there is default argument!
return true;
}
boolean failure = false;
Set<GenericsTypeName> fixedPlaceHolders = Collections.emptySet();
Map<GenericsTypeName, GenericsType> candidateGenerics = new HashMap<>();
// correct receiver for inner class
// we assume the receiver is an instance of the declaring class of the
// candidate method, but findMethod() returns also outer class methods
// for that receiver; for now we skip receiver-based checks in that case
boolean skipBecauseOfInnerClassNotReceiver = !implementsInterfaceOrIsSubclassOf(receiver, candidateMethod.getDeclaringClass());
if (!skipBecauseOfInnerClassNotReceiver) {
if (candidateMethod instanceof ConstructorNode) {
candidateGenerics = GenericsUtils.extractPlaceholders(receiver);
fixedPlaceHolders = new HashSet<>(candidateGenerics.keySet());
} else {
failure = inferenceCheck(fixedPlaceHolders, candidateGenerics, candidateMethod.getDeclaringClass(), receiver, false);
GenericsType[] gts = candidateMethod.getGenericsTypes();
if (candidateMethod.isStatic()) {
// not in scope
candidateGenerics.clear();
} else if (gts != null) {
// first remove hidden params
for (GenericsType gt : gts) {
candidateGenerics.remove(new GenericsTypeName(gt.getName()));
}
// GROOVY-8034: non-static method may use class generics
gts = applyGenericsContext(candidateGenerics, gts);
}
GenericsUtils.extractPlaceholders(makeClassSafe0(OBJECT_TYPE, gts), candidateGenerics);
// the outside context parts till now define placeholder we are not allowed to
// generalize, thus we save that for later use...
// extension methods are special, since they set the receiver as
// first parameter. While we normally allow generalization for the first
// parameter, in case of an extension method we must not.
fixedPlaceHolders = extractResolvedPlaceHolders(candidateGenerics);
}
}
int lastParamIndex = parameters.length - 1;
for (int i = 0, n = argumentTypes.length; i < n; i += 1) {
ClassNode parameterType = parameters[Math.min(i, lastParamIndex)].getOriginType();
ClassNode argumentType = StaticTypeCheckingVisitor.wrapTypeIfNecessary(argumentTypes[i]);
failure |= inferenceCheck(fixedPlaceHolders, candidateGenerics, parameterType, argumentType, i >= lastParamIndex);
if (i == 0 && isExtensionMethod) {
// re-load fixed names for extension
fixedPlaceHolders = extractResolvedPlaceHolders(candidateGenerics);
}
}
return !failure;
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingSupport method addMissingEntries.
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();
ClassNode cn = gt.getType();
if (cn.redirect() == UNKNOWN_PARAMETER_TYPE)
continue;
resolved.put(entry.getKey(), gt);
}
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingSupport method applyGenericsConnections.
static void applyGenericsConnections(final Map<GenericsTypeName, GenericsType> connections, final Map<GenericsTypeName, GenericsType> resolvedPlaceholders) {
if (connections == null || connections.isEmpty())
return;
int count = 0;
while (count++ < 10000) {
boolean checkForMorePlaceholders = false;
for (Map.Entry<GenericsTypeName, GenericsType> entry : resolvedPlaceholders.entrySet()) {
// entry could be T=T, T=T extends U, T=V, T=String, T=? extends String, etc.
GenericsType oldValue = entry.getValue();
if (oldValue.isPlaceholder()) {
// T=T or V, not T=String or ? ...
GenericsTypeName name = new GenericsTypeName(oldValue.getName());
// find "V" in T=V
GenericsType newValue = connections.get(name);
if (newValue == oldValue)
continue;
if (newValue == null) {
newValue = connections.get(entry.getKey());
if (newValue != null) {
// GROOVY-10315, GROOVY-10317
newValue = getCombinedGenericsType(oldValue, newValue);
}
}
if (newValue == null) {
entry.setValue(newValue = applyGenericsContext(connections, oldValue));
if (!checkForMorePlaceholders) {
checkForMorePlaceholders = !equalIncludingGenerics(oldValue, newValue);
}
} else if (!newValue.isPlaceholder() || newValue != resolvedPlaceholders.get(name)) {
// GROOVY-6787: Don't override the original if the replacement doesn't respect the bounds otherwise
// the original bounds are lost, which can result in accepting an incompatible type as an argument!
ClassNode replacementType = extractType(newValue);
ClassNode suitabilityType = !replacementType.isGenericsPlaceHolder() ? replacementType : Optional.ofNullable(replacementType.getGenericsTypes()).map(gts -> extractType(gts[0])).orElse(replacementType.redirect());
if (oldValue.isCompatibleWith(suitabilityType)) {
if (newValue.isWildcard() && newValue.getLowerBound() == null && newValue.getUpperBounds() == null) {
// GROOVY-9998: apply upper/lower bound for unknown
entry.setValue(replacementType.asGenericsType());
} else {
entry.setValue(newValue);
}
if (!checkForMorePlaceholders && newValue.isPlaceholder()) {
checkForMorePlaceholders = !equalIncludingGenerics(oldValue, newValue);
}
}
}
}
}
if (!checkForMorePlaceholders)
break;
}
if (count >= 10000) {
throw new GroovyBugError("unable to handle generics in " + resolvedPlaceholders + " with connections " + connections);
}
}
Aggregations