Skip to content

Commit b454948

Browse files
committed
GROOVY-9977
1 parent a8c2423 commit b454948

File tree

4 files changed

+173
-23
lines changed

4 files changed

+173
-23
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java

+21
Original file line numberDiff line numberDiff line change
@@ -1262,4 +1262,25 @@ public void testTypeChecked9974() {
12621262

12631263
runConformTest(sources, "true");
12641264
}
1265+
1266+
@Test
1267+
public void testTypeChecked9977() {
1268+
//@formatter:off
1269+
String[] sources = {
1270+
"Main.groovy",
1271+
"@groovy.transform.TypeChecked\n" +
1272+
"class C {\n" +
1273+
" public final Comparator<Integer> f = { a, b -> Integer.compare(a, b) }\n" +
1274+
" \n" +
1275+
" final Comparator<Integer> p = { a, b -> Integer.compare(a, b) }\n" +
1276+
" def m() {\n" +
1277+
" Comparator<Integer> v = { a, b -> Integer.compare(a, b) }\n" +
1278+
" }\n" +
1279+
"}\n" +
1280+
"print new C().getP().compare(0, 1)\n",
1281+
};
1282+
//@formatter:on
1283+
1284+
runConformTest(sources, "-1");
1285+
}
12651286
}

base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+66-7
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
import java.util.Set;
133133
import java.util.concurrent.atomic.AtomicLong;
134134
import java.util.concurrent.atomic.AtomicReference;
135+
import java.util.function.Function;
135136
import java.util.function.Supplier;
136137

137138
import static org.apache.groovy.ast.tools.ClassNodeUtils.samePackageName;
@@ -186,7 +187,6 @@
186187
import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
187188
import static org.codehaus.groovy.ast.tools.GeneralUtils.thisPropX;
188189
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
189-
import static org.codehaus.groovy.ast.tools.GenericsUtils.extractPlaceholders;
190190
import static org.codehaus.groovy.ast.tools.GenericsUtils.toGenericTypesString;
191191
import static org.codehaus.groovy.ast.tools.WideningCategories.isBigDecCategory;
192192
import static org.codehaus.groovy.ast.tools.WideningCategories.isBigIntCategory;
@@ -858,7 +858,8 @@ public void visitBinaryExpression(final BinaryExpression expression) {
858858
try {
859859
final Expression leftExpression = expression.getLeftExpression();
860860
final Expression rightExpression = expression.getRightExpression();
861-
leftExpression.visit(this);
861+
862+
leftExpression.visit(this); ClassNode lType;
862863
SetterInfo setterInfo = removeSetterInfo(leftExpression);
863864
if (setterInfo != null) {
864865
if (ensureValidSetter(expression, leftExpression, rightExpression, setterInfo)) {
@@ -867,12 +868,18 @@ public void visitBinaryExpression(final BinaryExpression expression) {
867868
// GRECLIPSE end
868869
return;
869870
}
870-
871+
lType = getType(leftExpression);
871872
} else {
873+
// GRECLIPSE add -- GROOVY-9977
874+
lType = getType(leftExpression);
875+
if (op == ASSIGN && isFunctionalInterface(lType)) {
876+
processFunctionalInterfaceAssignment(lType, rightExpression);
877+
}
878+
// GRECLIPSE end
872879
rightExpression.visit(this);
873880
}
874-
ClassNode lType = getType(leftExpression);
875881
/* GRECLIPSE edit -- GROOVY-9953
882+
ClassNode lType = getType(leftExpression);
876883
ClassNode rType = getType(rightExpression);
877884
if (isNullConstant(rightExpression)) {
878885
if (!isPrimitiveType(lType))
@@ -1033,6 +1040,37 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
10331040
}
10341041
}
10351042

1043+
// GRECLIPSE add
1044+
private static boolean isFunctionalInterface(final ClassNode type) {
1045+
return type.isInterface() && isSAMType(type);
1046+
}
1047+
1048+
private void processFunctionalInterfaceAssignment(final ClassNode lhsType, final Expression rhsExpression) {
1049+
if (rhsExpression instanceof ClosureExpression) {
1050+
MethodNode abstractMethod = ClassHelper.findSAM(lhsType);
1051+
Map<GenericsType, GenericsType> mappings = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(abstractMethod.getDeclaringClass(), lhsType);
1052+
Function<ClassNode, ClassNode> resolver = t -> t.isGenericsPlaceHolder() ? GenericsUtils.findActualTypeByGenericsPlaceholderName(t.getUnresolvedName(), mappings) : t;
1053+
1054+
ClassNode[] samParameterTypes = Arrays.stream(abstractMethod.getParameters()).map(Parameter::getType).map(resolver).toArray(ClassNode[]::new);
1055+
Parameter[] closureParameters = getParametersSafe((ClosureExpression) rhsExpression);
1056+
int n = closureParameters.length;
1057+
if (n == samParameterTypes.length) {
1058+
for (int i = 0; i < n; i += 1) {
1059+
Parameter parameter = closureParameters[i];
1060+
if (parameter.isDynamicTyped()) {
1061+
parameter.setType(samParameterTypes[i]);
1062+
parameter.setOriginType(samParameterTypes[i]);
1063+
}
1064+
}
1065+
} else {
1066+
addStaticTypeError("Wrong number of parameters: ", rhsExpression);
1067+
}
1068+
1069+
storeInferredReturnType(rhsExpression, resolver.apply(abstractMethod.getReturnType()));
1070+
}
1071+
}
1072+
// GRECLIPSE end
1073+
10361074
/**
10371075
* Given a binary expression corresponding to an assignment, will check that the type of the RHS matches one
10381076
* of the possible setters and if not, throw a type checking error.
@@ -1995,7 +2033,13 @@ public void visitProperty(PropertyNode node) {
19952033
try {
19962034
typeCheckingContext.isInStaticContext = node.isInStaticContext();
19972035
currentProperty = node;
2036+
/* GRECLIPSE edit -- GROOVY-9977
19982037
super.visitProperty(node);
2038+
*/
2039+
visitAnnotations(node);
2040+
visitClassCodeContainer(node.getGetterBlock());
2041+
visitClassCodeContainer(node.getSetterBlock());
2042+
// GRECLIPSE end
19992043
} finally {
20002044
currentProperty = null;
20012045
typeCheckingContext.isInStaticContext = osc;
@@ -2008,9 +2052,24 @@ public void visitField(final FieldNode node) {
20082052
try {
20092053
typeCheckingContext.isInStaticContext = node.isInStaticContext();
20102054
currentField = node;
2055+
/* GRECLIPSE edit -- GROOVY-9977
20112056
super.visitField(node);
2057+
*/
2058+
visitAnnotations(node);
2059+
// GRECLIPSE end
20122060
Expression init = node.getInitialExpression();
20132061
if (init != null) {
2062+
// GRECLIPSE add -- GROOVY-9977
2063+
ClassNode lType = getType(node);
2064+
if (isFunctionalInterface(lType)) {
2065+
processFunctionalInterfaceAssignment(lType, init);
2066+
}
2067+
init.visit(this);
2068+
ClassNode rType = getType(init);
2069+
if (init instanceof ConstructorCallExpression) {
2070+
inferDiamondType((ConstructorCallExpression) init, lType);
2071+
}
2072+
// GRECLIPSE end
20142073
FieldExpression left = new FieldExpression(node);
20152074
BinaryExpression bexp = binX(
20162075
left,
@@ -2020,13 +2079,13 @@ public void visitField(final FieldNode node) {
20202079
bexp.setSourcePosition(init);
20212080
/* GRECLIPSE edit -- GROOVY-9882
20222081
typeCheckAssignment(bexp, left, node.getOriginType(), init, getType(init));
2023-
*/
20242082
ClassNode lType = node.getOriginType(), rType = getType(init);
2025-
typeCheckAssignment(bexp, left, lType, init, getResultType(lType, ASSIGN, rType, bexp));
2026-
// GRECLIPSE end
20272083
if (init instanceof ConstructorCallExpression) {
20282084
inferDiamondType((ConstructorCallExpression) init, node.getOriginType());
20292085
}
2086+
*/
2087+
typeCheckAssignment(bexp, left, lType, init, getResultType(lType, ASSIGN, rType, bexp));
2088+
// GRECLIPSE end
20302089
}
20312090
} finally {
20322091
currentField = null;

base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+43-8
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,7 @@ public void visitBinaryExpression(final BinaryExpression expression) {
766766
}
767767
} else {
768768
lType = getType(leftExpression);
769+
/* GRECLIPSE edit -- GROOVY-9977
769770
boolean isFunctionalInterface = isFunctionalInterface(lType);
770771
if (isFunctionalInterface && rightExpression instanceof MethodReferenceExpression) {
771772
LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(lType);
@@ -778,7 +779,11 @@ public void visitBinaryExpression(final BinaryExpression expression) {
778779
} else if (op == ASSIGN && isFunctionalInterface && rightExpression instanceof ClosureExpression) {
779780
inferParameterAndReturnTypesOfClosureOnRHS(lType, (ClosureExpression) rightExpression);
780781
}
781-
782+
*/
783+
if (op == ASSIGN && isFunctionalInterface(lType)) {
784+
processFunctionalInterfaceAssignment(lType, rightExpression);
785+
}
786+
// GRECLIPSE end
782787
rightExpression.visit(this);
783788
}
784789

@@ -943,6 +948,20 @@ private void validateResourceInARM(final BinaryExpression expression, final Clas
943948
}
944949
}
945950

951+
// GRECLIPSE add
952+
private void processFunctionalInterfaceAssignment(final ClassNode lhsType, final Expression rhsExpression) {
953+
if (rhsExpression instanceof ClosureExpression) {
954+
inferParameterAndReturnTypesOfClosureOnRHS(lhsType, (ClosureExpression) rhsExpression);
955+
} else if (rhsExpression instanceof MethodReferenceExpression) {
956+
LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(lhsType);
957+
958+
inferParameterAndReturnTypesOfClosureOnRHS(lhsType, lambdaExpression);
959+
rhsExpression.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
960+
rhsExpression.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambdaExpression.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
961+
}
962+
}
963+
// GRECLIPSE end
964+
946965
private void inferParameterAndReturnTypesOfClosureOnRHS(final ClassNode lhsType, final ClosureExpression rhsExpression) {
947966
Tuple2<ClassNode[], ClassNode> typeInfo = GenericsUtils.parameterizeSAM(lhsType);
948967
Parameter[] closureParameters = getParametersSafe(rhsExpression);
@@ -1886,7 +1905,13 @@ public void visitProperty(final PropertyNode node) {
18861905
try {
18871906
typeCheckingContext.isInStaticContext = node.isInStaticContext();
18881907
currentProperty = node;
1908+
/* GRECLIPSE edit -- GROOVY-9977
18891909
super.visitProperty(node);
1910+
*/
1911+
visitAnnotations(node);
1912+
visitClassCodeContainer(node.getGetterBlock());
1913+
visitClassCodeContainer(node.getSetterBlock());
1914+
// GRECLIPSE end
18901915
} finally {
18911916
currentProperty = null;
18921917
typeCheckingContext.isInStaticContext = osc;
@@ -1899,9 +1924,24 @@ public void visitField(final FieldNode node) {
18991924
try {
19001925
typeCheckingContext.isInStaticContext = node.isInStaticContext();
19011926
currentField = node;
1927+
/* GRECLIPSE edit -- GROOVY-9977
19021928
super.visitField(node);
1929+
*/
1930+
visitAnnotations(node);
1931+
// GRECLIPSE end
19031932
Expression init = node.getInitialExpression();
19041933
if (init != null) {
1934+
// GRECLIPSE add -- GROOVY-9977
1935+
ClassNode lType = getType(node);
1936+
if (isFunctionalInterface(lType)) {
1937+
processFunctionalInterfaceAssignment(lType, init);
1938+
}
1939+
init.visit(this);
1940+
ClassNode rType = getType(init);
1941+
if (init instanceof ConstructorCallExpression) {
1942+
inferDiamondType((ConstructorCallExpression) init, lType);
1943+
}
1944+
// GRECLIPSE end
19051945
FieldExpression left = new FieldExpression(node);
19061946
BinaryExpression bexp = binX(
19071947
left,
@@ -1911,17 +1951,12 @@ public void visitField(final FieldNode node) {
19111951
bexp.setSourcePosition(init);
19121952
/* GRECLIPSE edit -- GROOVY-9882
19131953
typeCheckAssignment(bexp, left, node.getOriginType(), init, getType(init));
1914-
*/
19151954
ClassNode lType = node.getOriginType(), rType = getType(init);
1916-
typeCheckAssignment(bexp, left, lType, init, getResultType(lType, ASSIGN, rType, bexp));
1917-
// GRECLIPSE end
19181955
if (init instanceof ConstructorCallExpression) {
19191956
inferDiamondType((ConstructorCallExpression) init, node.getOriginType());
19201957
}
1921-
// GRECLIPSE add
1922-
else if (init instanceof ClosureExpression && isFunctionalInterface(lType)) {
1923-
inferParameterAndReturnTypesOfClosureOnRHS(lType, (ClosureExpression) init);
1924-
}
1958+
*/
1959+
typeCheckAssignment(bexp, left, lType, init, getResultType(lType, ASSIGN, rType, bexp));
19251960
// GRECLIPSE end
19261961
}
19271962
} finally {

base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+43-8
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,7 @@ public void visitBinaryExpression(final BinaryExpression expression) {
766766
}
767767
} else {
768768
lType = getType(leftExpression);
769+
/* GRECLIPSE edit -- GROOVY-9977
769770
boolean isFunctionalInterface = isFunctionalInterface(lType);
770771
if (isFunctionalInterface && rightExpression instanceof MethodReferenceExpression) {
771772
LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(lType);
@@ -778,7 +779,11 @@ public void visitBinaryExpression(final BinaryExpression expression) {
778779
} else if (op == ASSIGN && isFunctionalInterface && rightExpression instanceof ClosureExpression) {
779780
inferParameterAndReturnTypesOfClosureOnRHS(lType, (ClosureExpression) rightExpression);
780781
}
781-
782+
*/
783+
if (op == ASSIGN && isFunctionalInterface(lType)) {
784+
processFunctionalInterfaceAssignment(lType, rightExpression);
785+
}
786+
// GRECLIPSE end
782787
rightExpression.visit(this);
783788
}
784789

@@ -941,6 +946,20 @@ private void validateResourceInARM(final BinaryExpression expression, final Clas
941946
}
942947
}
943948

949+
// GRECLIPSE add
950+
private void processFunctionalInterfaceAssignment(final ClassNode lhsType, final Expression rhsExpression) {
951+
if (rhsExpression instanceof ClosureExpression) {
952+
inferParameterAndReturnTypesOfClosureOnRHS(lhsType, (ClosureExpression) rhsExpression);
953+
} else if (rhsExpression instanceof MethodReferenceExpression) {
954+
LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(lhsType);
955+
956+
inferParameterAndReturnTypesOfClosureOnRHS(lhsType, lambdaExpression);
957+
rhsExpression.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
958+
rhsExpression.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambdaExpression.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
959+
}
960+
}
961+
// GRECLIPSE end
962+
944963
private void inferParameterAndReturnTypesOfClosureOnRHS(final ClassNode lhsType, final ClosureExpression rhsExpression) {
945964
Tuple2<ClassNode[], ClassNode> typeInfo = GenericsUtils.parameterizeSAM(lhsType);
946965
Parameter[] closureParameters = getParametersSafe(rhsExpression);
@@ -1873,7 +1892,13 @@ public void visitProperty(final PropertyNode node) {
18731892
try {
18741893
typeCheckingContext.isInStaticContext = node.isInStaticContext();
18751894
currentProperty = node;
1895+
/* GRECLIPSE edit -- GROOVY-9977
18761896
super.visitProperty(node);
1897+
*/
1898+
visitAnnotations(node);
1899+
visitClassCodeContainer(node.getGetterBlock());
1900+
visitClassCodeContainer(node.getSetterBlock());
1901+
// GRECLIPSE end
18771902
} finally {
18781903
currentProperty = null;
18791904
typeCheckingContext.isInStaticContext = osc;
@@ -1886,9 +1911,24 @@ public void visitField(final FieldNode node) {
18861911
try {
18871912
typeCheckingContext.isInStaticContext = node.isInStaticContext();
18881913
currentField = node;
1914+
/* GRECLIPSE edit -- GROOVY-9977
18891915
super.visitField(node);
1916+
*/
1917+
visitAnnotations(node);
1918+
// GRECLIPSE end
18901919
Expression init = node.getInitialExpression();
18911920
if (init != null) {
1921+
// GRECLIPSE add -- GROOVY-9977
1922+
ClassNode lType = getType(node);
1923+
if (isFunctionalInterface(lType)) {
1924+
processFunctionalInterfaceAssignment(lType, init);
1925+
}
1926+
init.visit(this);
1927+
ClassNode rType = getType(init);
1928+
if (init instanceof ConstructorCallExpression) {
1929+
inferDiamondType((ConstructorCallExpression) init, lType);
1930+
}
1931+
// GRECLIPSE end
18921932
FieldExpression left = new FieldExpression(node);
18931933
BinaryExpression bexp = binX(
18941934
left,
@@ -1898,17 +1938,12 @@ public void visitField(final FieldNode node) {
18981938
bexp.setSourcePosition(init);
18991939
/* GRECLIPSE edit -- GROOVY-9882
19001940
typeCheckAssignment(bexp, left, node.getOriginType(), init, getType(init));
1901-
*/
19021941
ClassNode lType = node.getOriginType(), rType = getType(init);
1903-
typeCheckAssignment(bexp, left, lType, init, getResultType(lType, ASSIGN, rType, bexp));
1904-
// GRECLIPSE end
19051942
if (init instanceof ConstructorCallExpression) {
19061943
inferDiamondType((ConstructorCallExpression) init, node.getOriginType());
19071944
}
1908-
// GRECLIPSE add
1909-
else if (init instanceof ClosureExpression && isFunctionalInterface(lType)) {
1910-
inferParameterAndReturnTypesOfClosureOnRHS(lType, (ClosureExpression) init);
1911-
}
1945+
*/
1946+
typeCheckAssignment(bexp, left, lType, init, getResultType(lType, ASSIGN, rType, bexp));
19121947
// GRECLIPSE end
19131948
}
19141949
} finally {

0 commit comments

Comments
 (0)