Skip to content

Commit ca900fb

Browse files
committed
GROOVY-6782, GROOVY-8974, GROOVY-9033, GROOVY-10089
1 parent a05d059 commit ca900fb

File tree

4 files changed

+121
-51
lines changed

4 files changed

+121
-51
lines changed

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

+46
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,25 @@ public void testCompileStatic6610() {
788788
runConformTest(sources, "42");
789789
}
790790

791+
@Test
792+
public void testCompileStatic6782() {
793+
//@formatter:off
794+
String[] sources = {
795+
"Main.groovy",
796+
"@groovy.transform.CompileStatic\n" +
797+
"void test() {\n" +
798+
" String[] array = [123]\n" +
799+
" def temp = array\n" +
800+
" def x = temp[0]\n" +
801+
" temp = [:]\n" + // works if this line is removed
802+
"}\n" +
803+
"test()\n",
804+
};
805+
//@formatter:on
806+
807+
runConformTest(sources);
808+
}
809+
791810
@Test
792811
public void testCompileStatic6904() {
793812
//@formatter:off
@@ -6206,4 +6225,31 @@ public void testCompileStatic10072() {
62066225

62076226
runConformTest(sources);
62086227
}
6228+
6229+
@Test
6230+
public void testCompileStatic10089() {
6231+
//@formatter:off
6232+
String[] sources = {
6233+
"Main.groovy",
6234+
"@groovy.transform.CompileStatic\n" +
6235+
"void test(... attributes) {\n" +
6236+
" List one = [\n" +
6237+
" [id:'x', options:[count:1]]\n" +
6238+
" ]\n" +
6239+
" List two = attributes.collect {\n" +
6240+
" def node = Collections.singletonMap('children', one)\n" +
6241+
" if (node) {\n" +
6242+
" node = node.get('children').find { child -> child['id'] == 'x' }\n" +
6243+
" }\n" +
6244+
" [id: it['id'], name: node['name'], count: node['options']['count']]\n" +
6245+
// ^^^^^^^^^^^^^^^ GroovyCastException (map ctor for Collection)
6246+
" }\n" +
6247+
" print two\n" +
6248+
"}\n" +
6249+
"test( [id:'x'] )\n",
6250+
};
6251+
//@formatter:on
6252+
6253+
runConformTest(sources, "[[id:x, name:null, count:1]]");
6254+
}
62096255
}

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

+25-17
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ else if (op == LOGICAL_OR) {
935935
}
936936
}
937937

938-
/* GRECLIPSE edit -- GROOVY-8974, GROOVY-9033
938+
/* GRECLIPSE edit -- GROOVY-8974, et al.
939939
if (lType.isUsingGenerics() && missesGenericsTypes(resultType) && isAssignment(op)) {
940940
// unchecked assignment
941941
// examples:
@@ -950,19 +950,6 @@ else if (op == LOGICAL_OR) {
950950
resultType = completedType;
951951
}
952952
*/
953-
if (isAssignment(op)) {
954-
if (rightExpression instanceof ConstructorCallExpression) {
955-
inferDiamondType((ConstructorCallExpression) rightExpression, lType);
956-
}
957-
if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
958-
if (!resultType.isGenericsPlaceHolder()) // plain reference loses information
959-
resultType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
960-
} else if (lType.equals(OBJECT_TYPE) && GenericsUtils.hasUnresolvedGenerics(resultType)) { // def list = []
961-
Map<GenericsTypeName, GenericsType> placeholders = extractGenericsParameterMapOfThis(typeCheckingContext);
962-
resultType = fullyResolveType(resultType, Optional.ofNullable(placeholders).orElseGet(Collections::emptyMap));
963-
}
964-
} else
965-
// GRECLIPSE end
966953

967954
if (isArrayOp(op)
968955
&& !lType.isArray()
@@ -985,20 +972,34 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
985972

986973
boolean isEmptyDeclaration = expression instanceof DeclarationExpression && rightExpression instanceof EmptyExpression;
987974
if (!isEmptyDeclaration && isAssignment(op)) {
988-
/* GRECLIPSE edit -- GROOVY-8974
989975
if (rightExpression instanceof ConstructorCallExpression) {
990976
inferDiamondType((ConstructorCallExpression) rightExpression, lType);
991977
}
992-
*/
978+
// GRECLIPSE add -- unchecked assignment
979+
if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
980+
// the inferred type of the binary expression is the type of the RHS
981+
// "completed" with generics type information available from the LHS
982+
if (!resultType.isGenericsPlaceHolder()) // plain reference loses information
983+
resultType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
984+
}
985+
// GRECLIPSE end
993986
ClassNode originType = getOriginalDeclarationType(leftExpression);
994987
typeCheckAssignment(expression, leftExpression, originType, rightExpression, resultType);
988+
/* GRECLIPSE edit
995989
// if assignment succeeds but result type is not a subtype of original type, then we are in a special cast handling
996990
// and we must update the result type
997991
if (!implementsInterfaceOrIsSubclassOf(getWrapper(resultType), getWrapper(originType))) {
998992
resultType = originType;
999993
} else if (lType.isUsingGenerics() && !lType.isEnum() && hasRHSIncompleteGenericTypeInfo(resultType)) {
1000994
// for example, LHS is List<ConcreteClass> and RHS is List<T> where T is a placeholder
1001995
resultType = lType;
996+
*/
997+
// check for implicit conversion like "String a = 123", "int[] b = [1,2,3]", "List c = [].stream()", etc.
998+
if (!implementsInterfaceOrIsSubclassOf(wrapTypeIfNecessary(resultType), wrapTypeIfNecessary(originType))) {
999+
resultType = originType;
1000+
} else if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) {
1001+
resultType = originType; // retain primitive semantics
1002+
// GRECLIPSE end
10021003
} else {
10031004
// GROOVY-7549: RHS type may not be accessible to enclosing class
10041005
int modifiers = resultType.getModifiers();
@@ -1008,12 +1009,19 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
10081009
&& (Modifier.isPrivate(modifiers) || !Objects.equals(enclosingType.getPackageName(), resultType.getPackageName()))) {
10091010
resultType = originType; // TODO: Find accesible type in hierarchy of resultType?
10101011
}
1012+
// GRECLIPSE add -- GROOVY-9033, GROOVY-10089
1013+
else if (GenericsUtils.hasUnresolvedGenerics(resultType)) {
1014+
Map<GenericsTypeName, GenericsType> enclosing = extractGenericsParameterMapOfThis(typeCheckingContext);
1015+
resultType = fullyResolveType(resultType, Optional.ofNullable(enclosing).orElseGet(Collections::emptyMap));
1016+
}
1017+
// GRECLIPSE end
10111018
}
1012-
1019+
/* GRECLIPSE edit
10131020
// make sure we keep primitive types
10141021
if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) {
10151022
resultType = originType;
10161023
}
1024+
*/
10171025

10181026
// if we are in an if/else branch, keep track of assignment
10191027
if (typeCheckingContext.ifElseForWhileAssignmentTracker != null && leftExpression instanceof VariableExpression

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

+25-17
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@ else if (op == LOGICAL_OR) {
817817
}
818818

819819
boolean isAssignment = isAssignment(expression.getOperation().getType());
820-
/* GRECLIPSE edit -- GROOVY-8974, GROOVY-9033
820+
/* GRECLIPSE edit -- GROOVY-8974, et al.
821821
if (isAssignment && lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
822822
// unchecked assignment
823823
// examples:
@@ -832,19 +832,6 @@ else if (op == LOGICAL_OR) {
832832
resultType = completedType;
833833
}
834834
*/
835-
if (isAssignment) {
836-
if (rightExpression instanceof ConstructorCallExpression) {
837-
inferDiamondType((ConstructorCallExpression) rightExpression, lType);
838-
}
839-
if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
840-
if (!resultType.isGenericsPlaceHolder()) // plain reference loses information
841-
resultType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
842-
} else if (lType.equals(OBJECT_TYPE) && GenericsUtils.hasUnresolvedGenerics(resultType)) { // def list = []
843-
Map<GenericsTypeName, GenericsType> placeholders = extractGenericsParameterMapOfThis(typeCheckingContext);
844-
resultType = fullyResolveType(resultType, Optional.ofNullable(placeholders).orElseGet(Collections::emptyMap));
845-
}
846-
} else
847-
// GRECLIPSE end
848835

849836
if (isArrayOp(op)
850837
&& !lType.isArray()
@@ -867,20 +854,34 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
867854

868855
boolean isEmptyDeclaration = (expression instanceof DeclarationExpression && rightExpression instanceof EmptyExpression);
869856
if (isAssignment && !isEmptyDeclaration) {
870-
/* GRECLIPSE edit -- GROOVY-8974
871857
if (rightExpression instanceof ConstructorCallExpression) {
872858
inferDiamondType((ConstructorCallExpression) rightExpression, lType);
873859
}
874-
*/
860+
// GRECLIPSE add -- unchecked assignment
861+
if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
862+
// the inferred type of the binary expression is the type of the RHS
863+
// "completed" with generics type information available from the LHS
864+
if (!resultType.isGenericsPlaceHolder()) // plain reference loses information
865+
resultType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
866+
}
867+
// GRECLIPSE end
875868
ClassNode originType = getOriginalDeclarationType(leftExpression);
876869
typeCheckAssignment(expression, leftExpression, originType, rightExpression, resultType);
870+
/* GRECLIPSE edit
877871
// if assignment succeeds but result type is not a subtype of original type, then we are in a special cast handling
878872
// and we must update the result type
879873
if (!implementsInterfaceOrIsSubclassOf(getWrapper(resultType), getWrapper(originType))) {
880874
resultType = originType;
881875
} else if (lType.isUsingGenerics() && !lType.isEnum() && hasRHSIncompleteGenericTypeInfo(resultType)) {
882876
// for example, LHS is List<ConcreteClass> and RHS is List<T> where T is a placeholder
883877
resultType = lType;
878+
*/
879+
// check for implicit conversion like "String a = 123", "int[] b = [1,2,3]", "List c = [].stream()", etc.
880+
if (!implementsInterfaceOrIsSubclassOf(wrapTypeIfNecessary(resultType), wrapTypeIfNecessary(originType))) {
881+
resultType = originType;
882+
} else if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) {
883+
resultType = originType; // retain primitive semantics
884+
// GRECLIPSE end
884885
} else {
885886
// GROOVY-7549: RHS type may not be accessible to enclosing class
886887
int modifiers = resultType.getModifiers();
@@ -890,12 +891,19 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
890891
&& (Modifier.isPrivate(modifiers) || !Objects.equals(enclosingType.getPackageName(), resultType.getPackageName()))) {
891892
resultType = originType; // TODO: Find accesible type in hierarchy of resultType?
892893
}
894+
// GRECLIPSE add -- GROOVY-9033, GROOVY-10089
895+
else if (GenericsUtils.hasUnresolvedGenerics(resultType)) {
896+
Map<GenericsTypeName, GenericsType> enclosing = extractGenericsParameterMapOfThis(typeCheckingContext);
897+
resultType = fullyResolveType(resultType, Optional.ofNullable(enclosing).orElseGet(Collections::emptyMap));
898+
}
899+
// GRECLIPSE end
893900
}
894-
901+
/* GRECLIPSE edit
895902
// make sure we keep primitive types
896903
if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) {
897904
resultType = originType;
898905
}
906+
*/
899907

900908
// track conditional assignment
901909
if (!isNullConstant(rightExpression)

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

+25-17
Original file line numberDiff line numberDiff line change
@@ -807,7 +807,7 @@ else if (op == LOGICAL_OR) {
807807

808808
if (resultType == null) {
809809
resultType = lType;
810-
/* GRECLIPSE edit -- GROOVY-8974, GROOVY-9033
810+
/* GRECLIPSE edit -- GROOVY-8974, et al.
811811
} else if (lType.isUsingGenerics() && isAssignment(op) && missesGenericsTypes(resultType)) {
812812
// unchecked assignment
813813
// List<Type> list = new LinkedList()
@@ -817,21 +817,8 @@ else if (op == LOGICAL_OR) {
817817
// the inferred type of the binary expression is the type of the RHS
818818
// "completed" with generics type information available from the LHS
819819
resultType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
820-
}
821820
*/
822-
} else if (isAssignment(op)) {
823-
if (rightExpression instanceof ConstructorCallExpression) {
824-
inferDiamondType((ConstructorCallExpression) rightExpression, lType);
825-
}
826-
if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
827-
if (!resultType.isGenericsPlaceHolder()) // plain reference loses information
828-
resultType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
829-
} else if (lType.equals(OBJECT_TYPE) && GenericsUtils.hasUnresolvedGenerics(resultType)) { // def list = []
830-
Map<GenericsTypeName, GenericsType> placeholders = extractGenericsParameterMapOfThis(typeCheckingContext);
831-
resultType = fullyResolveType(resultType, Optional.ofNullable(placeholders).orElseGet(Collections::emptyMap));
832-
}
833821
}
834-
// GRECLIPSE end
835822

836823
// GROOVY-5874: if left expression is a closure shared variable, a second pass should be done
837824
if (leftExpression instanceof VariableExpression && ((VariableExpression) leftExpression).isClosureSharedVariable()) {
@@ -859,20 +846,34 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
859846

860847
boolean isEmptyDeclaration = (expression instanceof DeclarationExpression && rightExpression instanceof EmptyExpression);
861848
if (!isEmptyDeclaration && isAssignment(op)) {
862-
/* GRECLIPSE edit -- GROOVY-8974
863849
if (rightExpression instanceof ConstructorCallExpression) {
864850
inferDiamondType((ConstructorCallExpression) rightExpression, lType);
865851
}
866-
*/
852+
// GRECLIPSE add -- unchecked assignment
853+
if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
854+
// the inferred type of the binary expression is the type of the RHS
855+
// "completed" with generics type information available from the LHS
856+
if (!resultType.isGenericsPlaceHolder()) // plain reference loses information
857+
resultType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
858+
}
859+
// GRECLIPSE end
867860
ClassNode originType = getOriginalDeclarationType(leftExpression);
868861
typeCheckAssignment(expression, leftExpression, originType, rightExpression, resultType);
862+
/* GRECLIPSE edit
869863
// if assignment succeeds but result type is not a subtype of original type, then we are in a special cast handling
870864
// and we must update the result type
871865
if (!implementsInterfaceOrIsSubclassOf(getWrapper(resultType), getWrapper(originType))) {
872866
resultType = originType;
873867
} else if (lType.isUsingGenerics() && !lType.isEnum() && hasRHSIncompleteGenericTypeInfo(resultType)) {
874868
// for example, LHS is List<ConcreteClass> and RHS is List<T> where T is a placeholder
875869
resultType = lType;
870+
*/
871+
// check for implicit conversion like "String a = 123", "int[] b = [1,2,3]", "List c = [].stream()", etc.
872+
if (!implementsInterfaceOrIsSubclassOf(wrapTypeIfNecessary(resultType), wrapTypeIfNecessary(originType))) {
873+
resultType = originType;
874+
} else if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) {
875+
resultType = originType; // retain primitive semantics
876+
// GRECLIPSE end
876877
} else {
877878
// GROOVY-7549: RHS type may not be accessible to enclosing class
878879
int modifiers = resultType.getModifiers();
@@ -882,12 +883,19 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
882883
&& (Modifier.isPrivate(modifiers) || !Objects.equals(enclosingType.getPackageName(), resultType.getPackageName()))) {
883884
resultType = originType; // TODO: Find accesible type in hierarchy of resultType?
884885
}
886+
// GRECLIPSE add -- GROOVY-9033, GROOVY-10089
887+
else if (GenericsUtils.hasUnresolvedGenerics(resultType)) {
888+
Map<GenericsTypeName, GenericsType> enclosing = extractGenericsParameterMapOfThis(typeCheckingContext);
889+
resultType = fullyResolveType(resultType, Optional.ofNullable(enclosing).orElseGet(Collections::emptyMap));
890+
}
891+
// GRECLIPSE end
885892
}
886-
893+
/* GRECLIPSE edit
887894
// make sure we keep primitive types
888895
if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) {
889896
resultType = originType;
890897
}
898+
*/
891899

892900
// track conditional assignment
893901
if (!isNullConstant(rightExpression)

0 commit comments

Comments
 (0)