[extract method] IDEA-354122: make static should pass local variables as method parameters

GitOrigin-RevId: e1c14880358479e7c6b151f0ece7ab6cdd8ac87e
This commit is contained in:
Alexandr Suhinin
2024-06-19 14:25:09 +03:00
committed by intellij-monorepo-bot
parent ba3c74d746
commit 0baf44b97e
9 changed files with 107 additions and 2 deletions

View File

@@ -77,6 +77,7 @@ class CodeFragmentAnalyzer(val elements: List<PsiElement>) {
}
fun findOuterLocals(sourceClassMember: PsiElement, targetClassMember: PsiElement): List<ExternalReference>? {
if (sourceClassMember == targetClassMember) return emptyList()
val outerVariables = mutableListOf<PsiVariable>()
val canBeExtracted = elements
.all { element -> ControlFlowUtil.collectOuterLocals(outerVariables, element, sourceClassMember, targetClassMember) }

View File

@@ -56,7 +56,7 @@ object ExtractMethodPipeline {
if (targetClass.isInterface && !PsiUtil.isAvailable(JavaFeature.EXTENSION_METHODS, targetClass)) return null
if (extractOptions.targetClass == targetClass) return extractOptions
val sourceMember = PsiTreeUtil.getContextOfType(extractOptions.elements.first(), PsiMember::class.java)
val targetMember = PsiTreeUtil.getChildOfType(targetClass, PsiMember::class.java)
val targetMember = generateSequence (sourceMember) { PsiTreeUtil.getParentOfType(it, PsiMember::class.java) }.find { member -> member.containingClass == targetClass }
if (sourceMember == null || targetMember == null) return null
val typeParameters = findRequiredTypeParameters(targetClass, extractOptions.elements)
@@ -209,10 +209,13 @@ object ExtractMethodPipeline {
if (isInnerClass && !PsiUtil.isAvailable(JavaFeature.INNER_STATICS, targetClass)) return null
val memberUsages = analyzer.findInstanceMemberUsages(targetClass, extractOptions.elements)
if (memberUsages.any(::isNotExtractableUsage)) return null
val addedParameters = memberUsages.groupBy(MemberUsage::member).entries
val memberParameters = memberUsages.groupBy(MemberUsage::member).entries
.map { (member: PsiMember, usages: List<MemberUsage>) ->
createInputParameter(member, usages.map(MemberUsage::reference)) ?: return null
}
val topmostClass = PsiTreeUtil.getTopmostParentOfType(targetClass, PsiMember::class.java) ?: targetClass
val localParameters = analyzer.findOuterLocals(targetClass, topmostClass)?.map(::inputParameterOf) ?: return null
val addedParameters = memberParameters + localParameters
return extractOptions.copy(
inputParameters = extractOptions.inputParameters + addedParameters,
isStatic = true,

View File

@@ -0,0 +1,12 @@
public class Test {
void test(int x) {
new Runnable() {
@Override
public void run() {
<selection>System.out.println(x);</selection>
}
};
}
}

View File

@@ -0,0 +1,16 @@
public class Test {
void test(int x) {
new Runnable() {
@Override
public void run() {
extracted(x);
}
};
}
private static void extracted(int x) {
System.out.println(x);
}
}

View File

@@ -0,0 +1,12 @@
public class Test {
void test(int x) {
new Runnable() {
@Override
public void run() {
<selection>System.out.println(x);</selection>
}
};
}
}

View File

@@ -0,0 +1,16 @@
public class Test {
void test(int x) {
new Runnable() {
@Override
public void run() {
extracted(x);
}
private static void extracted(int x) {
System.out.println(x);
}
};
}
}

View File

@@ -0,0 +1,12 @@
public class Test {
void test(int x) {
new Runnable() {
@Override
public void run() {
<selection>System.out.println(x);</selection>
}
};
}
}

View File

@@ -0,0 +1,16 @@
public class Test {
void test(int x) {
new Runnable() {
@Override
public void run() {
extracted();
}
private void extracted() {
System.out.println(x);
}
};
}
}

View File

@@ -358,6 +358,23 @@ class ExtractMethodAndDuplicatesInplaceTest: LightJavaCodeInsightTestCase() {
doTest()
}
fun testMakeStaticPassLocalParameters(){
JavaRefactoringSettings.getInstance().EXTRACT_STATIC_METHOD_AND_PASS_FIELDS = true
shouldSelectTargetClass("Anonymous.*")
doTest()
}
fun testNotStaticByDefault(){
shouldSelectTargetClass("Anonymous.*")
doTest()
}
fun testChangeTargetClassAndMakeStatic(){
JavaRefactoringSettings.getInstance().EXTRACT_STATIC_METHOD_AND_PASS_FIELDS = true
shouldSelectTargetClass("Test.*")
doTest()
}
fun testIntroduceObjectConflictInsideNestedClass(){
doTest {
renameTemplate("Result")