Skip to content

Commit c70339d

Browse files
committed
GROOVY-7363, GROOVY-8638
1 parent 0f77c74 commit c70339d

File tree

5 files changed

+93
-27
lines changed

5 files changed

+93
-27
lines changed

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

+43
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,49 @@ public void testCompileStatic7361a() {
11241124
runConformTest(sources, "[x:y]");
11251125
}
11261126

1127+
@Test
1128+
public void testCompileStatic7363() {
1129+
//@formatter:off
1130+
String[] sources = {
1131+
"Main.groovy",
1132+
"@groovy.transform.CompileStatic\n" +
1133+
"void test() {\n" +
1134+
" def abc = new ABC()\n" +
1135+
" print('' + abc.b.object.value)\n" +
1136+
"}\n" +
1137+
"test()\n",
1138+
1139+
"Types.java",
1140+
"interface A<T, U extends B<T>> {\n" +
1141+
" U getB();\n" +
1142+
"}\n" +
1143+
"class ABC implements A<C, BC> {\n" +
1144+
" void setB(BC b) {}\n" +
1145+
" @Override\n" +
1146+
" public BC getB() {\n" +
1147+
" return new BC();\n" +
1148+
" }\n" +
1149+
"}\n" +
1150+
"interface B<T> {\n" +
1151+
" T getObject();\n" +
1152+
"}\n" +
1153+
"class BC implements B<C> {\n" +
1154+
" @Override\n" +
1155+
" public C getObject() {\n" +
1156+
" return new C();\n" +
1157+
" }\n" +
1158+
"}\n" +
1159+
"class C {\n" +
1160+
" long getValue() {\n" +
1161+
" return 42;\n" +
1162+
" }\n" +
1163+
"}\n",
1164+
};
1165+
//@formatter:on
1166+
1167+
runConformTest(sources, "42");
1168+
}
1169+
11271170
@Test
11281171
public void testCompileStatic7549() {
11291172
//@formatter:off

base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/SimpleTypeLookup.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,7 @@ protected MethodNode findMethodDeclaration(final String name, final ClassNode de
737737

738738
// concrete types (without mixins/traits) return all methods from getMethods(String), except interface default methods
739739
if (!declaringType.isAbstract() && !declaringType.isInterface() && !implementsTrait(declaringType)) {
740-
List<MethodNode> candidates = declaringType.getMethods(name);
740+
List<MethodNode> candidates = getMethods(name, declaringType);
741741
for (ClassNode face : interfaces) {
742742
for (MethodNode method : face.getDeclaredMethods(name)) {
743743
if (method.isDefault()) candidates.add(method);
@@ -949,13 +949,14 @@ protected static List<MethodNode> getMethods(final String name, final ClassNode
949949
List<MethodNode> methods = type.getMethods(name);
950950
List<MethodNode> traitMethods = type.redirect().getNodeMetaData("trait.methods");
951951
if (traitMethods != null) {
952-
methods = new ArrayList<>(methods);
953952
for (MethodNode method : traitMethods) {
954953
if (method.getName().equals(name)) {
955954
methods.add(method);
956955
}
957956
}
958957
}
958+
methods.removeIf(m -> Flags.isSynthetic(m.getModifiers())); // GROOVY-8638
959+
959960
return methods.size() <= 1 ? methods : unique(methods, Comparator.comparing(m -> {
960961
StringBuilder sb = new StringBuilder();
961962
for (Parameter p : m.getParameters()) {

ide-test/org.codehaus.groovy.eclipse.codeassist.test/src/org/codehaus/groovy/eclipse/codeassist/tests/MethodCompletionTests.groovy

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2020 the original author or authors.
2+
* Copyright 2009-2021 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.
@@ -896,6 +896,7 @@ final class MethodCompletionTests extends CompletionTestSuite {
896896
|}
897897
|'''.stripMargin()
898898
ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'com'))
899+
proposalExists(proposals, 'compareTo(Foo', 1)
899900
proposalExists(proposals, 'compareTo', 1)
900901
}
901902

ide/org.codehaus.groovy.eclipse.codeassist/src/org/codehaus/groovy/eclipse/codeassist/creators/AbstractProposalCreator.java

+43-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2020 the original author or authors.
2+
* Copyright 2009-2021 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.
@@ -19,18 +19,19 @@
1919
import java.util.Collection;
2020
import java.util.Collections;
2121
import java.util.HashMap;
22-
import java.util.Iterator;
2322
import java.util.LinkedHashSet;
2423
import java.util.List;
2524
import java.util.Map;
2625
import java.util.Set;
26+
import java.util.function.BiConsumer;
2727
import java.util.function.BiPredicate;
2828

2929
import org.codehaus.groovy.ast.ClassHelper;
3030
import org.codehaus.groovy.ast.ClassNode;
3131
import org.codehaus.groovy.ast.FieldNode;
3232
import org.codehaus.groovy.ast.MethodNode;
3333
import org.codehaus.groovy.ast.ModuleNode;
34+
import org.codehaus.groovy.ast.tools.GenericsUtils;
3435
import org.codehaus.groovy.eclipse.codeassist.ProposalUtils;
3536
import org.codehaus.groovy.eclipse.codeassist.proposals.AbstractGroovyProposal;
3637
import org.codehaus.groovy.eclipse.codeassist.proposals.IGroovyProposal;
@@ -112,7 +113,9 @@ protected Collection<FieldNode> getAllFields(ClassNode thisType, Set<ClassNode>
112113

113114
Map<String, FieldNode> allFields = new HashMap<>();
114115

115-
Set<ClassNode> types = getAllSupers(thisType, exclude);
116+
Set<ClassNode> types = new LinkedHashSet<>();
117+
VariableScope.createTypeHierarchy(thisType, types, false);
118+
types.removeAll(exclude);
116119

117120
for (ClassNode type : types) {
118121
List<FieldNode> fields = type.getFields();
@@ -140,32 +143,50 @@ protected Collection<FieldNode> getAllFields(ClassNode thisType, Set<ClassNode>
140143
}
141144

142145
protected Collection<MethodNode> getAllMethods(ClassNode type, Set<ClassNode> exclude) {
143-
List<MethodNode> traitMethods = type.redirect().getNodeMetaData("trait.methods");
144-
List<MethodNode> allMethods = type.getAllDeclaredMethods();
145-
if (traitMethods != null) allMethods.addAll(traitMethods);
146-
147-
if (!exclude.isEmpty()) {
148-
// remove all methods from classes that have already been visited
149-
for (Iterator<MethodNode> methodIter = allMethods.iterator(); methodIter.hasNext();) {
150-
if (exclude.contains(methodIter.next().getDeclaringClass())) {
151-
methodIter.remove();
146+
Map<String, MethodNode> methods = new HashMap<>();
147+
148+
BiConsumer<MethodNode, Map<String, ClassNode>> mapper = (mn, spec) -> {
149+
StringBuilder sb = new StringBuilder(mn.getName());
150+
sb.append('(');
151+
for (org.codehaus.groovy.ast.Parameter p : mn.getParameters()) {
152+
ClassNode pt = p.getOriginType();
153+
if (!pt.isGenericsPlaceHolder()) {
154+
sb.append(pt.getName());
155+
} else {
156+
sb.append(spec.getOrDefault(pt.getUnresolvedName(), pt).getName());
157+
}
158+
sb.append(';');
159+
}
160+
161+
methods.merge(sb.toString(), mn, (m1, m2) ->
162+
// keep override method unless it's synthetic
163+
!Flags.isSynthetic(m1.getModifiers()) ? m1 : m2);
164+
};
165+
166+
if (exclude == null || !exclude.contains(type)) {
167+
List<MethodNode> traitMethods = type.redirect().getNodeMetaData("trait.methods");
168+
if (traitMethods != null && !traitMethods.isEmpty()) {
169+
for (MethodNode mn : traitMethods) {
170+
mapper.accept(mn, Collections.emptyMap());
152171
}
153172
}
154173
}
155174

156-
Set<ClassNode> types = getAllSupers(type, exclude);
175+
Set<ClassNode> types = new LinkedHashSet<>();
176+
VariableScope.createTypeHierarchy(type, types, true);
177+
for (ClassNode cn : types) {
178+
if ((exclude != null && exclude.contains(cn)) ||
179+
Flags.isSynthetic(cn.getModifiers())) continue;
157180

158-
// keep track of the already seen types so that next time, we won't include them
159-
exclude.addAll(types);
181+
Map<String, ClassNode> spec = GenericsUtils.createGenericsSpec(cn);
182+
for (MethodNode mn : cn.getMethods()) {
183+
mapper.accept(mn, spec);
184+
}
185+
}
160186

161-
return allMethods;
162-
}
187+
if (exclude != null) exclude.addAll(types); // exclude types next time
163188

164-
private static Set<ClassNode> getAllSupers(ClassNode type, Set<ClassNode> exclude) {
165-
Set<ClassNode> superTypes = new LinkedHashSet<>();
166-
VariableScope.createTypeHierarchy(type, superTypes, false);
167-
superTypes.removeAll(exclude);
168-
return superTypes;
189+
return methods.values();
169190
}
170191

171192
protected static boolean leftIsMoreAccessible(FieldNode field, FieldNode existing) {

ide/org.codehaus.groovy.eclipse.codeassist/src/org/codehaus/groovy/eclipse/codeassist/creators/CategoryProposalCreator.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2020 the original author or authors.
2+
* Copyright 2009-2021 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.
@@ -71,7 +71,7 @@ public List<IGroovyProposal> findAllProposals(final ClassNode selfType, final Se
7171
List<IGroovyProposal> proposals = new ArrayList<>();
7272
for (ClassNode category : categories) {
7373
boolean isDefaultCategory = isDefaultCategory(category);
74-
for (MethodNode method : category.getAllDeclaredMethods()) {
74+
for (MethodNode method : getAllMethods(category, null)) {
7575
// check for DGMs filtered by deprecation or user preference
7676
if (isDefaultCategory && (GroovyUtils.isDeprecated(method) || filter.isFiltered(method))) {
7777
continue;

0 commit comments

Comments
 (0)