Skip to content
Permalink
Browse files
GROOVY-10528: STC: fix NPE for raw type inference
  • Loading branch information
eric-milles committed Mar 29, 2022
1 parent b479470 commit 14b1aa711afb5ba197640b6a6addb7102aed0d78
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 38 deletions.
@@ -3051,17 +3051,11 @@ private void doInferClosureParameterTypes(final ClassNode receiver, final Expres
List<ClassNode[]> closureSignatures = getSignaturesFromHint(selectedMethod, hintClass, options, expression);
List<ClassNode[]> candidates = new LinkedList<>();
for (ClassNode[] signature : closureSignatures) {
// in order to compute the inferred types of the closure parameters, we're using the following trick:
// 1. create a dummy MethodNode for which the return type is a class node for which the generic types are the types returned by the hint
// 2. call inferReturnTypeGenerics
// 3. fetch inferred types from the result of inferReturnTypeGenerics
// In practice, it could be done differently but it has the main advantage of reusing
// existing code, hence reducing the amount of code to debug in case of failure.
ClassNode[] inferred = resolveGenericsFromTypeHint(receiver, arguments, selectedMethod, signature);
resolveGenericsFromTypeHint(receiver, arguments, selectedMethod, signature);
if (signature.length == closureParams.length // same number of arguments
|| (signature.length == 1 && closureParams.length == 0) // implicit it
|| (closureParams.length > signature.length && inferred[inferred.length - 1].isArray())) { // vargs
candidates.add(inferred);
|| (closureParams.length > signature.length && last(signature).isArray())) { // vargs
candidates.add(signature);
}
}
if (candidates.size() > 1) {
@@ -3119,47 +3113,51 @@ private void doInferClosureParameterTypes(final ClassNode receiver, final Expres
}
}

private ClassNode[] resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
ClassNode dummyResultNode = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
GenericsType[] genericTypes = new GenericsType[signature.length];
for (int i = 0, n = signature.length; i < n; i += 1) {
genericTypes[i] = new GenericsType(signature[i]);
}
dummyResultNode.setGenericsTypes(genericTypes);
/**
* Computes the inferred types of the closure parameters using the following trick:
* <ol>
* <li> creates a dummy MethodNode for which the return type is a class node
* for which the generic types are the types returned by the hint
* <li> calls {@link #inferReturnTypeGenerics}
* <li> returns inferred types from the result
* </ol>
* In practice it could be done differently but it has the main advantage of
* reusing existing code, hence reducing the amount of code to debug in case
* of failure.
*/
private void resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
returnType.setGenericsTypes(Arrays.stream(signature).map(ClassNode::asGenericsType).toArray(GenericsType[]::new));

MethodNode dummyMN = selectedMethod instanceof ExtensionMethodNode ? ((ExtensionMethodNode) selectedMethod).getExtensionMethodNode() : selectedMethod;
dummyMN = new MethodNode(
dummyMN.getName(),
dummyMN.getModifiers(),
dummyResultNode,
dummyMN.getParameters(),
dummyMN.getExceptions(),
EmptyStatement.INSTANCE
);
dummyMN = new MethodNode(dummyMN.getName(), dummyMN.getModifiers(), returnType, dummyMN.getParameters(), dummyMN.getExceptions(), null);
dummyMN.setDeclaringClass(selectedMethod.getDeclaringClass());
dummyMN.setGenericsTypes(selectedMethod.getGenericsTypes());
if (selectedMethod instanceof ExtensionMethodNode) {
ExtensionMethodNode orig = (ExtensionMethodNode) selectedMethod;
dummyMN = new ExtensionMethodNode(
dummyMN,
dummyMN.getName(),
dummyMN.getModifiers(),
dummyResultNode,
orig.getParameters(),
orig.getExceptions(),
EmptyStatement.INSTANCE,
orig.isStaticExtension()
returnType,
selectedMethod.getParameters(),
selectedMethod.getExceptions(),
null,
((ExtensionMethodNode) selectedMethod).isStaticExtension()
);
dummyMN.setDeclaringClass(orig.getDeclaringClass());
dummyMN.setGenericsTypes(orig.getGenericsTypes());
dummyMN.setDeclaringClass(selectedMethod.getDeclaringClass());
dummyMN.setGenericsTypes(selectedMethod.getGenericsTypes());
}
ClassNode returnType = inferReturnTypeGenerics(receiver, dummyMN, arguments);

returnType = inferReturnTypeGenerics(receiver, dummyMN, arguments);
GenericsType[] returnTypeGenerics = returnType.getGenericsTypes();
ClassNode[] inferred = new ClassNode[returnTypeGenerics.length];
for (int i = 0, n = returnTypeGenerics.length; i < n; i += 1) {
GenericsType genericsType = returnTypeGenerics[i];
inferred[i] = getCombinedBoundType(genericsType);
GenericsType gt = returnTypeGenerics[i];
if (gt.isPlaceholder()) { // GROOVY-9968, GROOVY-10528, et al.
signature[i] = gt.getUpperBounds() != null ? gt.getUpperBounds()[0] : gt.getType().redirect();
} else {
signature[i] = getCombinedBoundType(gt);
}
}
return inferred;
}

private static String[] convertToStringArray(final Expression options) {
@@ -3206,7 +3204,7 @@ private void checkClosureWithDelegatesTo(final ClassNode receiver, final MethodN
);
if (resolved != null) {
if (resolved.length == 1) {
resolved = resolveGenericsFromTypeHint(receiver, arguments, mn, resolved);
resolveGenericsFromTypeHint(receiver, arguments, mn, resolved);
expression.putNodeMetaData(DELEGATION_METADATA, newDelegationMetadata(resolved[0], stInt));
} else {
addStaticTypeError("Incorrect type hint found in method " + (mn), type);
@@ -4390,6 +4390,20 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}

// GROOVY-10528
void testRawTypeGuard() {
assertScript '''
void test(... args) {
args.each { object ->
if (object instanceof Iterable) {
object.each { test(it) }
}
}
}
test(1,[2,3])
'''
}

//--------------------------------------------------------------------------

static class MyList

0 comments on commit 14b1aa7

Please sign in to comment.