[java-inspections] ClassCanBeRecord: fix broken undo and exception being thrown when ctor param names don't match instance field names

#IDEA-371645 fixed

Merge-request: IJ-MR-161862
Merged-by: Bartek Pacia <bartek.pacia@jetbrains.com>

GitOrigin-RevId: e151e27ffc84aa8263f1b67d6c80662614bec6ac
This commit is contained in:
Bartek Pacia
2025-05-03 15:45:24 +00:00
committed by intellij-monorepo-bot
parent ca3986ea11
commit ef3b8c610e
33 changed files with 136 additions and 69 deletions

View File

@@ -24,8 +24,6 @@ import com.intellij.psi.util.PropertyUtilBase;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.RefactoringFactory;
import com.intellij.refactoring.RenameRefactoring;
import com.intellij.refactoring.rename.RenameProcessor;
import com.intellij.refactoring.rename.RenamePsiElementProcessor;
import com.intellij.refactoring.rename.RenameUtil;
@@ -90,37 +88,17 @@ final class ConvertToRecordProcessor extends BaseRefactoringProcessor {
@Override
protected void doRun() {
prepareRenameOfAccessors();
prepareRenameOfConstructorParameters();
super.doRun();
}
private void prepareRenameOfConstructorParameters() {
RecordConstructorCandidate ctorCandidate = myRecordCandidate.getCanonicalConstructorCandidate();
if (ctorCandidate == null) return;
ctorCandidate.getCtorParamsToFields().forEach((ctorParam, field) -> {
if (!ctorParam.getName().equals(field.getName())) {
RenameRefactoring renameRefactoring = RefactoringFactory.getInstance(myProject).createRename(ctorParam, field.getName());
renameRefactoring.setPreviewUsages(false);
renameRefactoring.setSearchInComments(false);
// The below line is required to not show conflicts midway and break the refactoring flow.
renameRefactoring.setSearchInNonJavaFiles(false);
renameRefactoring.setInteractive(null);
renameRefactoring.run();
}
});
}
private void prepareRenameOfAccessors() {
List<FieldAccessorCandidate> accessorsToRename = getAccessorsToRename();
for (var fieldAccessorCandidate : accessorsToRename) {
String backingFieldName = fieldAccessorCandidate.backingField().getName();
List<PsiMethod> methods = substituteWithSuperMethodsIfPossible(fieldAccessorCandidate.method());
RenamePsiElementProcessor methodRenameProcessor = RenamePsiElementProcessor.forElement(methods.get(0));
methods.forEach(method -> {
myAllRenames.put(method, backingFieldName);
methodRenameProcessor.prepareRenaming(method, backingFieldName, myAllRenames);
@@ -265,6 +243,7 @@ final class ConvertToRecordProcessor extends BaseRefactoringProcessor {
@Override
protected void performRefactoring(UsageInfo @NotNull [] usages) {
renameMembers(usages);
renameConstructorParameters();
final PsiClass psiClass = myRecordCandidate.getPsiClass();
final RecordConstructorCandidate canonicalCtorCandidate = myRecordCandidate.getCanonicalConstructorCandidate();
@@ -366,6 +345,30 @@ final class ConvertToRecordProcessor extends BaseRefactoringProcessor {
place, null, null);
}
private void renameConstructorParameters() {
RecordConstructorCandidate ctorCandidate = myRecordCandidate.getCanonicalConstructorCandidate();
if (ctorCandidate == null) return;
Map<PsiElement, String> ctorParamRenames = new LinkedHashMap<>();
List<UsageInfo> usagesToRename = new ArrayList<>();
ctorCandidate.getCtorParamsToFields().forEach((ctorParam, field) -> {
if (!ctorParam.getName().equals(field.getName())) {
UsageInfo[] usages = RenameUtil.findUsages(ctorParam, field.getName(), false, false, ctorParamRenames);
usagesToRename.addAll(Arrays.asList(usages));
ctorParamRenames.put(ctorParam, field.getName());
}
});
MultiMap<PsiElement, UsageInfo> renameUsagesByElement = RenameProcessor.classifyUsages(ctorParamRenames.keySet(), usagesToRename);
for (var entry : ctorParamRenames.entrySet()) {
PsiElement element = entry.getKey();
String newName = entry.getValue();
UsageInfo[] elementRenameUsages = renameUsagesByElement.get(entry.getKey()).toArray(UsageInfo.EMPTY_ARRAY);
RenamePsiElementProcessor renamePsiElementProcessor = RenamePsiElementProcessor.forElement(element);
renamePsiElementProcessor.renameElement(element, newName, elementRenameUsages, null);
}
}
private void renameMembers(UsageInfo @NotNull [] usages) {
List<UsageInfo> renameUsages = ContainerUtil.filter(usages, u -> !(u instanceof ConvertToRecordUsageInfo));
MultiMap<PsiElement, UsageInfo> renameUsagesByElement = RenameProcessor.classifyUsages(myAllRenames.keySet(), renameUsages);

View File

@@ -29,7 +29,7 @@ import java.util.Objects;
import java.util.StringJoiner;
import java.util.stream.Collectors;
class RecordBuilder {
final class RecordBuilder {
private final StringBuilder myRecordText = new StringBuilder();
private final PsiClass myOriginClass;