move inner: moved class should be checked if it contains inaccessible refs (IDEA-60450)

This commit is contained in:
anna
2010-10-28 19:36:26 +04:00
parent 92ed84def2
commit fe83d89f0d
5 changed files with 91 additions and 35 deletions

View File

@@ -347,51 +347,20 @@ public class MoveInnerProcessor extends BaseRefactoringProcessor {
protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
final HashMap<PsiElement,HashSet<PsiElement>> reported = new HashMap<PsiElement, HashSet<PsiElement>>();
class Visitor extends JavaRecursiveElementWalkingVisitor {
private final HashMap<PsiElement,HashSet<PsiElement>> reported = new HashMap<PsiElement, HashSet<PsiElement>>();
@Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
PsiElement resolved = reference.resolve();
if (resolved instanceof PsiMember &&
PsiTreeUtil.isAncestor(myInnerClass, resolved, true) &&
becomesInaccessible((PsiMember)resolved)) {
final PsiElement container = ConflictsUtil.getContainer(reference);
HashSet<PsiElement> containerSet = reported.get(resolved);
if (containerSet == null) {
containerSet = new HashSet<PsiElement>();
reported.put(resolved, containerSet);
}
if (!containerSet.contains(container)) {
containerSet.add(container);
String message = RefactoringBundle.message("0.will.become.inaccessible.from.1",
RefactoringUIUtil.getDescription(resolved, true),
RefactoringUIUtil.getDescription(container, true));
conflicts.putValue(resolved, message);
}
registerConflict(reference, resolved, reported, conflicts);
}
}
private boolean becomesInaccessible(PsiMember element) {
final String visibilityModifier = VisibilityUtil.getVisibilityModifier(element.getModifierList());
if (PsiModifier.PRIVATE.equals(visibilityModifier)) return true;
if (PsiModifier.PUBLIC.equals(visibilityModifier)) return false;
final PsiFile containingFile = myOuterClass.getContainingFile();
if (myTargetContainer instanceof PsiDirectory) {
final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)myTargetContainer);
return !isInPackage(containingFile, aPackage);
}
// target container is a class
PsiFile targetFile = myTargetContainer.getContainingFile();
if (targetFile != null) {
final PsiDirectory containingDirectory = targetFile.getContainingDirectory();
if (containingDirectory != null) {
final PsiPackage targetPackage = JavaDirectoryService.getInstance().getPackage(containingDirectory);
return isInPackage(containingFile, targetPackage);
}
}
return false;
}
@Override public void visitClass(PsiClass aClass) {
@@ -402,10 +371,63 @@ public class MoveInnerProcessor extends BaseRefactoringProcessor {
// if (myInnerClass.hasModifierProperty(PsiModifier.)) {
myOuterClass.accept(new Visitor());
myInnerClass.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
super.visitReferenceElement(reference);
final PsiElement resolve = reference.resolve();
if (resolve instanceof PsiMember) {
if (PsiTreeUtil.isAncestor(myOuterClass, resolve, true) && !PsiTreeUtil.isAncestor(myInnerClass, resolve, true)) {
if (becomesInaccessible((PsiMember)resolve)) {
registerConflict(reference, resolve, reported, conflicts);
}
}
}
}
});
return showConflicts(conflicts, refUsages.get());
}
private static void registerConflict(PsiJavaCodeReferenceElement reference,
PsiElement resolved,
HashMap<PsiElement, HashSet<PsiElement>> reported, MultiMap<PsiElement, String> conflicts) {
final PsiElement container = ConflictsUtil.getContainer(reference);
HashSet<PsiElement> containerSet = reported.get(resolved);
if (containerSet == null) {
containerSet = new HashSet<PsiElement>();
reported.put(resolved, containerSet);
}
if (!containerSet.contains(container)) {
containerSet.add(container);
String message = RefactoringBundle.message("0.will.become.inaccessible.from.1",
RefactoringUIUtil.getDescription(resolved, true),
RefactoringUIUtil.getDescription(container, true));
conflicts.putValue(resolved, message);
}
}
private boolean becomesInaccessible(PsiMember element) {
final String visibilityModifier = VisibilityUtil.getVisibilityModifier(element.getModifierList());
if (PsiModifier.PRIVATE.equals(visibilityModifier)) return true;
if (PsiModifier.PUBLIC.equals(visibilityModifier)) return false;
final PsiFile containingFile = myOuterClass.getContainingFile();
if (myTargetContainer instanceof PsiDirectory) {
final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)myTargetContainer);
return !isInPackage(containingFile, aPackage);
}
// target container is a class
PsiFile targetFile = myTargetContainer.getContainingFile();
if (targetFile != null) {
final PsiDirectory containingDirectory = targetFile.getContainingDirectory();
if (containingDirectory != null) {
final PsiPackage targetPackage = JavaDirectoryService.getInstance().getPackage(containingDirectory);
return isInPackage(containingFile, targetPackage);
}
}
return false;
}
private static boolean isInPackage(final PsiFile containingFile, PsiPackage aPackage) {
if (containingFile != null) {
final PsiDirectory containingDirectory = containingFile.getContainingDirectory();

View File

@@ -0,0 +1,8 @@
package p;
class A {
private static class C {
}
}

View File

@@ -0,0 +1,5 @@
package p;
class B extends A.C {
}

View File

@@ -0,0 +1,11 @@
package p;
class A {
private static class C {
}
private static class B extends C{
}
}

View File

@@ -46,6 +46,16 @@ public class MoveInnerTest extends MultiFileTestCase {
doTest(createAction("xxx.Outer.Inner", "Inner", true, "outer", false, false, null));
}
public void testInnerClassInheritance() throws Exception {
try {
doTest(createAction("p.A.B", "B", false, null, false, false, null));
fail("Conflict was not detected");
}
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
assertEquals("class <b><code>p.A.C</code></b> will become inaccessible from class <b><code>p.A.B</code></b>.", e.getMessage());
}
}
public void testScr30106() throws Exception {
doTest(createAction("p.A.B", "B", true, "outer", false, false, null));
}