[java] Use context for passing language level identifier check

Migrates all usages of `LanguageLevel#HIGHEST` into `PsiNameHelper#isIdentifier`, such usages are wrong because whether something can be considered an identifier depends on the current effective language level. #IDEA-372983

GitOrigin-RevId: 966d5a91ca4cfcba22ae6ccb3555df06963740f6
This commit is contained in:
Bart van Helvert
2025-05-22 17:47:03 +02:00
committed by intellij-monorepo-bot
parent 156bdd7b04
commit 0fe9c881bf
4 changed files with 71 additions and 45 deletions

View File

@@ -7,7 +7,6 @@ import com.intellij.java.refactoring.JavaRefactoringBundle;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
@@ -15,6 +14,7 @@ import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.refactoring.MoveDestination;
import com.intellij.refactoring.RefactoringActionHandler;
@@ -67,7 +67,7 @@ public final class JavaIntroduceParameterObjectDelegate
String baseParameterName = StringUtil.decapitalize(descriptor.getClassName());
final Project project = method.getProject();
if (!PsiNameHelper.getInstance(project).isIdentifier(baseParameterName, LanguageLevel.HIGHEST)) {
if (!PsiNameHelper.getInstance(project).isIdentifier(baseParameterName, PsiUtil.getLanguageLevel(method))) {
baseParameterName = StringUtil.fixVariableNameDerivedFromPropertyName(baseParameterName);
}
final String paramName = body != null

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 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.codeInsight.completion;
import com.intellij.codeInsight.TailType;
@@ -118,7 +118,7 @@ public final class JavaMemberNameCompletionContributor extends CompletionContrib
SuggestedNameInfo suggestedNameInfo = codeStyleManager.suggestVariableName(variableKind, propertyName, null, type, StringUtil.isEmpty(matcher.getPrefix()));
suggestedNameInfo = codeStyleManager.suggestUniqueVariableName(suggestedNameInfo, var, false);
String[] suggestedNames = suggestedNameInfo.names;
addLookupItems(set, suggestedNameInfo, matcher, project, suggestedNames);
addLookupItems(set, suggestedNameInfo, matcher, project, var, suggestedNames);
if (!hasStartMatches(set, matcher)) {
Set<String> setOfNames = Arrays.stream(suggestedNames).collect(Collectors.toSet());
String objectName = "object";
@@ -134,23 +134,23 @@ public final class JavaMemberNameCompletionContributor extends CompletionContrib
}
if (!hasStartMatches(set, matcher) && includeOverlapped) {
addLookupItems(set, null, matcher, project, getOverlappedNameVersions(matcher.getPrefix(), suggestedNames, ""));
addLookupItems(set, null, matcher, project, var, getOverlappedNameVersions(matcher.getPrefix(), suggestedNames, ""));
}
PsiElement parent = PsiTreeUtil.getParentOfType(var, PsiCodeBlock.class);
if(parent == null) parent = PsiTreeUtil.getParentOfType(var, PsiMethod.class, PsiLambdaExpression.class);
addLookupItems(set, suggestedNameInfo, matcher, project, getUnresolvedReferences(parent, false, matcher));
addLookupItems(set, suggestedNameInfo, matcher, project, var, getUnresolvedReferences(parent, false, matcher));
if (var instanceof PsiParameter && parent instanceof PsiMethod) {
addSuggestionsInspiredByFieldNames(set, matcher, var, project, codeStyleManager);
PsiDocComment docComment = ((PsiMethod)parent).getDocComment();
if (docComment != null) {
addLookupItems(set, null, matcher, project, ArrayUtil.toStringArray(getUnresolvedMethodParamNamesFromJavadoc(docComment)));
addLookupItems(set, null, matcher, project, var, ArrayUtil.toStringArray(getUnresolvedMethodParamNamesFromJavadoc(docComment)));
}
}
PsiExpression initializer = var.getInitializer();
if (initializer != null) {
SuggestedNameInfo initializerSuggestions = CommonJavaRefactoringUtil.getSuggestedName(type, initializer);
addLookupItems(set, initializerSuggestions, matcher, project, initializerSuggestions.names);
addLookupItems(set, initializerSuggestions, matcher, project, var, initializerSuggestions.names);
}
}
@@ -206,7 +206,7 @@ public final class JavaMemberNameCompletionContributor extends CompletionContrib
String name = field.getName();
if (field.getType().isAssignableFrom(var.getType())) {
String prop = codeStyleManager.variableNameToPropertyName(name, VariableKind.FIELD);
addLookupItems(set, null, matcher, project, codeStyleManager.propertyNameToVariableName(prop, VariableKind.PARAMETER));
addLookupItems(set, null, matcher, project, var, codeStyleManager.propertyNameToVariableName(prop, VariableKind.PARAMETER));
}
}
}
@@ -289,7 +289,7 @@ public final class JavaMemberNameCompletionContributor extends CompletionContrib
SuggestedNameInfo suggestedNameInfo = codeStyleManager.suggestVariableName(VariableKind.PARAMETER, null, null, var.getType());
String[] suggestedNames = suggestedNameInfo.names;
addLookupItems(set, suggestedNameInfo, matcher, project, suggestedNames);
addLookupItems(set, suggestedNameInfo, matcher, project, var, suggestedNames);
}
private static void completeFieldName(Set<LookupElement> set, @NotNull PsiField var, @NotNull PrefixMatcher matcher, boolean includeOverlapped) {
@@ -307,7 +307,7 @@ public final class JavaMemberNameCompletionContributor extends CompletionContrib
SuggestedNameInfo suggestedNameInfo = codeStyleManager.suggestVariableName(variableKind, null, null, var.getType());
String[] suggestedNames = suggestedNameInfo.names;
addLookupItems(set, suggestedNameInfo, matcher, project, suggestedNames);
addLookupItems(set, suggestedNameInfo, matcher, project, var, suggestedNames);
if (!hasStartMatches(set, matcher) && includeOverlapped) {
// use suggested names as suffixes
@@ -318,17 +318,17 @@ public final class JavaMemberNameCompletionContributor extends CompletionContrib
}
addLookupItems(set, null, matcher, project, getOverlappedNameVersions(prefix, suggestedNames, requiredSuffix));
addLookupItems(set, null, matcher, project, var, getOverlappedNameVersions(prefix, suggestedNames, requiredSuffix));
}
addLookupItems(set, suggestedNameInfo, matcher, project, getUnresolvedReferences(var.getParent(), false, matcher));
addLookupItems(set, suggestedNameInfo, matcher, project, var, getUnresolvedReferences(var.getParent(), false, matcher));
PsiExpression initializer = var.getInitializer();
PsiClass containingClass = var.getContainingClass();
if (initializer != null && containingClass != null) {
SuggestedNameInfo initializerSuggestions = JavaNameSuggestionUtil.suggestFieldName(
var.getType(), null, initializer, var.hasModifierProperty(PsiModifier.STATIC), containingClass);
addLookupItems(set, initializerSuggestions, matcher, project, initializerSuggestions.names);
addLookupItems(set, initializerSuggestions, matcher, project, var, initializerSuggestions.names);
}
}
@@ -341,7 +341,7 @@ public final class JavaMemberNameCompletionContributor extends CompletionContrib
SuggestedNameInfo suggestedNameInfo = codeStyleManager.suggestVariableName(varKind, null, null, varType);
String[] strings = completeVariableNameForRefactoring(codeStyleManager, matcher, varType, varKind, suggestedNameInfo,
includeOverlapped, methodPrefix);
addLookupItems(set, suggestedNameInfo, matcher, project, strings);
addLookupItems(set, suggestedNameInfo, matcher, project, null, strings);
}
static String @NotNull [] completeVariableNameForRefactoring(@NotNull JavaCodeStyleManager codeStyleManager,
@@ -381,7 +381,7 @@ public final class JavaMemberNameCompletionContributor extends CompletionContrib
if (containingClass != null && !(containingClass instanceof PsiImplicitClass)) {
String name = containingClass.getName();
if (StringUtil.isNotEmpty(name)) {
addLookupItems(set, null, matcher, element.getProject(), name);
addLookupItems(set, null, matcher, element.getProject(), element, name);
}
}
return;
@@ -405,9 +405,9 @@ public final class JavaMemberNameCompletionContributor extends CompletionContrib
.withInsertHandler(ParenthesesInsertHandler.WITH_PARAMETERS));
}
addLookupItems(set, null, matcher, element.getProject(), getUnresolvedReferences(ourClassParent, true, matcher));
addLookupItems(set, null, matcher, element.getProject(), element, getUnresolvedReferences(ourClassParent, true, matcher));
addLookupItems(set, null, matcher, element.getProject(), getPropertiesHandlersNames(
addLookupItems(set, null, matcher, element.getProject(), element, getPropertiesHandlersNames(
ourClassParent,
((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC),
PsiUtil.getTypeByPsiElement(element), element));
@@ -451,11 +451,18 @@ public final class JavaMemberNameCompletionContributor extends CompletionContrib
return ArrayUtilRt.toStringArray(propertyHandlers);
}
private static void addLookupItems(Set<LookupElement> lookupElements, @Nullable SuggestedNameInfo callback, PrefixMatcher matcher, Project project, String... strings) {
private static void addLookupItems(
Set<LookupElement> lookupElements,
@Nullable SuggestedNameInfo callback,
PrefixMatcher matcher, Project project,
@Nullable PsiElement context,
String... strings
) {
LanguageLevel languageLevel = context != null ? PsiUtil.getLanguageLevel(context) : PsiUtil.getLanguageLevel(project);
outer:
for (int i = 0; i < strings.length; i++) {
String name = strings[i];
if (!matcher.prefixMatches(name) || !PsiNameHelper.getInstance(project).isIdentifier(name, LanguageLevel.HIGHEST)) {
if (!matcher.prefixMatches(name) || !PsiNameHelper.getInstance(project).isIdentifier(name, languageLevel)) {
continue;
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 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.psi.impl.source.codeStyle;
import com.intellij.application.options.CodeStyle;
@@ -15,6 +15,7 @@ import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.LiteralNameSuggester;
import com.intellij.openapi.util.text.PastParticiple;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.JavaRelease;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.*;
@@ -224,7 +225,7 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
public @Nullable String suggestCompiledParameterName(@NotNull PsiType type) {
// avoid hang due to nice name evaluation that uses indices for resolve (IDEA-116803)
Collection<String> result = doSuggestParameterNamesByTypeWithoutIndex(type);
return ContainerUtil.getFirstItem(getSuggestionsByNames(result, VariableKind.PARAMETER, true));
return ContainerUtil.getFirstItem(getSuggestionsByNames(result, VariableKind.PARAMETER, true, null));
}
@Override
@@ -240,7 +241,7 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
Set<String> names = new LinkedHashSet<>();
if (propertyName != null) {
String[] namesByName = ArrayUtilRt.toStringArray(getSuggestionsByName(propertyName, kind, correctKeywords));
String[] namesByName = ArrayUtilRt.toStringArray(getSuggestionsByName(propertyName, kind, correctKeywords, expr));
sortVariableNameSuggestions(namesByName, kind, propertyName, null);
ContainerUtil.addAll(names, namesByName);
}
@@ -248,7 +249,7 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
final NamesByExprInfo namesByExpr;
if (expr != null) {
namesByExpr = suggestVariableNameByExpression(expr, kind);
String[] suggestions = ArrayUtilRt.toStringArray(getSuggestionsByNames(namesByExpr.names, kind, correctKeywords));
String[] suggestions = ArrayUtilRt.toStringArray(getSuggestionsByNames(namesByExpr.names, kind, correctKeywords, expr));
if (namesByExpr.propertyName != null) {
sortVariableNameSuggestions(suggestions, kind, namesByExpr.propertyName, null);
}
@@ -259,7 +260,7 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
}
if (type != null) {
String[] namesByType = suggestVariableNameByType(type, kind, correctKeywords);
String[] namesByType = suggestVariableNameByType(type, kind, correctKeywords, expr);
sortVariableNameSuggestions(namesByType, kind, null, type);
ContainerUtil.addAll(names, namesByType);
}
@@ -328,9 +329,13 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
}
}
private String @NotNull [] suggestVariableNameByType(@NotNull PsiType type, @NotNull VariableKind variableKind, boolean correctKeywords) {
private String @NotNull [] suggestVariableNameByType(
@NotNull PsiType type,
@NotNull VariableKind variableKind,
boolean correctKeywords,
@Nullable PsiElement context) {
Collection<String> byTypeNames = doSuggestNamesByType(type, variableKind);
return ArrayUtilRt.toStringArray(getSuggestionsByNames(byTypeNames, variableKind, correctKeywords));
return ArrayUtilRt.toStringArray(getSuggestionsByNames(byTypeNames, variableKind, correctKeywords, context));
}
private @NotNull Collection<String> doSuggestParameterNamesByTypeWithoutIndex(@NotNull PsiType type) {
@@ -366,13 +371,16 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
return suggestions;
}
private @Nullable String suggestNameFromTypeMap(@NotNull PsiType type, @NotNull VariableKind variableKind, @Nullable String longTypeName) {
private @Nullable String suggestNameFromTypeMap(
@NotNull PsiType type,
@NotNull VariableKind variableKind,
@Nullable String longTypeName) {
if (longTypeName != null) {
if (type.equals(PsiTypes.nullType())) {
longTypeName = CommonClassNames.JAVA_LANG_OBJECT;
}
String name = nameByType(longTypeName, variableKind);
if (name != null && isIdentifier(name)) {
if (name != null && isIdentifier(name, null)) {
return type instanceof PsiArrayType ? StringUtil.pluralize(name) : name;
}
}
@@ -771,7 +779,7 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
}
else if (expr instanceof PsiLiteralExpression) {
final String text = StringUtil.unquoteString(expr.getText());
if (isIdentifier(text)) {
if (isIdentifier(text, expr)) {
return new NamesByExprInfo(text);
}
}
@@ -989,19 +997,29 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
name = Character.toUpperCase(name.charAt(0)) + name.substring(1);
}
name = prefix + name + getSuffixByVariableKind(variableKind);
name = changeIfNotIdentifier(name);
name = changeIfNotIdentifier(name, null);
return name;
}
private @NotNull Collection<String> getSuggestionsByNames(@NotNull Iterable<String> names, @NotNull VariableKind kind, boolean correctKeywords) {
private @NotNull Collection<String> getSuggestionsByNames(
@NotNull Iterable<String> names,
@NotNull VariableKind kind,
boolean correctKeywords,
@Nullable PsiElement context
) {
final Collection<String> suggestions = new LinkedHashSet<>();
for (String name : names) {
suggestions.addAll(getSuggestionsByName(name, kind, correctKeywords));
suggestions.addAll(getSuggestionsByName(name, kind, correctKeywords, context));
}
return suggestions;
}
private @NotNull Collection<String> getSuggestionsByName(@NotNull String name, @NotNull VariableKind variableKind, boolean correctKeywords) {
private @NotNull Collection<String> getSuggestionsByName(
@NotNull String name,
@NotNull VariableKind variableKind,
boolean correctKeywords,
@Nullable PsiElement context
) {
if (!StringUtil.isJavaIdentifier(name)) return List.of();
boolean upperCaseStyle = variableKind == VariableKind.STATIC_FINAL_FIELD;
boolean preferLongerNames = getJavaSettings().PREFER_LONGER_NAMES;
@@ -1010,11 +1028,11 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
List<String> answer = new ArrayList<>();
for (String suggestion : NameUtil.getSuggestionsByName(name, prefix, suffix, upperCaseStyle, preferLongerNames, false)) {
answer.add(correctKeywords ? changeIfNotIdentifier(suggestion) : suggestion);
answer.add(correctKeywords ? changeIfNotIdentifier(suggestion, context) : suggestion);
}
String wordByPreposition = getWordByPreposition(name, prefix, suffix, upperCaseStyle);
if (wordByPreposition != null && (!correctKeywords || isIdentifier(wordByPreposition))) {
if (wordByPreposition != null && (!correctKeywords || isIdentifier(wordByPreposition, context))) {
answer.add(wordByPreposition);
}
if (name.equals("hashCode")) {
@@ -1228,7 +1246,7 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
suggestSemanticNamesByType(type, kind)
);
final Set<String> suggestions = new LinkedHashSet<>(getSuggestionsByNames(allSemanticNames, kind, true));
final Set<String> suggestions = new LinkedHashSet<>(getSuggestionsByNames(allSemanticNames, kind, true, null));
final String propertyName = ContainerUtil.getFirstItem(semanticNames);
addNamesFromStatistics(suggestions, kind, propertyName, type);
@@ -1243,18 +1261,19 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
};
}
private @NonNls @NotNull String changeIfNotIdentifier(@NotNull String name) {
if (!isIdentifier(name)) {
private @NonNls @NotNull String changeIfNotIdentifier(@NotNull String name, @Nullable PsiElement context) {
if (!isIdentifier(name, context)) {
String propertyName = StringUtil.fixVariableNameDerivedFromPropertyName(name);
if (isIdentifier(propertyName)) {
if (isIdentifier(propertyName, context)) {
return propertyName;
}
}
return name;
}
private boolean isIdentifier(@NotNull String name) {
return PsiNameHelper.getInstance(myProject).isIdentifier(name, LanguageLevel.HIGHEST);
private boolean isIdentifier(@NotNull String name, @Nullable PsiElement context) {
LanguageLevel languageLevel = context != null ? PsiUtil.getLanguageLevel(context) : JavaRelease.getHighest();
return PsiNameHelper.getInstance(myProject).isIdentifier(name, languageLevel);
}
private @NotNull JavaCodeStyleSettings getJavaSettings() {

View File

@@ -1,13 +1,13 @@
// Copyright 2000-2024 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.refactoring.util;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.LanguageLevel;
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.psi.util.PsiUtil;
import com.intellij.refactoring.ui.NameSuggestionsGenerator;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
@@ -54,7 +54,7 @@ public final class JavaNameSuggestionUtil {
public static String[] appendUnresolvedExprName(String[] names, final PsiExpression expr) {
if (expr instanceof PsiReferenceExpression && ((PsiReferenceExpression)expr).resolve() == null) {
final String name = expr.getText();
if (PsiNameHelper.getInstance(expr.getProject()).isIdentifier(name, LanguageLevel.HIGHEST)) {
if (PsiNameHelper.getInstance(expr.getProject()).isIdentifier(name, PsiUtil.getLanguageLevel(expr))) {
names = ArrayUtil.mergeArrays(new String[]{name}, names);
}
}