diff --git a/java/java-analysis-impl/src/com/siyeh/ig/psiutils/VariableNameGenerator.java b/java/java-analysis-impl/src/com/siyeh/ig/psiutils/VariableNameGenerator.java index 82c86c8326ed..3500bc1ab6a4 100644 --- a/java/java-analysis-impl/src/com/siyeh/ig/psiutils/VariableNameGenerator.java +++ b/java/java-analysis-impl/src/com/siyeh/ig/psiutils/VariableNameGenerator.java @@ -5,6 +5,8 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.SuggestedNameInfo; import com.intellij.psi.codeStyle.VariableKind; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.text.UniqueNameGenerator; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -17,10 +19,13 @@ import java.util.*; * It's recommended to have at least one {@link #byName(String...)} call with at least one non-null candidate as the last resort. */ public final class VariableNameGenerator { + private final static int MAX_ITERATIONS = 10; + private final @NotNull JavaCodeStyleManager myManager; private final @NotNull PsiElement myContext; private final @NotNull VariableKind myKind; private final @NonNls Set candidates = new LinkedHashSet<>(); + private final @NonNls Set skipNames = new HashSet<>(); /** * Constructs a new generator @@ -91,6 +96,16 @@ public final class VariableNameGenerator { return this; } + /** + * @param names which generator tries not to use + * Because of performance reason, there is no guarantee that some of these names will not be reused + * @return this generator + */ + public VariableNameGenerator skipNames(List names) { + skipNames.addAll(names); + return this; + } + /** * Generates and returns the unique name * @param lookForward whether further conflicting declarations should be considered @@ -102,12 +117,29 @@ public final class VariableNameGenerator { @NonNls final Set candidates = this.candidates.isEmpty() ? Collections.singleton("v") : this.candidates; for (String candidate : candidates) { String name = myManager.suggestUniqueVariableName(candidate, myContext, lookForward); - if (name.equals(candidate)) return name; + if (name.equals(candidate)){ + suffixed = name; + break; + } if (suffixed == null) { suffixed = name; } } - return suffixed; + return generateWithSkipped(lookForward, suffixed); + } + + private String generateWithSkipped(boolean lookForward, String originalName) { + if (!skipNames.contains(originalName)) return originalName; + String newName = originalName; + int i = 0; + while (true) { + i++; + if (i > MAX_ITERATIONS) break; + newName = UniqueNameGenerator.generateUniqueNameOneBased(newName, name -> !skipNames.contains(name)); + newName = myManager.suggestUniqueVariableName(newName, myContext, lookForward); + if (!skipNames.contains(newName)) return newName; + } + return newName; } @NotNull @@ -126,6 +158,7 @@ public final class VariableNameGenerator { else suffixed.add(name); } result.addAll(suffixed); - return result; + + return ContainerUtil.map(result, name -> generateWithSkipped(lookForward, name)); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaPatternCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaPatternCompletionUtil.java index 4015bd4eaa7a..a991293ce876 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaPatternCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaPatternCompletionUtil.java @@ -10,6 +10,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.pom.java.JavaFeature; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.impl.source.JavaVarTypeUtil; import com.intellij.psi.util.PsiFormatUtil; import com.intellij.psi.util.PsiTreeUtil; @@ -17,6 +18,7 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; +import com.siyeh.ig.psiutils.VariableNameGenerator; import one.util.streamex.EntryStream; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -169,7 +171,12 @@ public final class JavaPatternCompletionUtil { PsiDeconstructionPattern deconstructionPattern = PsiTreeUtil.getParentOfType(context, PsiDeconstructionPattern.class); List names = new ArrayList<>(); for (PsiRecordComponent component : record.getRecordComponents()) { - names.add(manager.suggestUniqueVariableName(component.getName(), context, true, v -> false, name -> !names.contains(name))); + String name = new VariableNameGenerator(context, VariableKind.LOCAL_VARIABLE) + .byName(component.getName()) + .skipNames(names) + .generateAll(true) + .get(0); + names.add(name); } List types = findTypes(deconstructionPattern, record); return new PatternModel(record, names, types, onlyDeconstructionList); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/JavaCodeStyleManagerImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/JavaCodeStyleManagerImpl.java index c59560e7cf1a..f1d620f8a5c3 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/JavaCodeStyleManagerImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/JavaCodeStyleManagerImpl.java @@ -1043,17 +1043,12 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager { public @NotNull String suggestUniqueVariableName(@NotNull String baseName, PsiElement place, boolean lookForward) { Predicate canBeReused = v -> place instanceof PsiParameter && !PsiTreeUtil.isAncestor(((PsiParameter)place).getDeclarationScope(), v, false); - return suggestUniqueVariableName(baseName, place, lookForward, false, canBeReused, null); + return suggestUniqueVariableName(baseName, place, lookForward, false, canBeReused); } @Override public @NotNull String suggestUniqueVariableName(@NotNull String baseName, PsiElement place, Predicate canBeReused) { - return suggestUniqueVariableName(baseName, place, true, false, canBeReused, null); - } - - @Override - public @NotNull String suggestUniqueVariableName(@NotNull String baseName, PsiElement place, boolean lookForward, Predicate canBeReused, @NotNull Predicate additionalValidator) { - return suggestUniqueVariableName(baseName, place, lookForward, false, canBeReused, additionalValidator); + return suggestUniqueVariableName(baseName, place, true, false, canBeReused); } @Override @@ -1073,7 +1068,7 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager { } String unique = suggestUniqueVariableName(name, place, lookForward); if (!unique.equals(name)) { - String withShadowing = suggestUniqueVariableName(name, place, lookForward, place instanceof PsiParameter, v -> false, null); + String withShadowing = suggestUniqueVariableName(name, place, lookForward, place instanceof PsiParameter, v -> false); if (withShadowing.equals(name)) { uniqueNames.add(name); } @@ -1093,12 +1088,10 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager { PsiElement place, boolean lookForward, boolean allowShadowing, - Predicate canBeReused, - @Nullable Predicate additionalValidator) { + Predicate canBeReused) { PsiElement scope = PsiTreeUtil.getNonStrictParentOfType(place, PsiStatement.class, PsiCodeBlock.class, PsiMethod.class); return UniqueNameGenerator.generateUniqueNameOneBased( - baseName, name -> (additionalValidator == null || additionalValidator.test(name)) && - !hasConflictingVariable(place, name, allowShadowing) && + baseName, name -> !hasConflictingVariable(place, name, allowShadowing) && (!lookForward || !hasConflictingVariableAfterwards(scope, name, canBeReused))); } diff --git a/java/java-impl/src/com/siyeh/ig/fixes/CreateMissingDeconstructionRecordClassBranchesFix.java b/java/java-impl/src/com/siyeh/ig/fixes/CreateMissingDeconstructionRecordClassBranchesFix.java index 694f7fdfbd38..013bbe96b219 100644 --- a/java/java-impl/src/com/siyeh/ig/fixes/CreateMissingDeconstructionRecordClassBranchesFix.java +++ b/java/java-impl/src/com/siyeh/ig/fixes/CreateMissingDeconstructionRecordClassBranchesFix.java @@ -3,12 +3,14 @@ package com.siyeh.ig.fixes; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.containers.ContainerUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.psiutils.CreateSwitchBranchesUtil; import com.siyeh.ig.psiutils.SwitchUtils; +import com.siyeh.ig.psiutils.VariableNameGenerator; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -118,8 +120,10 @@ public final class CreateMissingDeconstructionRecordClassBranchesFix extends Cre PsiClass recordClass = PsiUtil.resolveClassInClassTypeOnly(TypeConversionUtil.erasure(recordType)); if (recordClass == null || !recordClass.isRecord()) return null; for (PsiRecordComponent recordComponent : recordClass.getRecordComponents()) { - String nextName = codeStyleManager.suggestUniqueVariableName(recordComponent.getName(), block, false, v -> false, - name -> !variableNames.contains(name)); + String nextName = new VariableNameGenerator(block, VariableKind.LOCAL_VARIABLE) + .byName(recordComponent.getName()) + .skipNames(variableNames) + .generate(false); variableNames.add(nextName); } for (List branch : branches.getValue()) { diff --git a/java/java-psi-api/src/com/intellij/psi/codeStyle/JavaCodeStyleManager.java b/java/java-psi-api/src/com/intellij/psi/codeStyle/JavaCodeStyleManager.java index ac7550de98a7..3996eb021954 100644 --- a/java/java-psi-api/src/com/intellij/psi/codeStyle/JavaCodeStyleManager.java +++ b/java/java-psi-api/src/com/intellij/psi/codeStyle/JavaCodeStyleManager.java @@ -199,24 +199,6 @@ public abstract class JavaCodeStyleManager { @NotNull public abstract String suggestUniqueVariableName(@NotNull String baseName, PsiElement place, Predicate canBeReused); - /** - * Suggests a unique name for the variable used at the specified location looking forward with possible filtering. - * - * @param baseName the base name info for the variable. - * @param place the location where the variable will be used. - * @param lookForward if true, the existing variables are searched in both directions; if false - only backward - * @param canBeReused a predicate which returns true for variables which names still could be reused (e.g. a variable will be deleted - * during the ongoing refactoring) - * @param additionalValidator additional validator, which the generated name should pass - * @return the generated unique name - */ - @NotNull - public abstract String suggestUniqueVariableName(@NotNull String baseName, - PsiElement place, - boolean lookForward, - Predicate canBeReused, - @Nullable Predicate additionalValidator); - /** * Suggests a unique name for the variable used at the specified location. * diff --git a/java/java-psi-impl/src/com/intellij/core/CoreJavaCodeStyleManager.java b/java/java-psi-impl/src/com/intellij/core/CoreJavaCodeStyleManager.java index d4372e02de62..0c74339e3692 100644 --- a/java/java-psi-impl/src/com/intellij/core/CoreJavaCodeStyleManager.java +++ b/java/java-psi-impl/src/com/intellij/core/CoreJavaCodeStyleManager.java @@ -86,15 +86,14 @@ public class CoreJavaCodeStyleManager extends JavaCodeStyleManager { } @Override - public @NotNull String suggestUniqueVariableName(@NotNull @NonNls String baseName, PsiElement place, boolean lookForward) { - return suggestUniqueVariableName(baseName, place, lookForward, v -> false, null); + public @NotNull String suggestUniqueVariableName(@NotNull @NonNls String baseName, PsiElement place, boolean lookForward) { + return suggestUniqueVariableName(baseName, place, lookForward, v -> false); } - private static @NotNull String suggestUniqueVariableNameInner(@NotNull @NonNls String baseName, + private static @NotNull String suggestUniqueVariableName(@NotNull @NonNls String baseName, PsiElement place, boolean lookForward, - Predicate canBeReused, - @Nullable Predicate additionalValidator) { + Predicate canBeReused) { int index = 0; PsiElement scope = PsiTreeUtil.getNonStrictParentOfType(place, PsiStatement.class, PsiCodeBlock.class, PsiMethod.class); NextName: @@ -104,9 +103,6 @@ public class CoreJavaCodeStyleManager extends JavaCodeStyleManager { name += index; } index++; - if (additionalValidator != null && !additionalValidator.test(name)) { - continue; - } if (PsiUtil.isVariableNameUnique(name, place)) { if (lookForward) { final String name1 = name; @@ -144,16 +140,7 @@ public class CoreJavaCodeStyleManager extends JavaCodeStyleManager { @Override public @NotNull String suggestUniqueVariableName(@NotNull String baseName, PsiElement place, Predicate canBeReused) { - return suggestUniqueVariableName(baseName, place, true, canBeReused, null); - } - - @Override - public @NotNull String suggestUniqueVariableName(@NotNull String baseName, - PsiElement place, - boolean lookForward, - Predicate canBeReused, - @Nullable Predicate additionalValidator) { - return suggestUniqueVariableNameInner(baseName, place, lookForward, canBeReused, additionalValidator); + return suggestUniqueVariableName(baseName, place, true, canBeReused); } @Override