mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[java-refactoring] IDEA-71792 Support inlining of abstract methods having one implementation
GitOrigin-RevId: 00491acff53aff96705a866e0d799dfe22873d23
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ce73510eb1
commit
09bd11efd8
@@ -7,7 +7,9 @@ import com.intellij.codeInsight.TargetElementUtil;
|
||||
import com.intellij.java.refactoring.JavaRefactoringBundle;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
@@ -15,14 +17,19 @@ import com.intellij.openapi.util.NlsContexts;
|
||||
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch;
|
||||
import com.intellij.psi.util.PsiFormatUtil;
|
||||
import com.intellij.psi.util.PsiFormatUtilBase;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.refactoring.HelpID;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.refactoring.util.InlineUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -52,6 +59,10 @@ public final class InlineMethodHandler extends JavaInlineActionHandler {
|
||||
public static void performInline(Project project, Editor editor, PsiMethod method, boolean allowInlineThisOnly) {
|
||||
PsiReference reference = editor != null ? TargetElementUtil.findReference(editor, editor.getCaretModel().getOffset()) : null;
|
||||
|
||||
if (reference != null && reference.isReferenceTo(method) && method.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
if (tryInlineAbstractMethodImplementation(project, editor, method, reference)) return;
|
||||
}
|
||||
|
||||
PsiCodeBlock methodBody = method.getBody();
|
||||
Supplier<PsiCodeBlock> specialization = InlineMethodSpecialization.forReference(reference);
|
||||
if (specialization != null) {
|
||||
@@ -139,7 +150,7 @@ public final class InlineMethodHandler extends JavaInlineActionHandler {
|
||||
|
||||
if (reference != null) {
|
||||
final PsiElement referenceElement = reference.getElement();
|
||||
if (referenceElement.getLanguage() == JavaLanguage.INSTANCE &&
|
||||
if (referenceElement.getLanguage() == JavaLanguage.INSTANCE &&
|
||||
!(referenceElement instanceof PsiJavaCodeReferenceElement)) {
|
||||
reference = null;
|
||||
}
|
||||
@@ -157,8 +168,34 @@ public final class InlineMethodHandler extends JavaInlineActionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean tryInlineAbstractMethodImplementation(@NotNull Project project,
|
||||
@NotNull Editor editor,
|
||||
@NotNull PsiMethod method,
|
||||
@NotNull PsiReference reference) {
|
||||
PsiMethod realMethod = ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
() -> ReadAction.nonBlocking(() -> {
|
||||
Collection<PsiMethod> methods =
|
||||
OverridingMethodsSearch.search(method).filtering(m -> !m.hasModifierProperty(PsiModifier.ABSTRACT))
|
||||
.findAll();
|
||||
return ContainerUtil.getOnlyItem(methods);
|
||||
}).executeSynchronously(),
|
||||
JavaRefactoringBundle.message("dialog.title.resolving.method.implementation"), true, project);
|
||||
if (realMethod == null || realMethod.getBody() == null) return false;
|
||||
String message = JavaRefactoringBundle.message("dialog.message.confirmation.to.process.only.implementation",
|
||||
PsiFormatUtil.formatMethod(realMethod, PsiSubstitutor.EMPTY,
|
||||
PsiFormatUtilBase.SHOW_NAME |
|
||||
PsiFormatUtilBase.SHOW_CONTAINING_CLASS, 0));
|
||||
int answer = Messages.showYesNoDialog(project, message, getRefactoringName(), Messages.getQuestionIcon());
|
||||
if (answer == Messages.NO) return true;
|
||||
InlineMethodProcessor processor = new InlineMethodProcessor(project, realMethod, reference, editor, true, false, false, true);
|
||||
processor.setPrepareSuccessfulSwingThreadCallback(() -> {});
|
||||
processor.run();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean checkRecursive(PsiMethod method) {
|
||||
return checkCalls(method.getBody(), method);
|
||||
PsiCodeBlock body = method.getBody();
|
||||
return body != null && checkCalls(body, method);
|
||||
}
|
||||
|
||||
private static boolean checkCalls(PsiElement scope, PsiMethod method) {
|
||||
@@ -179,15 +216,10 @@ public final class InlineMethodHandler extends JavaInlineActionHandler {
|
||||
}
|
||||
|
||||
public static boolean isThisReference(PsiReference reference) {
|
||||
if (reference != null) {
|
||||
final PsiElement referenceElement = reference.getElement();
|
||||
if (referenceElement instanceof PsiJavaCodeReferenceElement &&
|
||||
referenceElement.getParent() instanceof PsiMethodCallExpression &&
|
||||
"this".equals(((PsiJavaCodeReferenceElement)referenceElement).getReferenceName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return reference != null &&
|
||||
reference.getElement() instanceof PsiJavaCodeReferenceElement codeRef &&
|
||||
codeRef.getParent() instanceof PsiMethodCallExpression &&
|
||||
"this".equals(codeRef.getReferenceName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,10 +10,7 @@ import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.refactoring.util.InlineUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
@@ -21,10 +18,11 @@ import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A helper class to perform the parameter substitution during the Inline method refactoring.
|
||||
* A helper class to perform the parameter substitution during the Inline method refactoring.
|
||||
* It helps to declare parameters as locals, passing arguments from the call site and then tries to inline the parameters when possible.
|
||||
*/
|
||||
class InlineMethodHelper {
|
||||
@@ -60,23 +58,41 @@ class InlineMethodHelper {
|
||||
@NotNull
|
||||
private PsiSubstitutor createSubstitutor() {
|
||||
JavaResolveResult resolveResult = myCall.resolveMethodGenerics();
|
||||
if (myMethod.isPhysical()) {
|
||||
// Could be specialized
|
||||
LOG.assertTrue(myManager.areElementsEquivalent(resolveResult.getElement(), myMethod));
|
||||
}
|
||||
if (resolveResult.getSubstitutor() != PsiSubstitutor.EMPTY) {
|
||||
PsiSubstitutor origSubstitutor = resolveResult.getSubstitutor();
|
||||
PsiSubstitutor substitutor = resolveResult.getSubstitutor();
|
||||
if (substitutor != PsiSubstitutor.EMPTY) {
|
||||
if (myMethod.isPhysical()) { // Could be specialized, thus non-physical, see InlineMethodSpecialization
|
||||
PsiMethod calledMethod = ObjectUtils.tryCast(resolveResult.getElement(), PsiMethod.class);
|
||||
if (calledMethod != null && !myManager.areElementsEquivalent(calledMethod, myMethod)) {
|
||||
// Could be an implementation method
|
||||
PsiSubstitutor superSubstitutor =
|
||||
TypeConversionUtil.getSuperClassSubstitutor(Objects.requireNonNull(calledMethod.getContainingClass()),
|
||||
Objects.requireNonNull(myMethod.getContainingClass()),
|
||||
PsiSubstitutor.EMPTY);
|
||||
PsiSubstitutor superMethodSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(
|
||||
myMethod.getHierarchicalMethodSignature(), calledMethod.getHierarchicalMethodSignature());
|
||||
if (superMethodSubstitutor != null) {
|
||||
superSubstitutor = superSubstitutor.putAll(superMethodSubstitutor);
|
||||
}
|
||||
for (Map.Entry<PsiTypeParameter, PsiType> entry : superSubstitutor.getSubstitutionMap().entrySet()) {
|
||||
PsiTypeParameter parameter = entry.getKey();
|
||||
PsiType type = entry.getValue();
|
||||
if (type instanceof PsiClassType classType && classType.resolve() instanceof PsiTypeParameter typeParameter) {
|
||||
substitutor = substitutor.put(typeParameter, origSubstitutor.substitute(parameter));
|
||||
}
|
||||
}
|
||||
origSubstitutor = substitutor;
|
||||
}
|
||||
}
|
||||
Iterator<PsiTypeParameter> oldTypeParameters = PsiUtil.typeParametersIterator(myMethod);
|
||||
Iterator<PsiTypeParameter> newTypeParameters = PsiUtil.typeParametersIterator(myMethodCopy);
|
||||
PsiSubstitutor substitutor = resolveResult.getSubstitutor();
|
||||
while (newTypeParameters.hasNext()) {
|
||||
final PsiTypeParameter newTypeParameter = newTypeParameters.next();
|
||||
final PsiTypeParameter oldTypeParameter = oldTypeParameters.next();
|
||||
substitutor = substitutor.put(newTypeParameter, resolveResult.getSubstitutor().substitute(oldTypeParameter));
|
||||
substitutor = substitutor.put(newTypeParameter, origSubstitutor.substitute(oldTypeParameter));
|
||||
}
|
||||
return substitutor;
|
||||
}
|
||||
|
||||
return PsiSubstitutor.EMPTY;
|
||||
return substitutor;
|
||||
}
|
||||
|
||||
PsiLocalVariable @NotNull [] declareParameters() {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import java.util.List;
|
||||
|
||||
public class InlineSingleImplementation {
|
||||
interface MyIface<T> {
|
||||
void mySimpleMethod();
|
||||
}
|
||||
|
||||
static class MyIfaceImpl<E extends CharSequence> implements MyIface<E> {
|
||||
@Override
|
||||
public void mySimpleMethod() {
|
||||
System.out.println("Impl");
|
||||
}
|
||||
}
|
||||
|
||||
void test(MyIface<String> iface) {
|
||||
iface.<caret>mySimpleMethod();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
public class InlineSingleImplementation {
|
||||
interface MyIface<T> {
|
||||
void mySimpleMethod();
|
||||
}
|
||||
|
||||
static class MyIfaceImpl<E extends CharSequence> implements MyIface<E> {
|
||||
@Override
|
||||
public void mySimpleMethod() {
|
||||
System.out.println("Impl");
|
||||
}
|
||||
}
|
||||
|
||||
void test(MyIface<String> iface) {
|
||||
System.out.println("Impl");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import java.util.List;
|
||||
|
||||
public class InlineSingleImplementation {
|
||||
interface MyIface<T> {
|
||||
void myUseGenericMethod(T t);
|
||||
}
|
||||
|
||||
static class MyIfaceImpl<E extends CharSequence> implements MyIface<E> {
|
||||
@Override
|
||||
public void myUseGenericMethod(E e) {
|
||||
E e1 = e;
|
||||
System.out.println("Impl: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
void test(MyIface<String> iface) {
|
||||
iface.<caret>myUseGenericMethod("hello");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
public class InlineSingleImplementation {
|
||||
interface MyIface<T> {
|
||||
void myUseGenericMethod(T t);
|
||||
}
|
||||
|
||||
static class MyIfaceImpl<E extends CharSequence> implements MyIface<E> {
|
||||
@Override
|
||||
public void myUseGenericMethod(E e) {
|
||||
E e1 = e;
|
||||
System.out.println("Impl: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
void test(MyIface<String> iface) {
|
||||
String e1 = "hello";
|
||||
System.out.println("Impl: " + "hello");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import java.util.List;
|
||||
|
||||
public class InlineSingleImplementation {
|
||||
interface MyIface<T> {
|
||||
<M> M myGenericMethod(M m, T t);
|
||||
}
|
||||
|
||||
static class MyIfaceImpl<E extends CharSequence> implements MyIface<E> {
|
||||
@Override
|
||||
public <M1> M1 myGenericMethod(M1 m, E e) {
|
||||
M1 m1 = m;
|
||||
E e1 = e;
|
||||
if (m == null) return null;
|
||||
System.out.println("Impl: " + m1 + " : " + e);
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
void test(MyIface<String> iface) {
|
||||
int x = iface.<caret>myGenericMethod(123, "hello");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
public class InlineSingleImplementation {
|
||||
interface MyIface<T> {
|
||||
<M> M myGenericMethod(M m, T t);
|
||||
}
|
||||
|
||||
static class MyIfaceImpl<E extends CharSequence> implements MyIface<E> {
|
||||
@Override
|
||||
public <M1> M1 myGenericMethod(M1 m, E e) {
|
||||
M1 m1 = m;
|
||||
E e1 = e;
|
||||
if (m == null) return null;
|
||||
System.out.println("Impl: " + m1 + " : " + e);
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
void test(MyIface<String> iface) {
|
||||
Integer result = null;
|
||||
Integer m1 = 123;
|
||||
String e1 = "hello";
|
||||
if ((Integer) 123 != null) {
|
||||
System.out.println("Impl: " + m1 + " : " + "hello");
|
||||
result = 123;
|
||||
}
|
||||
int x = result;
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileVisitor;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.refactoring.MockInlineMethodOptions;
|
||||
import com.intellij.refactoring.inline.InlineMethodProcessor;
|
||||
import com.intellij.refactoring.util.InlineUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
@@ -67,8 +66,7 @@ public class InlineMethodMultifileTest extends LightFixtureCompletionTestCase {
|
||||
final boolean condition = InlineMethodProcessor.checkBadReturns(method) && !InlineUtil.allUsagesAreTailCalls(method);
|
||||
assertFalse("Bad returns found", condition);
|
||||
|
||||
new InlineMethodProcessor(getProject(), method, null, getEditor(), new MockInlineMethodOptions().isInlineThisOnly())
|
||||
.run();
|
||||
new InlineMethodProcessor(getProject(), method, null, getEditor(), false).run();
|
||||
|
||||
Path expectedPath = myDescriptor.getAfterPath().resolve("src/org/jetbrains/" + getTestName(true));
|
||||
VirtualFile rootAfter = LocalFileSystem.getInstance().findFileByNioFile(expectedPath);
|
||||
|
||||
@@ -4,15 +4,19 @@ package com.intellij.java.refactoring.inline;
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInsight.TargetElementUtil;
|
||||
import com.intellij.java.refactoring.LightRefactoringTestCase;
|
||||
import com.intellij.lang.refactoring.InlineActionHandler;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.ui.TestDialog;
|
||||
import com.intellij.openapi.ui.TestDialogManager;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.MockInlineMethodOptions;
|
||||
import com.intellij.refactoring.inline.InlineMethodHandler;
|
||||
import com.intellij.refactoring.inline.InlineMethodProcessor;
|
||||
import com.intellij.refactoring.inline.InlineOptions;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.refactoring.util.InlineUtil;
|
||||
import com.intellij.testFramework.IdeaTestUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -49,7 +53,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
}
|
||||
|
||||
public void testSideEffect() { doTest(); }
|
||||
|
||||
|
||||
public void testParamAsAutocloseableRef() { doTest(); }
|
||||
|
||||
public void testInlineWithTry() { doTest(); }
|
||||
@@ -94,7 +98,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testSCR31093() { doTest(); }
|
||||
|
||||
public void testSCR37742() { doTest(); }
|
||||
|
||||
|
||||
public void testChainingConstructor() { doTest(); }
|
||||
|
||||
public void testChainingConstructor1() {
|
||||
@@ -121,7 +125,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testEnumConstructor() { doTest(); }
|
||||
|
||||
public void testEnumConstantConstructorParameter() { // IDEADEV-26133
|
||||
doTest();
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testEnumConstantConstructorParameterComplex() { // IDEADEV-26133
|
||||
@@ -180,7 +184,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testRawSubstitution() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testSubstitution() {
|
||||
doTest();
|
||||
}
|
||||
@@ -200,7 +204,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testSuperMethodInAnonymousClass() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testInlineAnonymousClassWithPrivateMethodInside() {
|
||||
doTest();
|
||||
}
|
||||
@@ -236,27 +240,27 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testNotAStatement2() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testNotAStatement3() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testNotAStatement4() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testForContinue() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testSingleReturn1() {
|
||||
doTestAssertBadReturn();
|
||||
}
|
||||
|
||||
|
||||
public void testSingleReturn1NotFinal() {
|
||||
doTestAssertBadReturn();
|
||||
}
|
||||
|
||||
|
||||
public void testSingleReturn2() {
|
||||
doTestAssertBadReturn();
|
||||
}
|
||||
@@ -269,7 +273,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testMethodReferenceInsideMethodCall() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testVolatilePassed() {
|
||||
doTest();
|
||||
}
|
||||
@@ -287,7 +291,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
doTest();
|
||||
fail("Conflict was not detected");
|
||||
}
|
||||
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
|
||||
catch (BaseRefactoringProcessor.ConflictsInTestsException | CommonRefactoringUtil.RefactoringErrorHintException e) {
|
||||
assertEquals(conflict, e.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -315,7 +319,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testOneLineLambdaVoidCompatibleOneLine() {
|
||||
doTestInlineThisOnly();
|
||||
}
|
||||
|
||||
|
||||
public void testOneLineLambdaValueCompatibleOneLine() {
|
||||
doTestInlineThisOnly();
|
||||
}
|
||||
@@ -329,7 +333,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
}
|
||||
|
||||
public void testNonCodeUsage() {
|
||||
doTest(true);
|
||||
doTestNonCode();
|
||||
}
|
||||
|
||||
public void testMethodInsideChangeIfStatement() {
|
||||
@@ -442,7 +446,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testPrivateFieldInSuperClassInSameFile() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testWidenArgument() {
|
||||
doTest();
|
||||
}
|
||||
@@ -490,7 +494,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testNotTailCallInsideIf() {
|
||||
doTestAssertBadReturn();
|
||||
}
|
||||
|
||||
|
||||
public void testConvertToSingleReturnWithFinished() {
|
||||
doTestAssertBadReturn();
|
||||
}
|
||||
@@ -502,7 +506,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testUnusedResult() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testReuseResultVar() {
|
||||
doTest();
|
||||
}
|
||||
@@ -510,23 +514,23 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testSpecializeClassGetName() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testSpecializeEnumName() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testSpecializeEnumValueOf() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testBooleanModelSimple() {
|
||||
doTestAssertBadReturn();
|
||||
}
|
||||
|
||||
|
||||
public void testBooleanModelMultiReturns() {
|
||||
doTestAssertBadReturn();
|
||||
}
|
||||
|
||||
|
||||
public void testBooleanModelIfElse() {
|
||||
doTestAssertBadReturn();
|
||||
}
|
||||
@@ -542,11 +546,11 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testBooleanModelFinalCondition() {
|
||||
doTestAssertBadReturn();
|
||||
}
|
||||
|
||||
|
||||
public void testInvertMethod() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testUnusedParameter() {
|
||||
doTest();
|
||||
}
|
||||
@@ -554,19 +558,19 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testEnumStaticMethod() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testTypeParameterMethodRefArgument() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testIgnoreReturnValue() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
|
||||
public void testSingleReturnComplexQualifier() {
|
||||
doTestAssertBadReturn();
|
||||
}
|
||||
|
||||
|
||||
public void testAnonymousCall() { doTest(); }
|
||||
public void testInSwitchExpression() { doTest(); }
|
||||
public void testInSwitchExpressionYield() { doTest(); }
|
||||
@@ -579,15 +583,30 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testTernaryBranchCollapsible() { doTest(); }
|
||||
|
||||
public void testNewWithSideEffect() { doTest(); }
|
||||
|
||||
|
||||
public void testSplitIfAndCollapseBack() { doTest(); }
|
||||
|
||||
|
||||
public void testThisVariableName() { doTest(); }
|
||||
|
||||
|
||||
public void testRenameLocalClass() { doTest(); }
|
||||
|
||||
|
||||
public void testRenameLocalClassDoubleConflict() { doTest(); }
|
||||
|
||||
public void testInlineSingleImplementation() {
|
||||
TestDialogManager.setTestDialog(TestDialog.YES, getTestRootDisposable());
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testInlineSingleImplementationGenericClass() {
|
||||
TestDialogManager.setTestDialog(TestDialog.YES, getTestRootDisposable());
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testInlineSingleImplementationGenericMethod() {
|
||||
TestDialogManager.setTestDialog(TestDialog.YES, getTestRootDisposable());
|
||||
BaseRefactoringProcessor.ConflictsInTestsException.withIgnoredConflicts(() -> doTest());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sdk getProjectJDK() {
|
||||
return getTestName(false).contains("Src") ? IdeaTestUtil.getMockJdk17() : super.getProjectJDK();
|
||||
@@ -595,12 +614,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
|
||||
private void doTestInlineThisOnly() {
|
||||
@NonNls String fileName = configure();
|
||||
performAction(new MockInlineMethodOptions(){
|
||||
@Override
|
||||
public boolean isInlineThisOnly() {
|
||||
return true;
|
||||
}
|
||||
}, false, false);
|
||||
performAction(true, false);
|
||||
checkResultByFile(fileName + ".after");
|
||||
}
|
||||
|
||||
@@ -608,16 +622,29 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
private void doTest(final boolean nonCode) {
|
||||
private void doTest(boolean assertBadReturn) {
|
||||
String fileName = configure();
|
||||
InlineActionHandler handler = ContainerUtil.find(InlineActionHandler.EP_NAME.getExtensionList(), ep -> ep instanceof InlineMethodHandler);
|
||||
assertNotNull(handler);
|
||||
PsiMethod method = findMethod();
|
||||
final boolean condition = InlineMethodProcessor.checkBadReturns(method) && !InlineUtil.allUsagesAreTailCalls(method);
|
||||
if (assertBadReturn) {
|
||||
assertTrue("Bad returns not found", condition);
|
||||
} else {
|
||||
assertFalse("Bad returns found", condition);
|
||||
}
|
||||
handler.inlineElement(getProject(), getEditor(), method);
|
||||
checkResultByFile(fileName + ".after");
|
||||
}
|
||||
|
||||
private void doTestNonCode() {
|
||||
@NonNls String fileName = configure();
|
||||
performAction(nonCode);
|
||||
performAction(false, true);
|
||||
checkResultByFile(fileName + ".after");
|
||||
}
|
||||
|
||||
private void doTestAssertBadReturn() {
|
||||
@NonNls String fileName = configure();
|
||||
BaseRefactoringProcessor.ConflictsInTestsException.withIgnoredConflicts(() -> performAction(new MockInlineMethodOptions(), false, true));
|
||||
checkResultByFile(fileName + ".after");
|
||||
BaseRefactoringProcessor.ConflictsInTestsException.withIgnoredConflicts(() -> doTest(true));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -627,32 +654,25 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
private void performAction(final boolean nonCode) {
|
||||
performAction(new MockInlineMethodOptions(), nonCode, false);
|
||||
private void performAction(final boolean inlineThisOnly, final boolean nonCode) {
|
||||
final PsiReference ref = getFile().findReferenceAt(getEditor().getCaretModel().getOffset());
|
||||
PsiReferenceExpression refExpr = ref instanceof PsiReferenceExpression ? (PsiReferenceExpression)ref : null;
|
||||
PsiMethod method = findMethod();
|
||||
final boolean condition = InlineMethodProcessor.checkBadReturns(method) && !InlineUtil.allUsagesAreTailCalls(method);
|
||||
assertFalse("Bad returns found", condition);
|
||||
final InlineMethodProcessor processor =
|
||||
new InlineMethodProcessor(getProject(), method, refExpr, getEditor(), inlineThisOnly, nonCode, nonCode, true);
|
||||
processor.run();
|
||||
}
|
||||
|
||||
private void performAction(final InlineOptions options, final boolean nonCode, final boolean assertBadReturn) {
|
||||
private PsiMethod findMethod() {
|
||||
PsiElement element = TargetElementUtil
|
||||
.findTargetElement(getEditor(), TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED);
|
||||
final PsiReference ref = getFile().findReferenceAt(getEditor().getCaretModel().getOffset());
|
||||
if (ref instanceof PsiJavaCodeReferenceElement) {
|
||||
final PsiElement parent = ((PsiJavaCodeReferenceElement)ref).getParent();
|
||||
if (parent instanceof PsiNewExpression) {
|
||||
element = ((PsiNewExpression)parent).resolveConstructor();
|
||||
}
|
||||
if (ref instanceof PsiJavaCodeReferenceElement codeRef && codeRef.getParent() instanceof PsiNewExpression newExpression) {
|
||||
element = newExpression.resolveConstructor();
|
||||
}
|
||||
PsiReferenceExpression refExpr = ref instanceof PsiReferenceExpression ? (PsiReferenceExpression)ref : null;
|
||||
assertTrue(element instanceof PsiMethod);
|
||||
PsiMethod method = (PsiMethod)element.getNavigationElement();
|
||||
final boolean condition = InlineMethodProcessor.checkBadReturns(method) && !InlineUtil.allUsagesAreTailCalls(method);
|
||||
if (assertBadReturn) {
|
||||
assertTrue("Bad returns not found", condition);
|
||||
} else {
|
||||
assertFalse("Bad returns found", condition);
|
||||
}
|
||||
final InlineMethodProcessor processor =
|
||||
new InlineMethodProcessor(getProject(), method, refExpr, getEditor(), options.isInlineThisOnly(), nonCode, nonCode,
|
||||
!options.isKeepTheDeclaration());
|
||||
processor.run();
|
||||
return (PsiMethod)element;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,4 +822,6 @@ introduce.variable.message.cannot.extract.in.implicit.class=Cannot extract in im
|
||||
tooltip.cannot.inline.pattern.variable=Cannot inline pattern variable
|
||||
inline.popup.highlight=Highlight {0} conflicting {0, choice, 1#write|2#writes}
|
||||
inline.popup.ignore.conflicts=Ignore writes and continue
|
||||
inline.warning.variables.used.in.initializer.are.updated=Unsafe Inline: Variables Used in Initializer Are Updated
|
||||
inline.warning.variables.used.in.initializer.are.updated=Unsafe Inline: Variables Used in Initializer Are Updated
|
||||
dialog.title.resolving.method.implementation=Resolving Method Implementation
|
||||
dialog.message.confirmation.to.process.only.implementation=An implementation of abstract method is found:<br><br><b>{0}</b><br><br>Do you want to inline this implementation?
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.refactoring;
|
||||
|
||||
import com.intellij.refactoring.inline.InlineOptions;
|
||||
|
||||
public class MockInlineMethodOptions implements InlineOptions {
|
||||
@Override
|
||||
public boolean isInlineThisOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(int exitCode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPreviewUsages() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user