Skip to content

Commit

Permalink
GROOVY-10053
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Apr 26, 2021
1 parent 040a09c commit 9615572
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2310,4 +2310,29 @@ public void testTypeChecked10052() {

runConformTest(sources, "xy");
}

@Test
public void testTypeChecked10053() {
if (Float.parseFloat(System.getProperty("java.specification.version")) > 8)
vmArguments = new String[] {"--add-opens", "java.base/java.util.stream=ALL-UNNAMED"};

//@formatter:off
String[] sources = {
"Main.groovy",
"Set<Number> f() {\n" +
" Collections.<Number>singleton(42)\n" +
"}\n" +
"@groovy.transform.TypeChecked\n" +
"def <N extends Number> Set<N> g(Class<N> t) {\n" +
" Set<N> result = new HashSet<>()\n" +
" f().stream().filter{n -> t.isInstance(n)}\n" +
" .map{n -> t.cast(n)}.forEach{n -> result.add(n)}\n" +
" return result\n" +
"}\n" +
"print g(Integer)\n",
};
//@formatter:on

runConformTest(sources, "[42]");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1790,12 +1790,17 @@ static void applyGenericsConnections(
// 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);
/* GRECLIPSE edit -- GROOVY-9998, GROOVY-10053
if (oldValue.isCompatibleWith(replacementType)) {
// GRECLIPSE add -- GROOVY-9998
*/
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) {
entry.setValue(new GenericsType(replacementType));
} else
// GRECLIPSE end
// GRECLIPSE end
entry.setValue(newValue);
if (newValue.isPlaceholder()) {
checkForMorePlaceholders = checkForMorePlaceholders || !equalIncludingGenerics(oldValue, newValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2832,29 +2832,21 @@ public void visitClosureExpression(final ClosureExpression expression) {
}
}

// GRECLIPSE add -- GROOVY-9751
// GRECLIPSE add -- GROOVY-9751, GROOVY-9803, GROOVY-10053
@Override
public void visitMethodPointerExpression(final MethodPointerExpression expression) {
super.visitMethodPointerExpression(expression);
Expression nameExpr = expression.getMethodName();
if (nameExpr instanceof ConstantExpression
&& getType(nameExpr).equals(STRING_TYPE)) {
String nameText = nameExpr.getText();

if ("new".equals(nameText)) {
ClassNode receiverType = getType(expression.getExpression());
if (isClassClassNodeWrappingConcreteType(receiverType)) {
storeType(expression, wrapClosureType(receiverType));
}
return;
}

List<Receiver<String>> receivers = new ArrayList<>();
addReceivers(receivers, makeOwnerList(expression.getExpression()), false);

ClassNode receiverType = null;
List<MethodNode> candidates = EMPTY_METHODNODE_LIST;
for (Receiver<String> currentReceiver : receivers) {
ClassNode receiverType = wrapTypeIfNecessary(currentReceiver.getType());
receiverType = wrapTypeIfNecessary(currentReceiver.getType());

candidates = findMethodsWithGenerated(receiverType, nameText);
if (isBeingCompiled(receiverType)) candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
Expand All @@ -2867,11 +2859,16 @@ && getType(nameExpr).equals(STRING_TYPE)) {
}

if (!candidates.isEmpty()) {
candidates.stream().map(MethodNode::getReturnType)
.reduce(WideningCategories::lowestUpperBound)
.filter(returnType -> !returnType.equals(OBJECT_TYPE))
.ifPresent(returnType -> storeType(expression, wrapClosureType(returnType)));
expression.putNodeMetaData(MethodNode.class, candidates); // GROOVY-9803
Map<GenericsTypeName, GenericsType> gts = GenericsUtils.extractPlaceholders(receiverType);
candidates.stream().map(candidate -> applyGenericsContext(gts, candidate.getReturnType()))
.reduce(WideningCategories::lowestUpperBound).ifPresent(returnType -> {
storeType(expression, wrapClosureType(returnType));
});
expression.putNodeMetaData(MethodNode.class, candidates);
} else {
ClassNode type = wrapTypeIfNecessary(getType(expression.getExpression()));
if (isClassClassNodeWrappingConcreteType(type)) type = type.getGenericsTypes()[0].getType();
addStaticTypeError("Cannot find matching method " + type.getText() + "#" + nameText + ". Please check if the declared type is correct and if the method exists.", nameExpr);
}
}
}
Expand Down Expand Up @@ -6010,7 +6007,7 @@ private static void extractGenericsConnectionsForSuperClassAndInterfaces(final M
}
}

// GRECLIPSE add -- GROOVY-9803
// GRECLIPSE add -- GROOVY-9803, GROOVY-10053
private static MethodNode chooseMethod(final MethodPointerExpression source, final Supplier<ClassNode[]> samSignature) {
List<MethodNode> options = source.getNodeMetaData(MethodNode.class);
if (options == null || options.isEmpty()) {
Expand All @@ -6020,15 +6017,17 @@ private static MethodNode chooseMethod(final MethodPointerExpression source, fin
ClassNode[] paramTypes = samSignature.get();
return options.stream().filter((MethodNode option) -> {
ClassNode[] types = collateMethodReferenceParameterTypes(source, option);
if (types.length == paramTypes.length) {
for (int i = 0, n = types.length; i < n; i += 1) {
if (!types[i].isGenericsPlaceHolder() && !isAssignableTo(types[i], paramTypes[i])) {
return false;
}
final int n = types.length;
if (n != paramTypes.length) {
return false;
}
for (int i = 0; i < n; i += 1) {
// param type represents incoming argument type
if (!isAssignableTo(paramTypes[i], types[i])) {
return false;
}
return true;
}
return false;
return true;
}).findFirst().orElse(null); // TODO: order matches by param distance
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1704,12 +1704,17 @@ static void applyGenericsConnections(final Map<GenericsTypeName, GenericsType> c
// 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);
/* GRECLIPSE edit -- GROOVY-9998, GROOVY-10053
if (oldValue.isCompatibleWith(replacementType)) {
// GRECLIPSE add -- GROOVY-9998
*/
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) {
entry.setValue(new GenericsType(replacementType));
} else
// GRECLIPSE end
// GRECLIPSE end
entry.setValue(newValue);
if (!checkForMorePlaceholders && newValue.isPlaceholder()) {
checkForMorePlaceholders = !equalIncludingGenerics(oldValue, newValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2552,9 +2552,12 @@ && getType(nameExpr).equals(STRING_TYPE)) {
List<Receiver<String>> receivers = new ArrayList<>();
addReceivers(receivers, makeOwnerList(expression.getExpression()), false);

// GRECLIPSE add -- GROOVY-10053
ClassNode receiverType = null;
// GRECLIPSE end
List<MethodNode> candidates = EMPTY_METHODNODE_LIST;
for (Receiver<String> currentReceiver : receivers) {
ClassNode receiverType = wrapTypeIfNecessary(currentReceiver.getType());
receiverType = wrapTypeIfNecessary(currentReceiver.getType());

candidates = findMethodsWithGenerated(receiverType, nameText);
if (isBeingCompiled(receiverType)) candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
Expand All @@ -2574,10 +2577,18 @@ && getType(nameExpr).equals(STRING_TYPE)) {
}

if (!candidates.isEmpty()) {
/* GRECLIPSE edit -- GROOVY-10053
candidates.stream().map(MethodNode::getReturnType)
.reduce(WideningCategories::lowestUpperBound)
.filter(returnType -> !returnType.equals(OBJECT_TYPE))
.ifPresent(returnType -> storeType(expression, wrapClosureType(returnType)));
*/
Map<GenericsTypeName, GenericsType> gts = GenericsUtils.extractPlaceholders(receiverType);
candidates.stream().map(candidate -> applyGenericsContext(gts, candidate.getReturnType()))
.reduce(WideningCategories::lowestUpperBound).ifPresent(returnType -> {
storeType(expression, wrapClosureType(returnType));
});
// GRECLIPSE end
expression.putNodeMetaData(MethodNode.class, candidates);
} else if (!(expression instanceof MethodReferenceExpression)) {
ClassNode type = wrapTypeIfNecessary(getType(expression.getExpression()));
Expand Down Expand Up @@ -5715,6 +5726,7 @@ private static MethodNode chooseMethod(final MethodPointerExpression source, fin
ClassNode[] paramTypes = samSignature.get();
return options.stream().filter((MethodNode option) -> {
ClassNode[] types = collateMethodReferenceParameterTypes(source, option);
/* GRECLIPSE edit -- GROOVY-10053
if (types.length == paramTypes.length) {
for (int i = 0, n = types.length; i < n; i += 1) {
if (!types[i].isGenericsPlaceHolder() && !isAssignableTo(types[i], paramTypes[i])) {
Expand All @@ -5724,6 +5736,19 @@ private static MethodNode chooseMethod(final MethodPointerExpression source, fin
return true;
}
return false;
*/
final int n = types.length;
if (n != paramTypes.length) {
return false;
}
for (int i = 0; i < n; i += 1) {
// param type represents incoming argument type
if (!isAssignableTo(paramTypes[i], types[i])) {
return false;
}
}
return true;
// GRECLIPSE end
}).findFirst().orElse(null); // TODO: order matches by param distance
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1646,7 +1646,14 @@ static void applyGenericsConnections(final Map<GenericsTypeName, GenericsType> c
// 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);
/* GRECLIPSE edit -- GROOVY-10053: accept type parameter from context as type argument for target
if (oldValue.isCompatibleWith(replacementType)) {
*/
ClassNode suitabilityType = !replacementType.isGenericsPlaceHolder()
? replacementType : Optional.ofNullable(replacementType.getGenericsTypes())
.map(gts -> extractType(gts[0])).orElse(replacementType.redirect());
if (oldValue.isCompatibleWith(suitabilityType)) {
// GRECLIPSE end
if (newValue.isWildcard() && newValue.getLowerBound() == null && newValue.getUpperBounds() == null) {
// GROOVY-9998: apply upper/lower bound for unknown
entry.setValue(new GenericsType(replacementType));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2468,9 +2468,12 @@ && getType(nameExpr).equals(STRING_TYPE)) {
List<Receiver<String>> receivers = new ArrayList<>();
addReceivers(receivers, makeOwnerList(expression.getExpression()), false);

// GRECLIPSE add -- GROOVY-10053
ClassNode receiverType = null;
// GRECLIPSE end
List<MethodNode> candidates = EMPTY_METHODNODE_LIST;
for (Receiver<String> currentReceiver : receivers) {
ClassNode receiverType = wrapTypeIfNecessary(currentReceiver.getType());
receiverType = wrapTypeIfNecessary(currentReceiver.getType());

candidates = findMethodsWithGenerated(receiverType, nameText);
if (isBeingCompiled(receiverType)) candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
Expand All @@ -2490,10 +2493,18 @@ && getType(nameExpr).equals(STRING_TYPE)) {
}

if (!candidates.isEmpty()) {
/* GRECLIPSE edit -- GROOVY-10053
candidates.stream().map(MethodNode::getReturnType)
.reduce(WideningCategories::lowestUpperBound)
.filter(returnType -> !returnType.equals(OBJECT_TYPE))
.ifPresent(returnType -> storeType(expression, wrapClosureType(returnType)));
*/
Map<GenericsTypeName, GenericsType> gts = GenericsUtils.extractPlaceholders(receiverType);
candidates.stream().map(candidate -> applyGenericsContext(gts, candidate.getReturnType()))
.reduce(WideningCategories::lowestUpperBound).ifPresent(returnType -> {
storeType(expression, wrapClosureType(returnType));
});
// GRECLIPSE end
expression.putNodeMetaData(MethodNode.class, candidates);
} else if (!(expression instanceof MethodReferenceExpression)) {
ClassNode type = wrapTypeIfNecessary(getType(expression.getExpression()));
Expand Down Expand Up @@ -5493,6 +5504,7 @@ private static MethodNode chooseMethod(final MethodPointerExpression source, fin
ClassNode[] paramTypes = samSignature.get();
return options.stream().filter((MethodNode option) -> {
ClassNode[] types = collateMethodReferenceParameterTypes(source, option);
/* GRECLIPSE edit -- GROOVY-10053
if (types.length == paramTypes.length) {
for (int i = 0, n = types.length; i < n; i += 1) {
if (!types[i].isGenericsPlaceHolder() && !isAssignableTo(types[i], paramTypes[i])) {
Expand All @@ -5502,6 +5514,19 @@ private static MethodNode chooseMethod(final MethodPointerExpression source, fin
return true;
}
return false;
*/
final int n = types.length;
if (n != paramTypes.length) {
return false;
}
for (int i = 0; i < n; i += 1) {
// param type represents incoming argument type
if (!isAssignableTo(paramTypes[i], types[i])) {
return false;
}
}
return true;
// GRECLIPSE end
}).findFirst().orElse(null); // TODO: order matches by param distance
}

Expand Down

0 comments on commit 9615572

Please sign in to comment.