Skip to content

Commit ecabd6f

Browse files
committedOct 21, 2020
Fix for #1184: check for inner of super, which is implicitly available
1 parent 609c910 commit ecabd6f

File tree

2 files changed

+101
-42
lines changed

2 files changed

+101
-42
lines changed
 

‎ide-test/org.codehaus.groovy.eclipse.tests/src/org/codehaus/groovy/eclipse/test/actions/OrganizeImportsTests.groovy

+31
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,37 @@ final class OrganizeImportsTests extends OrganizeImportsTestSuite {
946946
doContentsCompareTest(contents)
947947
}
948948

949+
@Test // https://github.com/groovy/groovy-eclipse/issues/1184
950+
void testInnerClass11() {
951+
createGroovyType 'p', 'A', '''\
952+
|abstract class A {
953+
| protected static class B {
954+
| }
955+
|}
956+
|'''
957+
String contents = '''\
958+
|import p.A
959+
|import p.A.B
960+
|class C extends A {
961+
| def m(B b) {
962+
| }
963+
|}
964+
|'''
965+
doContentsCompareTest(contents, contents - ~/\|import p.A.B\s+/)
966+
}
967+
968+
@Test // https://github.com/groovy/groovy-eclipse/issues/1184
969+
void testInnerClass12() {
970+
String contents = '''\
971+
|import java.util.Map.Entry
972+
|class C implements Map {
973+
| def m(Entry e) {
974+
| }
975+
|}
976+
|'''
977+
doContentsCompareTest(contents, contents - ~/\|import java.util.Map.Entry\s+/)
978+
}
979+
949980
@Test
950981
void testStaticImport1() {
951982
String contents = '''\

‎ide/org.codehaus.groovy.eclipse.ui/src/org/codehaus/groovy/eclipse/refactoring/actions/OrganizeGroovyImports.java

+70-42
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,32 @@ private IType[] resolveMissingTypes(IProgressMonitor monitor) throws JavaModelEx
360360
}
361361
}
362362

363+
/**
364+
* Determines if organize imports is unsafe due to syntax errors or other conditions.
365+
*/
366+
private static boolean isUnclean(ModuleNodeInfo info, GroovyCompilationUnit unit) {
367+
try {
368+
if (info.module.encounteredUnrecoverableError() || !unit.isConsistent()) {
369+
return true;
370+
}
371+
CategorizedProblem[] problems = info.result.getProblems();
372+
if (problems != null && problems.length > 0) {
373+
for (CategorizedProblem problem : problems) {
374+
if (problem.isError() && problem.getCategoryID() == CategorizedProblem.CAT_INTERNAL) {
375+
String message = problem.getMessage();
376+
if (message.contains("unexpected token")) {
377+
trace("Stopping due to error in compilation unit: %s", message);
378+
return true;
379+
}
380+
}
381+
}
382+
}
383+
} catch (Exception e) {
384+
return true;
385+
}
386+
return false;
387+
}
388+
363389
/**
364390
* GRECLIPSE-1390
365391
* Reorganizing imports (ie- sorting and grouping them) will remove annotations on import statements
@@ -376,16 +402,16 @@ private static boolean isSafeToReorganize(Iterable<ImportNode> allImports) {
376402
return true;
377403
}
378404

379-
private static boolean isAliased(ImportNode imp) {
380-
String alias = imp.getAlias();
405+
private static boolean isAliased(ImportNode node) {
406+
String alias = node.getAlias();
381407
if (alias == null) {
382408
return false;
383409
}
384-
String fieldName = imp.getFieldName();
410+
String fieldName = node.getFieldName();
385411
if (fieldName != null) {
386412
return !fieldName.equals(alias);
387413
}
388-
String className = imp.getClassName();
414+
String className = node.getClassName();
389415
if (className != null) {
390416
// it is possible to import from the default package
391417
boolean aliasIsSameAsClassName = className.endsWith(alias) &&
@@ -395,30 +421,14 @@ private static boolean isAliased(ImportNode imp) {
395421
return false;
396422
}
397423

398-
/**
399-
* Determines if organize imports is unsafe due to syntax errors or other conditions.
400-
*/
401-
private static boolean isUnclean(ModuleNodeInfo info, GroovyCompilationUnit unit) {
402-
try {
403-
if (info.module.encounteredUnrecoverableError() || !unit.isConsistent()) {
404-
return true;
405-
}
406-
CategorizedProblem[] problems = info.result.getProblems();
407-
if (problems != null && problems.length > 0) {
408-
for (CategorizedProblem problem : problems) {
409-
if (problem.isError() && problem.getCategoryID() == CategorizedProblem.CAT_INTERNAL) {
410-
String message = problem.getMessage();
411-
if (message.contains("unexpected token")) {
412-
trace("Stopping due to error in compilation unit: %s", message);
413-
return true;
414-
}
415-
}
416-
}
417-
}
418-
} catch (Exception e) {
419-
return true;
424+
private static String getTypeName(ClassNode node) {
425+
ClassNode type = GroovyUtils.getBaseType(node);
426+
// unresolved name may have dots and/or dollars (e.g. 'a.b.C$D' or 'C$D' or even 'C.D')
427+
if (!type.getName().matches(".*\\b" + type.getUnresolvedName().replace('$', '.'))) {
428+
// synch up name and unresolved name (e.g. 'java.util.Map$Entry as Foo$Entry')
429+
return type.getName() + " as " + type.getUnresolvedName().replace('.', '$');
420430
}
421-
return false;
431+
return type.getName();
422432
}
423433

424434
private static void trace(String message, Object... arguments) {
@@ -691,10 +701,9 @@ private void handleTypeReference(ClassNode node, boolean isAnnotation) {
691701
name = name.replaceAll(Pattern.quote(name.substring(i)) + "(?= |$)", "");
692702
}
693703
doNotRemoveImport(name.replace('$', '.'));
694-
return;
695-
}
696-
697-
if (!node.isResolved() && !current.getModule().getClasses().contains(node.redirect())) {
704+
} else if (current.getModule().getClasses().contains(node)) {
705+
// keep in importsSlatedForRemoval and leave out of missingTypes
706+
} else if (!node.isResolved()) {
698707
String[] parts = name.split("\\.");
699708
if (Character.isUpperCase(name.charAt(0))) {
700709
name = parts[0]; // 'Map.Entry' -> 'Map'
@@ -714,15 +723,17 @@ private void handleTypeReference(ClassNode node, boolean isAnnotation) {
714723
while (i < chars.length && Character.isJavaIdentifierPart(chars[i])) {
715724
i += 1;
716725
}
726+
// for a 'Map.Entry' reference find '.Map' in 'java.util.Map' or '.Map$' in 'java.util.Map$Entry'
717727
m = Pattern.compile("(?:\\A|\\$|\\.)" + String.valueOf(chars, 0, i) + "(?=\\$|$)").matcher(name);
718728
if (m.find()) {
719729
// 'java.util.Map$Entry' -> 'java.util.Map'
720-
String partialName = name.substring(0, m.end()).replace('$', '.');
721-
doNotRemoveImport(partialName);
730+
String partialName = name.substring(0, m.end());
731+
if (!isInnerOfSuper(partialName))
732+
doNotRemoveImport(partialName.replace('$', '.'));
722733
}
723734
} else {
724-
// We don't know exactly what the text is. We just know how
725-
// it resolves. This can be a problem if an inner class. We
735+
// We do not know exactly what the text is. We just know how
736+
// it resolves. This can be a problem for an inner class. We
726737
// don't really know what is in the text nor what the import
727738
// is, so just ensure that none are slated for removal.
728739
String partialName = name.replace('$', '.');
@@ -740,14 +751,31 @@ private void handleTypeReference(ClassNode node, boolean isAnnotation) {
740751
}
741752
}
742753

743-
private String getTypeName(ClassNode node) {
744-
ClassNode type = GroovyUtils.getBaseType(node);
745-
// unresolved name may have dots and/or dollars (e.g. 'a.b.C$D' or 'C$D' or even 'C.D')
746-
if (!type.getName().matches(".*\\b" + type.getUnresolvedName().replace('$', '.'))) {
747-
// synch up name and unresolved name (e.g. 'java.util.Map$Entry as Foo$Entry')
748-
return type.getName() + " as " + type.getUnresolvedName().replace('.', '$');
754+
private boolean isInnerOfSuper(String name) {
755+
if (name.lastIndexOf('$') > 0) {
756+
for (ClassNode node = current.getSuperClass(); node != null && !node.equals(ClassHelper.OBJECT_TYPE); node = node.getSuperClass()) {
757+
for (Iterator<? extends ClassNode> it = node.redirect().getInnerClasses(); it.hasNext();) {
758+
if (it.next().getName().equals(name)) {
759+
return true;
760+
}
761+
}
762+
}
763+
764+
java.util.Queue<ClassNode> todo = new java.util.ArrayDeque<>();
765+
java.util.Collections.addAll(todo, current.getInterfaces());
766+
Set<ClassNode> done = new LinkedHashSet<>();
767+
ClassNode node;
768+
769+
while ((node = todo.poll()) != null) { if (!done.add(node)) continue;
770+
for (Iterator<? extends ClassNode> it = node.redirect().getInnerClasses(); it.hasNext();) {
771+
if (it.next().getName().equals(name)) {
772+
return true;
773+
}
774+
}
775+
java.util.Collections.addAll(todo, node.getInterfaces());
776+
}
749777
}
750-
return type.getName();
778+
return false;
751779
}
752780

753781
private boolean checkRetainImport(String name) {

0 commit comments

Comments
 (0)
Please sign in to comment.