[java-refactoring] Rename conflicting local class

IDEA-332489 Inlining refactoring operation leads to naming conflicts

GitOrigin-RevId: 4e239def02419e7a0a30097d3b3ec8cdb69911fd
This commit is contained in:
Tagir Valeev
2024-03-16 12:47:10 +00:00
committed by intellij-monorepo-bot
parent 4228eb556f
commit 21617c8137
8 changed files with 122 additions and 24 deletions
@@ -655,7 +655,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
BlockData blockData = prepareBlock(ref, helper);
ChangeContextUtil.encodeContextInfo(blockData.block, false);
helper.substituteTypes(blockData.parmVars);
InlineUtil.solveVariableNameConflicts(blockData.block, ref, myMethodCopy.getBody());
InlineUtil.solveLocalNameConflicts(blockData.block, ref, myMethodCopy.getBody());
helper.initializeParameters(blockData.parmVars);
addThisInitializer(methodCall, blockData.thisVar);
@@ -101,7 +101,7 @@ public final class InlineObjectProcessor extends BaseRefactoringProcessor {
InlineTransformer ctorTransformer = InlineTransformer.getSuitableTransformer(myMethod).apply(myReference);
ctorTransformer.transformBody(ctorCopy, myReference, PsiTypes.voidType());
PsiCodeBlock ctorBody = Objects.requireNonNull(ctorCopy.getBody());
InlineUtil.solveVariableNameConflicts(ctorBody, target, ctorBody);
InlineUtil.solveLocalNameConflicts(ctorBody, target, ctorBody);
updateFieldRefs(ctorCopy, aClass);
ctorParameters = addRange(target, ctorBody, ctorParameters);
@@ -110,7 +110,7 @@ public final class InlineObjectProcessor extends BaseRefactoringProcessor {
InlineTransformer nextTransformer = InlineTransformer.getSuitableTransformer(myNextMethod).apply(myNextCall.getMethodExpression());
PsiLocalVariable result = nextTransformer.transformBody(nextCopy, myNextCall.getMethodExpression(), myNextCall.getType());
PsiCodeBlock nextBody = Objects.requireNonNull(nextCopy.getBody());
InlineUtil.solveVariableNameConflicts(nextBody, target, nextBody);
InlineUtil.solveLocalNameConflicts(nextBody, target, nextBody);
updateFieldRefs(nextCopy, aClass);
if (result != null) {
PsiLocalVariable[] resultAndParameters = ArrayUtil.prepend(result, nextParameters);
@@ -122,7 +122,7 @@ public final class InlineObjectProcessor extends BaseRefactoringProcessor {
nextParameters = addRange(target, nextBody, nextParameters);
}
InlineUtil.solveVariableNameConflicts(target, myReference.getElement(), target);
InlineUtil.solveLocalNameConflicts(target, myReference.getElement(), target);
ctorHelper.initializeParameters(ctorParameters);
nextHelper.initializeParameters(nextParameters);
@@ -30,11 +30,13 @@ import com.intellij.util.CommonJavaRefactoringUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.text.NameUtilCore;
import com.siyeh.ig.psiutils.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiFunction;
import static com.intellij.util.ObjectUtils.tryCast;
@@ -78,7 +80,7 @@ public final class InlineUtil implements CommonJavaInlineUtil {
}
}
}
solveVariableNameConflicts(initializer, ref, initializer);
solveLocalNameConflicts(initializer, ref, initializer);
ChangeContextUtil.encodeContextInfo(initializer, false);
PsiExpression expr = (PsiExpression)replaceDiamondWithInferredTypesIfNeeded(initializer, ref);
@@ -299,33 +301,62 @@ public final class InlineUtil implements CommonJavaInlineUtil {
return ref != initializer ? ref.replace(initializer) : initializer;
}
public static void solveVariableNameConflicts(final PsiElement scope,
final PsiElement placeToInsert,
final PsiElement renameScope) throws IncorrectOperationException {
if (scope instanceof PsiVariable var) {
String name = var.getName();
String oldName = name;
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(scope.getProject());
while (true) {
String newName = codeStyleManager.suggestUniqueVariableName(name, placeToInsert, true);
if (newName.equals(name)) break;
name = newName;
newName = codeStyleManager.suggestUniqueVariableName(name, var, true);
if (newName.equals(name)) break;
name = newName;
}
if (!name.equals(oldName)) {
RefactoringUtil.renameVariableReferences(var, name, new LocalSearchScope(renameScope), true);
var.getNameIdentifier().replace(JavaPsiFacade.getElementFactory(scope.getProject()).createIdentifier(name));
public static void solveLocalNameConflicts(final PsiElement scope,
final PsiElement placeToInsert,
final PsiElement renameScope) {
if (scope instanceof PsiVariable || scope instanceof PsiClass) {
PsiNameIdentifierOwner named = (PsiNameIdentifierOwner)scope;
String name = named.getName();
PsiElement identifier = named.getNameIdentifier();
if (name != null && identifier != null) {
String oldName = name;
Project project = scope.getProject();
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
BiFunction<PsiElement, String, String> suggester =
scope instanceof PsiVariable ?
(place, curName) -> codeStyleManager.suggestUniqueVariableName(curName, place, true) :
(place, curName) -> suggestClassName(place, curName);
while (true) {
String newName = suggester.apply(placeToInsert, name);
if (newName.equals(name)) break;
name = newName;
newName = suggester.apply(named, name);
if (newName.equals(name)) break;
name = newName;
}
if (!name.equals(oldName)) {
for (PsiReference reference : ReferencesSearch.search(named, new LocalSearchScope(renameScope), true)) {
reference.handleElementRename(name);
}
PsiElementFactory factory = JavaPsiFacade.getElementFactory(scope.getProject());
if (named instanceof PsiClass cls) {
for (PsiMethod constructor : cls.getConstructors()) {
if (!(constructor instanceof SyntheticElement) && constructor.getName().equals(oldName)) {
Objects.requireNonNull(constructor.getNameIdentifier()).replace(factory.createIdentifier(name));
}
}
}
Objects.requireNonNull(named.getNameIdentifier()).replace(factory.createIdentifier(name));
}
}
}
PsiElement[] children = scope.getChildren();
for (PsiElement child : children) {
solveVariableNameConflicts(child, placeToInsert, renameScope);
solveLocalNameConflicts(child, placeToInsert, renameScope);
}
}
private static @NotNull String suggestClassName(@NotNull PsiElement place, @NotNull String name) {
PsiResolveHelper helper = PsiResolveHelper.getInstance(place.getProject());
return NameUtilCore.uniqName(
name,
n -> helper.resolveReferencedClass(n, place) != null ||
place instanceof PsiClass && place.getParent() instanceof PsiDeclarationStatement decl &&
decl.getParent() instanceof PsiCodeBlock block &&
SyntaxTraverser.psiTraverser(block).filter(PsiClass.class).find(cls -> n.equals(cls.getName())) != null);
}
public static boolean isChainingConstructor(PsiMethod constructor) {
return CommonJavaRefactoringUtil.getChainedConstructor(constructor) != null;
}
@@ -0,0 +1,15 @@
class TestCase{
public void main() {
class T {
public T() {}
}
/*]*/fo<caret>o();/*[*/
}
public void foo() {
class T {
T t;
public T() {}
}
}
}
@@ -0,0 +1,13 @@
class TestCase{
public void main() {
class T {
public T() {}
}
/*]*//*[*/
class T1 {
T1 t;
public T1() {}
}
}
}
@@ -0,0 +1,19 @@
class TestCase{
public void main() {
class T1 {
public T1() {}
}
fo<caret>o();
}
public void foo() {
class T1 {
T1 t;
public T1() {}
}
class T2 {
T2 t;
public T2() {}
}
}
}
@@ -0,0 +1,16 @@
class TestCase{
public void main() {
class T1 {
public T1() {}
}
class T3 {
T3 t;
public T3() {}
}
class T2 {
T2 t;
public T2() {}
}
}
}
@@ -579,6 +579,10 @@ public class InlineMethodTest extends LightRefactoringTestCase {
public void testSplitIfAndCollapseBack() { doTest(); }
public void testThisVariableName() { doTest(); }
public void testRenameLocalClass() { doTest(); }
public void testRenameLocalClassDoubleConflict() { doTest(); }
@Override
protected Sdk getProjectJDK() {