Skip to content

Commit 46b3df5

Browse files
committed
GROOVY-10890
1 parent 9970f98 commit 46b3df5

File tree

5 files changed

+115
-86
lines changed

5 files changed

+115
-86
lines changed

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

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2022 the original author or authors.
2+
* Copyright 2009-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -6801,4 +6801,23 @@ public void testTypeChecked10847() {
68016801

68026802
runConformTest(sources);
68036803
}
6804+
6805+
@Test
6806+
public void testTypeChecked10890() {
6807+
//@formatter:off
6808+
String[] sources = {
6809+
"Main.groovy",
6810+
"enum A { X }\n" +
6811+
"enum B { Y }\n" +
6812+
"void m(Map<A,B> map) {}\n" +
6813+
"@groovy.transform.TypeChecked\n" +
6814+
"void test(Map<A,B> map) {\n" +
6815+
" m(new EnumMap<>(map))\n" +
6816+
"}\n" +
6817+
"test([(A.X): B.Y])\n",
6818+
};
6819+
//@formatter:on
6820+
6821+
runConformTest(sources);
6822+
}
68046823
}

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

+24-6
Original file line numberDiff line numberDiff line change
@@ -6138,16 +6138,14 @@ private Map<GenericsTypeName, GenericsType> extractGenericsConnectionsFromArgume
61386138
}
61396139

61406140
Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
6141-
extractGenericsConnections(connections, wrapTypeIfNecessary(argumentType), paramType); // GRECLIPSE edit -- GROOVY-10339
6142-
connections.forEach((gtn, gt) -> resolvedPlaceholders.merge(gtn, gt, StaticTypeCheckingSupport::getCombinedGenericsType));
6141+
extractGenericsConnections(connections, wrapTypeIfNecessary(argumentType), paramType);
6142+
connections.forEach((name, type) -> resolvedPlaceholders.merge(name, type, StaticTypeCheckingSupport::getCombinedGenericsType));
61436143
}
61446144
}
61456145
}
61466146

6147-
// in case of "<T, U extends Type<T>>" we can learn about "T" from resolved "U"
6148-
Map<GenericsTypeName, GenericsType> connections = Arrays.stream(methodGenericTypes)
6149-
.collect(toMap(gt -> new GenericsTypeName(gt.getName()), gt -> gt, (v,x) -> v));
6150-
extractGenericsConnectionsForSuperClassAndInterfaces(connections, resolvedPlaceholders);
6147+
// in case of "<T, U extends Type<T>>", we can learn about "T" from a resolved "U"
6148+
extractGenericsConnectionsForBoundTypes(methodGenericTypes, resolvedPlaceholders);
61516149
}
61526150

61536151
for (GenericsType gt : methodGenericTypes) {
@@ -6244,6 +6242,7 @@ else if (a instanceof ListExpression) {
62446242
}
62456243
}
62466244

6245+
/* GRECLIPSE edit -- GROOVY-10890
62476246
private static void extractGenericsConnectionsForSuperClassAndInterfaces(final Map<GenericsTypeName, GenericsType> resolvedPlaceholders, final Map<GenericsTypeName, GenericsType> connections) {
62486247
for (GenericsType value : new HashSet<GenericsType>(connections.values())) {
62496248
if (!value.isPlaceholder() && !value.isWildcard()) {
@@ -6283,6 +6282,25 @@ private static void extractGenericsConnectionsForSuperClassAndInterfaces(final M
62836282
}
62846283
}
62856284
}
6285+
*/
6286+
private static void extractGenericsConnectionsForBoundTypes(final GenericsType[] spec, final Map<GenericsTypeName, GenericsType> target) {
6287+
if (spec.length < 2) return;
6288+
for (GenericsType tp : spec) {
6289+
ClassNode[] bounds = tp.getUpperBounds();
6290+
if (bounds == null || bounds.length == 0) continue;
6291+
6292+
GenericsTypeName key = new GenericsTypeName(tp.getName());
6293+
GenericsType value = target.get(key); // look for specific resolved type
6294+
if (value == null || value.isPlaceholder() || value.isWildcard()) continue;
6295+
6296+
Map<GenericsTypeName, GenericsType> inner = new HashMap<>();
6297+
for (ClassNode bound : bounds) {
6298+
extractGenericsConnections(inner,value.getType(),bound);
6299+
}
6300+
inner.forEach(target::putIfAbsent);
6301+
}
6302+
}
6303+
// GRECLIPSE end
62866304

62876305
private static MethodNode chooseMethod(final MethodPointerExpression source, final Function<Void,ClassNode[]> samSignature) {
62886306
List<MethodNode> options = source.getNodeMetaData(MethodNode.class);

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

+24-6
Original file line numberDiff line numberDiff line change
@@ -5968,16 +5968,14 @@ private Map<GenericsTypeName, GenericsType> extractGenericsConnectionsFromArgume
59685968
}
59695969

59705970
Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
5971-
extractGenericsConnections(connections, wrapTypeIfNecessary(argumentType), paramType); // GRECLIPSE edit -- GROOVY-10339
5972-
connections.forEach((gtn, gt) -> resolvedPlaceholders.merge(gtn, gt, StaticTypeCheckingSupport::getCombinedGenericsType));
5971+
extractGenericsConnections(connections, wrapTypeIfNecessary(argumentType), paramType);
5972+
connections.forEach((name, type) -> resolvedPlaceholders.merge(name, type, StaticTypeCheckingSupport::getCombinedGenericsType));
59735973
}
59745974
}
59755975
}
59765976

5977-
// in case of "<T, U extends Type<T>>" we can learn about "T" from resolved "U"
5978-
Map<GenericsTypeName, GenericsType> connections = Arrays.stream(methodGenericTypes)
5979-
.collect(toMap(gt -> new GenericsTypeName(gt.getName()), gt -> gt, (v,x) -> v));
5980-
extractGenericsConnectionsForSuperClassAndInterfaces(connections, resolvedPlaceholders);
5977+
// in case of "<T, U extends Type<T>>", we can learn about "T" from a resolved "U"
5978+
extractGenericsConnectionsForBoundTypes(methodGenericTypes, resolvedPlaceholders);
59815979
}
59825980

59835981
for (GenericsType gt : methodGenericTypes) {
@@ -6073,6 +6071,7 @@ else if (a instanceof ListExpression) {
60736071
}
60746072
}
60756073

6074+
/* GRECLIPSE edit -- GROOVY-10890
60766075
private static void extractGenericsConnectionsForSuperClassAndInterfaces(final Map<GenericsTypeName, GenericsType> resolvedPlaceholders, final Map<GenericsTypeName, GenericsType> connections) {
60776076
for (GenericsType value : new HashSet<GenericsType>(connections.values())) {
60786077
if (!value.isPlaceholder() && !value.isWildcard()) {
@@ -6112,6 +6111,25 @@ private static void extractGenericsConnectionsForSuperClassAndInterfaces(final M
61126111
}
61136112
}
61146113
}
6114+
*/
6115+
private static void extractGenericsConnectionsForBoundTypes(final GenericsType[] spec, final Map<GenericsTypeName, GenericsType> target) {
6116+
if (spec.length < 2) return;
6117+
for (GenericsType tp : spec) {
6118+
ClassNode[] bounds = tp.getUpperBounds();
6119+
if (bounds == null || bounds.length == 0) continue;
6120+
6121+
GenericsTypeName key = new GenericsTypeName(tp.getName());
6122+
GenericsType value = target.get(key); // look for specific resolved type
6123+
if (value == null || value.isPlaceholder() || value.isWildcard()) continue;
6124+
6125+
Map<GenericsTypeName, GenericsType> inner = new HashMap<>();
6126+
for (ClassNode bound : bounds) {
6127+
extractGenericsConnections(inner,value.getType(),bound);
6128+
}
6129+
inner.forEach(target::putIfAbsent);
6130+
}
6131+
}
6132+
// GRECLIPSE end
61156133

61166134
private static MethodNode chooseMethod(final MethodPointerExpression source, final Supplier<ClassNode[]> samSignature) {
61176135
List<MethodNode> options = source.getNodeMetaData(MethodNode.class);

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

+16-41
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@
139139
import java.util.stream.Collectors;
140140
import java.util.stream.IntStream;
141141

142-
import static java.util.stream.Collectors.toMap;
143142
import static org.apache.groovy.util.BeanUtils.capitalize;
144143
import static org.apache.groovy.util.BeanUtils.decapitalize;
145144
import static org.codehaus.groovy.ast.ClassHelper.AUTOCLOSEABLE_TYPE;
@@ -5415,10 +5414,8 @@ private Map<GenericsTypeName, GenericsType> extractGenericsConnectionsFromArgume
54155414
}
54165415
}
54175416

5418-
// in case of "<T, U extends Type<T>>" we can learn about "T" from resolved "U"
5419-
Map<GenericsTypeName, GenericsType> connections = Arrays.stream(methodGenericTypes)
5420-
.collect(toMap(gt -> new GenericsTypeName(gt.getName()), gt -> gt, (v,x) -> v));
5421-
extractGenericsConnectionsForSuperClassAndInterfaces(connections, resolvedPlaceholders);
5417+
// in case of "<T, U extends Type<T>>" we can learn about "T" from a resolved "U"
5418+
extractGenericsConnectionsForBoundTypes(methodGenericTypes, resolvedPlaceholders);
54225419
}
54235420

54245421
for (GenericsType gt : methodGenericTypes) {
@@ -5506,43 +5503,21 @@ private void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals,
55065503
}
55075504
}
55085505

5509-
private static void extractGenericsConnectionsForSuperClassAndInterfaces(final Map<GenericsTypeName, GenericsType> resolvedPlaceholders, final Map<GenericsTypeName, GenericsType> connections) {
5510-
for (GenericsType value : new HashSet<>(connections.values())) {
5511-
if (!value.isPlaceholder() && !value.isWildcard()) {
5512-
ClassNode valueType = value.getType();
5513-
List<ClassNode> deepNodes = new LinkedList<>();
5514-
ClassNode unresolvedSuperClass = valueType.getUnresolvedSuperClass();
5515-
if (unresolvedSuperClass != null && unresolvedSuperClass.isUsingGenerics()) {
5516-
deepNodes.add(unresolvedSuperClass);
5517-
}
5518-
for (ClassNode node : valueType.getUnresolvedInterfaces()) {
5519-
if (node.isUsingGenerics()) {
5520-
deepNodes.add(node);
5521-
}
5522-
}
5523-
if (!deepNodes.isEmpty()) {
5524-
for (GenericsType genericsType : resolvedPlaceholders.values()) {
5525-
ClassNode lowerBound = genericsType.getLowerBound();
5526-
if (lowerBound != null) {
5527-
for (ClassNode deepNode : deepNodes) {
5528-
if (lowerBound.equals(deepNode)) {
5529-
extractGenericsConnections(connections, deepNode, lowerBound);
5530-
}
5531-
}
5532-
}
5533-
ClassNode[] upperBounds = genericsType.getUpperBounds();
5534-
if (upperBounds != null) {
5535-
for (ClassNode upperBound : upperBounds) {
5536-
for (ClassNode deepNode : deepNodes) {
5537-
if (upperBound.equals(deepNode)) {
5538-
extractGenericsConnections(connections, deepNode, upperBound);
5539-
}
5540-
}
5541-
}
5542-
}
5543-
}
5544-
}
5506+
private static void extractGenericsConnectionsForBoundTypes(final GenericsType[] spec, final Map<GenericsTypeName, GenericsType> target) {
5507+
if (spec.length < 2) return;
5508+
for (GenericsType tp : spec) {
5509+
ClassNode[] bounds = tp.getUpperBounds();
5510+
if (bounds == null || bounds.length == 0) continue;
5511+
5512+
GenericsTypeName key = new GenericsTypeName(tp.getName());
5513+
GenericsType value = target.get(key); // look for specific resolved type
5514+
if (value == null || value.isPlaceholder() || value.isWildcard()) continue;
5515+
5516+
Map<GenericsTypeName, GenericsType> inner = new HashMap<>();
5517+
for (ClassNode bound : bounds) {
5518+
extractGenericsConnections(inner,value.getType(),bound);
55455519
}
5520+
inner.forEach(target::putIfAbsent); // GROOVY-10890
55465521
}
55475522
}
55485523

base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyCompilationUnitDeclaration.java

+31-32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2022 the original author or authors.
2+
* Copyright 2009-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -1442,9 +1442,7 @@ private void createConstructorDeclarations(ClassNode classNode, boolean isEnum,
14421442
// add default constructor if no other constructors exist (and not anonymous/interface/trait)
14431443
} else if (constructorNodes.isEmpty() && !isAnon && !classNode.isInterface() && !isTrait(classNode)) {
14441444
ConstructorDeclaration constructorDecl = new ConstructorDeclaration(unitDeclaration.compilationResult);
1445-
constructorDecl.annotations = new Annotation[] {
1446-
new MarkerAnnotation(createTypeReferenceForClassNode(ClassHelper.make(groovy.transform.Generated.class)), -1)
1447-
};
1445+
constructorDecl.annotations = createAnnotations(ClassHelper.make(groovy.transform.Generated.class));
14481446
if (!isEnum) {
14491447
if (classNode.getObjectInitializerStatements().isEmpty()) {
14501448
constructorDecl.bits |= ASTNode.IsDefaultConstructor;
@@ -1463,9 +1461,7 @@ private void createConstructorDeclarations(ClassNode classNode, boolean isEnum,
14631461

14641462
if (isEnum) { // named arguments constructor
14651463
constructorDecl = new ConstructorDeclaration(unitDeclaration.compilationResult);
1466-
constructorDecl.annotations = new Annotation[] {
1467-
new MarkerAnnotation(createTypeReferenceForClassNode(ClassHelper.make(groovy.transform.Generated.class)), -1)
1468-
};
1464+
constructorDecl.annotations = createAnnotations(ClassHelper.make(groovy.transform.Generated.class));
14691465
constructorDecl.arguments = createArguments(new Parameter[] {
14701466
new Parameter(ClassHelper.makeWithoutCaching(java.util.LinkedHashMap.class, false), "__namedArgs")
14711467
});
@@ -1758,35 +1754,38 @@ private void configurePermittedSubtypes(TypeDeclaration typeDeclaration, ClassNo
17581754
}
17591755
}
17601756

1761-
private Annotation[] createAnnotations(List<AnnotationNode> groovyAnnotations) {
1762-
if (groovyAnnotations != null && !groovyAnnotations.isEmpty()) {
1763-
List<Annotation> annotations = new ArrayList<>(groovyAnnotations.size());
1757+
private Annotation[] createAnnotations(ClassNode annotationType) {
1758+
Annotation annotation = new MarkerAnnotation(createTypeReferenceForClassNode(annotationType), -1);
1759+
annotation.declarationSourceEnd = annotation.sourceEnd;
1760+
return new Annotation[] {annotation};
1761+
}
17641762

1765-
for (AnnotationNode annotationNode : groovyAnnotations) {
1766-
TypeReference annotationReference = createTypeReferenceForClassNode(annotationNode.getClassNode());
1767-
annotationReference.sourceStart = annotationNode.getStart();
1768-
annotationReference.sourceEnd = annotationNode.getEnd() - 1;
1763+
private Annotation[] createAnnotations(List<AnnotationNode> annotationNodes) {
1764+
if (annotationNodes == null || annotationNodes.isEmpty()) return null;
1765+
Annotation[] annotations = new Annotation[annotationNodes.size()];
1766+
int i = 0;
1767+
for (AnnotationNode annotationNode : annotationNodes) {
1768+
TypeReference annotationType = createTypeReferenceForClassNode(annotationNode.getClassNode());
1769+
annotationType.sourceStart = annotationNode.getStart();
1770+
annotationType.sourceEnd = annotationNode.getEnd() - 1;
17691771

1770-
Map<String, Expression> memberValuePairs = annotationNode.getMembers();
1771-
if (memberValuePairs == null || memberValuePairs.isEmpty()) {
1772-
MarkerAnnotation annotation = new MarkerAnnotation(annotationReference, annotationReference.sourceStart);
1773-
annotations.add(annotation);
1774-
} else if (memberValuePairs.size() == 1 && memberValuePairs.containsKey("value")) {
1775-
SingleMemberAnnotation annotation = new SingleMemberAnnotation(annotationReference, annotationReference.sourceStart);
1776-
annotation.memberValue = createAnnotationMemberExpression(memberValuePairs.get("value"), null);
1777-
annotations.add(annotation);
1778-
} else {
1779-
NormalAnnotation annotation = new NormalAnnotation(annotationReference, annotationReference.sourceStart);
1780-
annotation.memberValuePairs = createAnnotationMemberValuePairs(memberValuePairs);
1781-
annotations.add(annotation);
1782-
}
1783-
// TODO: declarationSourceEnd should be rparen position; antlr2 includes any trailing comment
1784-
annotations.get(annotations.size() - 1).declarationSourceEnd = annotationReference.sourceEnd;
1772+
Map<String, Expression> memberValuePairs = annotationNode.getMembers();
1773+
if (memberValuePairs == null || memberValuePairs.isEmpty()) {
1774+
MarkerAnnotation annotation = new MarkerAnnotation(annotationType, annotationType.sourceStart);
1775+
annotations[i] = annotation;
1776+
} else if (memberValuePairs.size() == 1 && memberValuePairs.containsKey("value")) {
1777+
SingleMemberAnnotation annotation = new SingleMemberAnnotation(annotationType, annotationType.sourceStart);
1778+
annotation.memberValue = createAnnotationMemberExpression(memberValuePairs.get("value"), null);
1779+
annotations[i] = annotation;
1780+
} else {
1781+
NormalAnnotation annotation = new NormalAnnotation(annotationType, annotationType.sourceStart);
1782+
annotation.memberValuePairs = createAnnotationMemberValuePairs(memberValuePairs);
1783+
annotations[i] = annotation;
17851784
}
1786-
1787-
return annotations.toArray(new Annotation[annotations.size()]);
1785+
// TODO: declarationSourceEnd should be rparen position; antlr2 includes trailing comment
1786+
annotations[i++].declarationSourceEnd = annotationType.sourceEnd;
17881787
}
1789-
return null;
1788+
return annotations;
17901789
}
17911790

17921791
private org.eclipse.jdt.internal.compiler.ast.Expression createAnnotationMemberExpression(Expression expr, ClassNode type) {

0 commit comments

Comments
 (0)