diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineMethodHandler.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineMethodHandler.java index ff81d52fb061..fe3bc126f7c3 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineMethodHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineMethodHandler.java @@ -17,7 +17,7 @@ package com.intellij.refactoring.inline; import com.intellij.codeInsight.TargetElementUtil; -import com.intellij.lang.StdLanguages; +import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.ReadonlyStatusHandler; @@ -37,7 +37,7 @@ class InlineMethodHandler extends JavaInlineActionHandler { } public boolean canInlineElement(PsiElement element) { - return element instanceof PsiMethod && element.getNavigationElement() instanceof PsiMethod && element.getLanguage() == StdLanguages.JAVA; + return element instanceof PsiMethod && element.getNavigationElement() instanceof PsiMethod && element.getLanguage() == JavaLanguage.INSTANCE; } public void inlineElement(final Project project, Editor editor, PsiElement element) { @@ -61,7 +61,7 @@ class InlineMethodHandler extends JavaInlineActionHandler { PsiReference reference = editor != null ? TargetElementUtil.findReference(editor, editor.getCaretModel().getOffset()) : null; if (reference != null) { final PsiElement refElement = reference.getElement(); - if (refElement != null && !isEnabledForLanguage(refElement.getLanguage())) { + if (!isEnabledForLanguage(refElement.getLanguage())) { String message = RefactoringBundle .message("refactoring.is.not.supported.for.language", "Inline of Java method", refElement.getLanguage().getDisplayName()); CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_METHOD); diff --git a/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java b/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java index 96c14cd36ccd..3af9924f33e0 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java @@ -323,11 +323,18 @@ public class InlineUtil { return TailCallType.Return; } if (callParent instanceof PsiExpressionStatement) { - PsiStatement callStatement = (PsiStatement)callParent; - PsiMethod callerMethod = PsiTreeUtil.getParentOfType(callStatement, PsiMethod.class); - if (callerMethod != null) { - final PsiStatement[] psiStatements = callerMethod.getBody().getStatements(); - return psiStatements.length > 0 && callStatement == psiStatements [psiStatements.length-1] ? TailCallType.Simple : TailCallType.None; + PsiStatement curElement = (PsiStatement)callParent; + while (true) { + if (PsiTreeUtil.getNextSiblingOfType(curElement, PsiStatement.class) != null) return TailCallType.None; + PsiElement parent = curElement.getParent(); + if (parent instanceof PsiCodeBlock) { + PsiElement blockParent = parent.getParent(); + if (blockParent instanceof PsiMethod || blockParent instanceof PsiLambdaExpression) return TailCallType.Simple; + if (!(blockParent instanceof PsiBlockStatement)) return TailCallType.None; + parent = blockParent.getParent(); + } + if (!(parent instanceof PsiLabeledStatement) && !(parent instanceof PsiIfStatement)) return TailCallType.None; + curElement = (PsiStatement)parent; } } return TailCallType.None; diff --git a/java/java-tests/testData/refactoring/inlineMethod/NotTailCallInsideIf.java b/java/java-tests/testData/refactoring/inlineMethod/NotTailCallInsideIf.java new file mode 100644 index 000000000000..96691f54147b --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineMethod/NotTailCallInsideIf.java @@ -0,0 +1,16 @@ +class A { + void foo() { + if(Math.random() > 2) { + System.out.println("xyz"); + return; + } + System.out.println("oops"); + } + + void bar(int x) { + if (x > 0) { + foo(); + } + System.out.println("x < 0"); + } +} diff --git a/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideIf.java b/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideIf.java new file mode 100644 index 000000000000..3c037d16a331 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideIf.java @@ -0,0 +1,24 @@ +class A { + void foo() { + if(Math.random() > 2) { + System.out.println("xyz"); + return; + } + System.out.println("oops"); + } + + void bar(int x) { + if (x > 0) { + foo(); + } else { + System.out.println("x < 0"); + } + } + + void baz(int x) { + if (x > 0) + if (x < 10) { + foo(); + } + } +} diff --git a/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideIf.java.after b/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideIf.java.after new file mode 100644 index 000000000000..82522c8600fc --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideIf.java.after @@ -0,0 +1,25 @@ +class A { + + void bar(int x) { + if (x > 0) { + if(Math.random() > 2) { + System.out.println("xyz"); + return; + } + System.out.println("oops"); + } else { + System.out.println("x < 0"); + } + } + + void baz(int x) { + if (x > 0) + if (x < 10) { + if(Math.random() > 2) { + System.out.println("xyz"); + return; + } + System.out.println("oops"); + } + } +} diff --git a/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideLambda.java b/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideLambda.java new file mode 100644 index 000000000000..aa2a600d864a --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideLambda.java @@ -0,0 +1,16 @@ +class A { + void foo() { + if(Math.random() > 2) { + System.out.println("xyz"); + return; + } + System.out.println("oops"); + } + + void bar(int x) { + Runnable r = () -> { + System.out.println("hi"); + foo(); + }; + } +} diff --git a/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideLambda.java.after b/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideLambda.java.after new file mode 100644 index 000000000000..601d394ed649 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineMethod/TailCallInsideLambda.java.after @@ -0,0 +1,13 @@ +class A { + + void bar(int x) { + Runnable r = () -> { + System.out.println("hi"); + if(Math.random() > 2) { + System.out.println("xyz"); + return; + } + System.out.println("oops"); + }; + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/inline/InlineMethodTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/inline/InlineMethodTest.java index 3fefab6a54a8..2231276f3e8d 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/inline/InlineMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/inline/InlineMethodTest.java @@ -407,20 +407,31 @@ public class InlineMethodTest extends LightRefactoringTestCase { doTestInlineThisOnly(); } + public void testTailCallInsideIf() { + doTest(); + } + + public void testTailCallInsideLambda() { + doTest(); + } + + public void testNotTailCallInsideIf() { + doTestAssertBadReturn(); + } + @Override protected Sdk getProjectJDK() { return getTestName(false).contains("Src") ? IdeaTestUtil.getMockJdk17() : super.getProjectJDK(); } private void doTestInlineThisOnly() { - @NonNls String fileName = "/refactoring/inlineMethod/" + getTestName(false) + ".java"; - configureByFile(fileName); + @NonNls String fileName = configure(); performAction(new MockInlineMethodOptions(){ @Override public boolean isInlineThisOnly() { return true; } - }, false); + }, false, false); checkResultByFile(fileName + ".after"); } @@ -429,18 +440,28 @@ public class InlineMethodTest extends LightRefactoringTestCase { } private void doTest(final boolean nonCode) { - String name = getTestName(false); - @NonNls String fileName = "/refactoring/inlineMethod/" + name + ".java"; - configureByFile(fileName); + @NonNls String fileName = configure(); performAction(nonCode); checkResultByFile(fileName + ".after"); } - private void performAction(final boolean nonCode) { - performAction(new MockInlineMethodOptions(), nonCode); + private void doTestAssertBadReturn() { + @NonNls String fileName = configure(); + performAction(new MockInlineMethodOptions(), false, true); } - private void performAction(final InlineOptions options, final boolean nonCode) { + @NotNull + private String configure() { + @NonNls String fileName = "/refactoring/inlineMethod/" + getTestName(false) + ".java"; + configureByFile(fileName); + return fileName; + } + + private static void performAction(final boolean nonCode) { + performAction(new MockInlineMethodOptions(), nonCode, false); + } + + private static void performAction(final InlineOptions options, final boolean nonCode, final boolean assertBadReturn) { PsiElement element = TargetElementUtil .findTargetElement(myEditor, TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED); final PsiReference ref = myFile.findReferenceAt(myEditor.getCaretModel().getOffset()); @@ -454,10 +475,14 @@ public class InlineMethodTest extends LightRefactoringTestCase { assertTrue(element instanceof PsiMethod); PsiMethod method = (PsiMethod)element.getNavigationElement(); final boolean condition = InlineMethodProcessor.checkBadReturns(method) && !InlineUtil.allUsagesAreTailCalls(method); - assertFalse("Bad returns found", condition); - final InlineMethodProcessor processor = - new InlineMethodProcessor(getProject(), method, refExpr, myEditor, options.isInlineThisOnly(), nonCode, nonCode, - !options.isKeepTheDeclaration()); - processor.run(); + if (assertBadReturn) { + assertTrue("Bad returns not found", condition); + } else { + assertFalse("Bad returns found", condition); + final InlineMethodProcessor processor = + new InlineMethodProcessor(getProject(), method, refExpr, myEditor, options.isInlineThisOnly(), nonCode, nonCode, + !options.isKeepTheDeclaration()); + processor.run(); + } } }