IJ-CR-137428 [java-completion] IDEA-355252 Don't suggest deconstruction with incorrect name

- use JavaCodeStyleManager

GitOrigin-RevId: 140a4b58a6d0f12a14219bd3f48091d61d187b0c
This commit is contained in:
Mikhail Pyltsin
2024-06-24 14:41:13 +02:00
committed by intellij-monorepo-bot
parent c2c156d53a
commit 5c719214e3
5 changed files with 59 additions and 40 deletions

View File

@@ -5,8 +5,7 @@ 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.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -19,7 +18,6 @@ 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;
@@ -97,11 +95,11 @@ public final class VariableNameGenerator {
}
/**
* @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
* @param names which generator will not use
* @return this generator
*/
public VariableNameGenerator skipNames(List<String> names) {
@Contract("_->this")
public VariableNameGenerator skipNames(@NotNull Collection<@NotNull String> names) {
skipNames.addAll(names);
return this;
}
@@ -116,30 +114,13 @@ public final class VariableNameGenerator {
String suffixed = null;
@NonNls final Set<String> candidates = this.candidates.isEmpty() ? Collections.singleton("v") : this.candidates;
for (String candidate : candidates) {
String name = myManager.suggestUniqueVariableName(candidate, myContext, lookForward);
if (name.equals(candidate)){
suffixed = name;
break;
}
String name = myManager.suggestUniqueVariableName(candidate, myContext, lookForward, skipNames);
if (name.equals(candidate)) return name;
if (suffixed == null) {
suffixed = name;
}
}
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;
return suffixed;
}
@NotNull
@@ -152,13 +133,13 @@ public final class VariableNameGenerator {
if (myContext instanceof PsiNameIdentifierOwner owner && candidate.equals(owner.getName())) {
name = candidate;
} else {
name = myManager.suggestUniqueVariableName(candidate, myContext, lookForward);
name = myManager.suggestUniqueVariableName(candidate, myContext, lookForward, skipNames);
}
if (name.equals(candidate)) result.add(name);
else suffixed.add(name);
}
result.addAll(suffixed);
return ContainerUtil.map(result, name -> generateWithSkipped(lookForward, name));
return result;
}
}

View File

@@ -174,8 +174,7 @@ public final class JavaPatternCompletionUtil {
String name = new VariableNameGenerator(context, VariableKind.LOCAL_VARIABLE)
.byName(component.getName())
.skipNames(names)
.generateAll(true)
.get(0);
.generate(true);
names.add(name);
}
List<PsiType> types = findTypes(deconstructionPattern, record);

View File

@@ -1041,14 +1041,20 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
@Override
public @NotNull String suggestUniqueVariableName(@NotNull String baseName, PsiElement place, boolean lookForward) {
Predicate<PsiVariable> canBeReused =
v -> place instanceof PsiParameter && !PsiTreeUtil.isAncestor(((PsiParameter)place).getDeclarationScope(), v, false);
return suggestUniqueVariableName(baseName, place, lookForward, false, canBeReused);
return suggestUniqueVariableName(baseName, place, lookForward, Set.of());
}
@Override
public @NotNull String suggestUniqueVariableName(@NotNull String baseName, PsiElement place, boolean lookForward, @NotNull Set<String> skipNames) {
Predicate<PsiVariable> canBeReused =
v -> place instanceof PsiParameter && !PsiTreeUtil.isAncestor(((PsiParameter)place).getDeclarationScope(), v, false);
return suggestUniqueVariableName(baseName, place, lookForward, false, canBeReused, v -> !skipNames.contains(v));
}
@Override
public @NotNull String suggestUniqueVariableName(@NotNull String baseName, PsiElement place, Predicate<? super PsiVariable> canBeReused) {
return suggestUniqueVariableName(baseName, place, true, false, canBeReused);
return suggestUniqueVariableName(baseName, place, true, false, canBeReused, null);
}
@Override
@@ -1068,7 +1074,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);
String withShadowing = suggestUniqueVariableName(name, place, lookForward, place instanceof PsiParameter, v -> false, null);
if (withShadowing.equals(name)) {
uniqueNames.add(name);
}
@@ -1088,10 +1094,12 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
PsiElement place,
boolean lookForward,
boolean allowShadowing,
Predicate<? super PsiVariable> canBeReused) {
Predicate<? super PsiVariable> canBeReused,
@Nullable Predicate<String> additionalValidator) {
PsiElement scope = PsiTreeUtil.getNonStrictParentOfType(place, PsiStatement.class, PsiCodeBlock.class, PsiMethod.class);
return UniqueNameGenerator.generateUniqueNameOneBased(
baseName, name -> !hasConflictingVariable(place, name, allowShadowing) &&
baseName, name -> (additionalValidator == null || additionalValidator.test(name)) &&
!hasConflictingVariable(place, name, allowShadowing) &&
(!lookForward || !hasConflictingVariableAfterwards(scope, name, canBeReused)));
}

View File

@@ -6,12 +6,14 @@ import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import java.util.function.Predicate;
public abstract class JavaCodeStyleManager {
@@ -171,6 +173,22 @@ public abstract class JavaCodeStyleManager {
@NotNull
public abstract String suggestUniqueVariableName(@NonNls @NotNull String baseName, PsiElement place, boolean lookForward);
/**
* Please, use {@link com.siyeh.ig.psiutils.VariableNameGenerator#skipNames(Collection)} instead of the direct call of this method
* Suggests a unique name for the variable used at the specified location. The returned name is guaranteed to not shadow
* the existing name.
*
* @param baseName the base name 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 skipNames the names which will not be used.
* @return the generated unique name,
*/
@ApiStatus.Internal
@NotNull
public abstract String suggestUniqueVariableName(@NonNls @NotNull String baseName, PsiElement place, boolean lookForward,
@NotNull Set<@NotNull String> skipNames);
/**
* Suggests a unique names for the variable used at the specified location. The resulting name info may contain names which
* shadow existing names.

View File

@@ -18,6 +18,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Predicate;
public class CoreJavaCodeStyleManager extends JavaCodeStyleManager {
@@ -87,13 +88,22 @@ 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);
return suggestUniqueVariableName(baseName, place, lookForward, v -> false, null);
}
@Override
public @NotNull String suggestUniqueVariableName(@NotNull String baseName,
PsiElement place,
boolean lookForward,
@NotNull Set<String> skipNames) {
return suggestUniqueVariableName(baseName, place, lookForward, v -> false, v -> !skipNames.contains(v));
}
private static @NotNull String suggestUniqueVariableName(@NotNull @NonNls String baseName,
PsiElement place,
boolean lookForward,
Predicate<? super PsiVariable> canBeReused) {
Predicate<? super PsiVariable> canBeReused,
@Nullable Predicate<String> additionalValidator) {
int index = 0;
PsiElement scope = PsiTreeUtil.getNonStrictParentOfType(place, PsiStatement.class, PsiCodeBlock.class, PsiMethod.class);
NextName:
@@ -103,6 +113,9 @@ 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;
@@ -140,7 +153,7 @@ public class CoreJavaCodeStyleManager extends JavaCodeStyleManager {
@Override
public @NotNull String suggestUniqueVariableName(@NotNull String baseName, PsiElement place, Predicate<? super PsiVariable> canBeReused) {
return suggestUniqueVariableName(baseName, place, true, canBeReused);
return suggestUniqueVariableName(baseName, place, true, canBeReused, null);
}
@Override