[extract method with object] show error when variables are used in loop (IDEA-356602)

GitOrigin-RevId: e84bcf1f1d03819ca4b5c12b91fad72baf82fc27
This commit is contained in:
Bas Leijdekkers
2025-05-13 17:43:07 +00:00
committed by intellij-monorepo-bot
parent 7816046ce9
commit 6e41b23672
3 changed files with 34 additions and 4 deletions
@@ -29,17 +29,29 @@ object ParameterObjectUtils {
}
private fun findAffectedReferences(variable: PsiVariable, scope: List<PsiElement>): List<PsiReferenceExpression>? {
val startingOffset = scope.last().textRange.endOffset
val parent = scope.first().parent
val beforeScope = scope.first().textRange.startOffset
val afterScope = scope.last().textRange.endOffset
val references = ReferencesSearch.search(variable)
.asIterable()
.mapNotNull { it.element as? PsiReferenceExpression }
.filter { reference -> reference.textRange.startOffset >= startingOffset }
.filter { reference -> reference.textRange.startOffset >= afterScope || reference.textRange.endOffset <= beforeScope }
.sortedBy { reference -> reference.textRange.startOffset }
val referencesBefore = references.filter { ref -> ref.textRange.endOffset <= beforeScope }
if (!referencesBefore.isEmpty()) {
val loop = PsiTreeUtil.getParentOfType(parent, PsiLoopStatement::class.java, true)
if (loop != null) {
val suspiciousLoop = referencesBefore.any { ref -> PsiTreeUtil.isAncestor(loop, ref, true) }
if (suspiciousLoop) return null
}
}
val firstAssignment = references.find { reference -> PsiUtil.isAccessedForWriting(reference) } ?: return references
val assignmentExpression = PsiTreeUtil.getParentOfType(firstAssignment, PsiAssignmentExpression::class.java)
if (assignmentExpression == null) return null
if (assignmentExpression.parent.parent != PsiTreeUtil.findCommonParent(assignmentExpression, scope.last())) return null
return references.filter { reference -> reference.textRange.endOffset <= assignmentExpression.textRange.endOffset } - firstAssignment
return references.filter { ref -> ref != firstAssignment
&& ref.textRange.endOffset <= assignmentExpression.textRange.endOffset
&& ref.textRange.startOffset >= afterScope }
}
}
@@ -0,0 +1,12 @@
public class Test {
void test() {
double x = 0;
double y = 0;
while (x < 10) {
<selection>x = x + .1;
y = y - .1;</selection>
}
System.out.println("x: " + x + "; y: " + y);
}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.refactoring
import com.intellij.codeInsight.hint.HintManager
@@ -443,6 +443,12 @@ class ExtractMethodAndDuplicatesInplaceTest: LightJavaCodeInsightTestCase() {
require(getActiveTemplate() != null)
}
fun testIntroduceObjectFailedWithLoop(){
assertThrows(RefactoringErrorHintException::class.java) {
doTest()
}
}
fun testIntroduceObjectFailedWithAssignment1(){
assertThrows(RefactoringErrorHintException::class.java) {
doTest()