diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java index dc8ad3b3adc1..9d1a6536bfb8 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java @@ -284,7 +284,8 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { } final PsiElement parentElement = element.getParent(); if (parentElement != null) { - if (parentElement.getParent() instanceof PsiClassObjectAccessExpression) { + final PsiElement grandPa = parentElement.getParent(); + if (grandPa instanceof PsiClassObjectAccessExpression) { return "Class cannot be inlined because it has usages of its class literal"; } if (ourCatchClausePattern.accepts(parentElement)) { diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassProcessor.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassProcessor.java index 92f13b35846d..1a5a1ea269fd 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassProcessor.java @@ -150,6 +150,35 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor { } }; InlineMethodProcessor.addInaccessibleMemberConflicts(myClass, usages, collector, result); + for (UsageInfo usage : usages) { + final PsiElement element = usage.getElement(); + if (element == null) continue; + final PsiElement parent = element.getParent(); + if (parent == null) continue; + if (parent instanceof PsiNewExpression && PsiTreeUtil.isAncestor(myClass, parent, false)) { + result.putValue(parent, "Class cannot be inlined because a call to its constructor inside body"); + } + final PsiElement grandPa = parent.getParent(); + if (grandPa instanceof PsiParameter && PsiTreeUtil.isAncestor(myClass, grandPa, false)) { + for (PsiReference psiReference : ReferencesSearch.search(grandPa)) { + final PsiElement refElement = psiReference.getElement(); + if (refElement instanceof PsiExpression) { + final PsiReferenceExpression referenceExpression = PsiTreeUtil.getParentOfType(refElement, PsiReferenceExpression.class); + if (referenceExpression != null && referenceExpression.getQualifierExpression() == refElement) { + final PsiElement resolvedMember = referenceExpression.resolve(); + if (resolvedMember != null && PsiTreeUtil.isAncestor(myClass, resolvedMember, false)) { + if (resolvedMember instanceof PsiMethod) { + if (myClass.findMethodsBySignature((PsiMethod)resolvedMember, true).length > 1) { //skip inherited methods + continue; + } + } + result.putValue(refElement, "Class cannot be inlined because a call to its member inside body"); + } + } + } + } + } + } return result; } diff --git a/java/java-tests/testData/refactoring/inlineToAnonymousClass/NoInlineRecursiveAccess.java b/java/java-tests/testData/refactoring/inlineToAnonymousClass/NoInlineRecursiveAccess.java new file mode 100644 index 000000000000..0ae807f3805f --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineToAnonymousClass/NoInlineRecursiveAccess.java @@ -0,0 +1,20 @@ +public class SelfParams { + private int myVar = 0; + + private int myProp = 0; + public int getProp() { + return myProp; + } + public void setProp(int prop) { + myProp = prop; + } + + public void copy(SelfParams sp) { + this.myVar = sp.myVar; + this.myProp = sp.getProp(); + } +} + +class Usage { + SelfParams s = new SelfParams(); +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineToAnonymousClass/ParamTypeReplacement.java b/java/java-tests/testData/refactoring/inlineToAnonymousClass/ParamTypeReplacement.java new file mode 100644 index 000000000000..0687e1e20ec0 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineToAnonymousClass/ParamTypeReplacement.java @@ -0,0 +1,13 @@ +public class Simple implements Runnable{ + void foo(Simple s) { + System.out.println(s.toString()); + s.run(); + } + + public void run(){ + } +} + +class Usage { + Simple s = new Simple(); +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineToAnonymousClass/ParamTypeReplacement.java.after b/java/java-tests/testData/refactoring/inlineToAnonymousClass/ParamTypeReplacement.java.after new file mode 100644 index 000000000000..5e73698e1d9a --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineToAnonymousClass/ParamTypeReplacement.java.after @@ -0,0 +1,11 @@ +class Usage { + Runnable s = new Runnable() { + void foo(Runnable s) { + System.out.println(s.toString()); + s.run(); + } + + public void run(){ + } + }; +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineToAnonymousClassTest.java b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineToAnonymousClassTest.java index 61c8e144f56c..9bbd231a00c7 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineToAnonymousClassTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineToAnonymousClassTest.java @@ -13,6 +13,8 @@ import com.intellij.usageView.UsageInfo; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NonNls; +import java.util.Iterator; + /** * @author yole */ @@ -222,6 +224,10 @@ public class InlineToAnonymousClassTest extends LightCodeInsightTestCase { doTest(false, true); } + public void testParamTypeReplacement() throws Exception { + doTest(false, true); + } + public void testNoInlineAbstract() throws Exception { doTestNoInline("Abstract classes cannot be inlined"); } @@ -322,13 +328,25 @@ public class InlineToAnonymousClassTest extends LightCodeInsightTestCase { doTestPreprocessUsages("Class is never used"); } + public void testNoInlineRecursiveAccess() throws Exception { + doTestConflict("Class cannot be inlined because a call to its member inside body", "Class cannot be inlined because a call to its member inside body"); + } + public void testConflictInaccessibleOuterField() throws Exception { + doTestConflict( + "Field C2.a that is used in inlined method is not accessible from call site(s) in method C2User.test()"); + } + + public void doTestConflict(final String... expected) throws Exception { InlineToAnonymousClassProcessor processor = prepareProcessor(); UsageInfo[] usages = processor.findUsages(); MultiMap conflicts = processor.getConflicts(usages); - assertEquals(1, conflicts.size()); - assertEquals("Field C2.a that is used in inlined method is not accessible from call site(s) in method C2User.test()", - conflicts.values().iterator().next()); + assertEquals(expected.length, conflicts.size()); + final Iterator iterator = conflicts.values().iterator(); + for (String s : expected) { + assertTrue(iterator.hasNext()); + assertEquals(s, iterator.next()); + } } private void doTestNoInline(final String expectedMessage) throws Exception {