IJ-CR-181688 [java-inspections] IDEA-371026 Exclusions from static imports

- more precise fixes

GitOrigin-RevId: 46829ef0b0ccef7d9d19f2686d5abc0700422914
This commit is contained in:
Mikhail Pyltsin
2025-11-10 18:58:36 +00:00
committed by intellij-monorepo-bot
parent 104946b962
commit 4d9e52d8fa
5 changed files with 125 additions and 71 deletions
@@ -53,12 +53,8 @@ public class JavaAutoImportOptions(public val project: Project) : UiDslUnnamedCo
JavaBundle.message("auto.static.import.scope")) {
override fun validate(value: Any?, component: JComponent?): ValidationInfo? {
val info = super.validate(value, component)
if (info != null && value is String && value.startsWith("-")) {
val cutInfo = super.validate(value.substring(1), component)
if (cutInfo == null) return null
}
return info
if (value !is String) return null
return super.validate(value.substring(if (value.startsWith("-")) 1 else 0), component)
}
override fun getIdeRows(): Array<out String> {
@@ -23,10 +23,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -44,22 +41,25 @@ public class JavaProjectCodeInsightSettings implements PersistentStateComponent<
return project.getService(JavaProjectCodeInsightSettings.class);
}
@NotNull
public AutoStaticNameContainer getAllIncludedAutoStaticNames() {
List<String> names = new ArrayList<>(includedAutoStaticNames);
names.addAll(JavaIdeCodeInsightSettings.getInstance().includedAutoStaticNames);
return AutoStaticNameContainer.create(names);
}
public record AutoStaticNameContainer(LinkedHashSet<String> includedNames, LinkedHashSet<String> excludedNames) {
public record AutoStaticNameContainer(@NotNull Set<String> includedNames,
@NotNull Set<String> excludedNames) {
public boolean containsName(@NotNull String name) {
return (includedNames.contains(name) || includedNames.contains(StringUtil.getPackageName(name))) &&
!(excludedNames.contains(name) || excludedNames.contains(StringUtil.getPackageName(name)));
}
public static AutoStaticNameContainer create(List<String> allNames) {
LinkedHashSet<String> includedNames = new LinkedHashSet<>();
LinkedHashSet<String> excludedNames = new LinkedHashSet<>();
@NotNull
public static AutoStaticNameContainer create(@NotNull List<String> allNames) {
Set<String> includedNames = new HashSet<>();
Set<String> excludedNames = new HashSet<>();
for (String name : allNames) {
if (name == null) continue;
if (StringUtil.isEmptyOrSpaces(name)) continue;
@@ -15,31 +15,32 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ObjectUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public final class StaticImportCanBeUsedInspection extends AbstractBaseJavaLocalInspectionTool implements CleanupLocalInspectionTool {
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
boolean isOnTheFly,
@NotNull LocalInspectionToolSession session) {
JavaProjectCodeInsightSettings settings = JavaProjectCodeInsightSettings.getSettings(holder.getProject());
Project project = holder.getProject();
JavaProjectCodeInsightSettings settings = JavaProjectCodeInsightSettings.getSettings(project);
JavaProjectCodeInsightSettings.AutoStaticNameContainer autoStaticNames = settings.getAllIncludedAutoStaticNames();
if (autoStaticNames.includedNames().isEmpty()) {
return PsiElementVisitor.EMPTY_VISITOR;
}
Set<String> shortNames = autoStaticNames.includedNames().stream()
.map(name -> StringUtil.getShortName(name))
.collect(Collectors.toSet());
Set<String> classShortNames = autoStaticNames.includedNames().stream()
.map(name -> StringUtil.getShortName(StringUtil.getPackageName(name)))
.filter(name -> !name.isBlank())
.collect(Collectors.toSet());
shortNames.addAll(classShortNames);
Set<String> shortNames = StreamEx.of(autoStaticNames.includedNames())
.map(name -> StringUtil.getShortName(name))
.append(autoStaticNames.includedNames().stream()
.map(name -> StringUtil.getShortName(StringUtil.getPackageName(name)))
.filter(name -> !name.isBlank()))
.toSet();
return new JavaElementVisitor() {
@Override
@@ -53,34 +54,50 @@ public final class StaticImportCanBeUsedInspection extends AbstractBaseJavaLocal
if (qualifierReference == null || name == null || !shortNames.contains(name)) return;
StaticImportContext context = findOnDemandImportContext(expression);
if (context == null) return;
List<LocalQuickFix> fixes = new ArrayList<>();
fixes.add(new StaticImportFix());
fixes.add(new DeleteSettingsFromAutoImportTable(context.fqn, project));
JavaProjectCodeInsightSettings projectSettings = JavaProjectCodeInsightSettings.getSettings(project);
JavaIdeCodeInsightSettings ideSettings = JavaIdeCodeInsightSettings.getInstance();
String packageName = StringUtil.getPackageName(context.fqn);
if (!projectSettings.includedAutoStaticNames.contains(context.fqn) &&
projectSettings.includedAutoStaticNames.contains(packageName) ||
!ideSettings.includedAutoStaticNames.contains(context.fqn) &&
ideSettings.includedAutoStaticNames.contains(packageName)) {
fixes.add(new AddExclusionFromAutoImportTable(context.fqn, project));
}
holder.registerProblem(qualifierReference,
JavaBundle.message("inspection.static.import.can.be.used.display.name"),
new StaticImportFix(),
new DeleteSettingsFromAutoImportTable(context.fqn),
new AddExclusionFromAutoImportTable(context.fqn));
fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
};
}
private static class AddExclusionFromAutoImportTable extends ModCommandQuickFix {
private final String myFqn;
private final @NotNull String myFqn;
private final boolean myIsProjectSettings;
private AddExclusionFromAutoImportTable(String fqn) { myFqn = fqn; }
private AddExclusionFromAutoImportTable(@NotNull String fqn, @NotNull Project project) {
myFqn = fqn;
JavaProjectCodeInsightSettings settings = JavaProjectCodeInsightSettings.getSettings(project);
myIsProjectSettings = settings.includedAutoStaticNames.contains(myFqn) ||
settings.includedAutoStaticNames.contains(StringUtil.getPackageName(myFqn));
}
@Override
public @NotNull ModCommand perform(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getPsiElement();
if (element == null) return ModCommand.nop();
JavaIdeCodeInsightSettings settings = JavaIdeCodeInsightSettings.getInstance();
if (settings.includedAutoStaticNames.contains(myFqn) || settings.includedAutoStaticNames.contains(StringUtil.getPackageName(myFqn))) {
if (myIsProjectSettings) {
return ModCommand.updateOptionList(element,
"JavaIdeCodeInsightSettings.includedAutoStaticNames",
"JavaProjectCodeInsightSettings.includedAutoStaticNames",
strings -> {
strings.add("-" + myFqn);
});
}
return ModCommand.updateOptionList(element,
"JavaProjectCodeInsightSettings.includedAutoStaticNames",
"JavaIdeCodeInsightSettings.includedAutoStaticNames",
strings -> {
strings.add("-" + myFqn);
});
@@ -88,7 +105,8 @@ public final class StaticImportCanBeUsedInspection extends AbstractBaseJavaLocal
@Override
public @NotNull String getName() {
return JavaBundle.message("inspection.static.import.can.be.used.exclude.from.auto.import.name", myFqn);
return JavaBundle.message("inspection.static.import.can.be.used.exclude.from.auto.import.name", myFqn,
myIsProjectSettings ? 0 : 1);
}
@Override
@@ -98,38 +116,46 @@ public final class StaticImportCanBeUsedInspection extends AbstractBaseJavaLocal
}
private static class DeleteSettingsFromAutoImportTable extends ModCommandQuickFix {
private final String myFqn;
private final @NotNull String myFqn;
private final boolean myIsProjectSettings;
private DeleteSettingsFromAutoImportTable(String fqn) { myFqn = fqn; }
private DeleteSettingsFromAutoImportTable(@NotNull String fqn, @NotNull Project project) {
myFqn = fqn;
JavaProjectCodeInsightSettings settings = JavaProjectCodeInsightSettings.getSettings(project);
myIsProjectSettings = settings.includedAutoStaticNames.contains(myFqn) ||
settings.includedAutoStaticNames.contains(StringUtil.getPackageName(myFqn));
}
@Override
public @NotNull ModCommand perform(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getPsiElement();
if (element == null) return ModCommand.nop();
JavaIdeCodeInsightSettings settings = JavaIdeCodeInsightSettings.getInstance();
if (settings.includedAutoStaticNames.contains(myFqn) || settings.includedAutoStaticNames.contains(StringUtil.getPackageName(myFqn))) {
String packageName = StringUtil.getPackageName(myFqn);
if (myIsProjectSettings) {
return ModCommand.updateOptionList(element,
"JavaIdeCodeInsightSettings.includedAutoStaticNames",
"JavaProjectCodeInsightSettings.includedAutoStaticNames",
strings -> {
strings.removeIf(t ->
t.startsWith(StringUtil.getPackageName(myFqn)) ||
t.startsWith("-" + StringUtil.getPackageName(myFqn))
t.equals(myFqn) ||
t.equals(packageName) ||
t.equals("-" + packageName)
);
});
}
return ModCommand.updateOptionList(element,
"JavaProjectCodeInsightSettings.includedAutoStaticNames",
"JavaIdeCodeInsightSettings.includedAutoStaticNames",
strings -> {
strings.removeIf(t->
t.startsWith(StringUtil.getPackageName(myFqn)) ||
t.startsWith("-" + StringUtil.getPackageName(myFqn))
);
strings.removeIf(t ->
t.equals(myFqn) ||
t.equals(packageName) ||
t.equals("-" + packageName)
);
});
}
@Override
public @NotNull String getName() {
return JavaBundle.message("inspection.static.import.can.be.used.remove.from.auto.import.name", myFqn);
return JavaBundle.message("inspection.static.import.can.be.used.remove.from.auto.import.name", myFqn, myIsProjectSettings ? 0 : 1);
}
@Override
@@ -46,7 +46,7 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
public void testSimple() {
try {
addStaticAutoImport("java.util.Arrays");
addStaticAutoImportToProject("java.util.Arrays");
doTest();
cleanupTest();
}
@@ -57,8 +57,8 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
public void testSimpleWithExclusion() {
try {
addStaticAutoImport("java.util.Arrays");
addStaticAutoImport("-java.util.Arrays.sort");
addStaticAutoImportToProject("java.util.Arrays");
addStaticAutoImportToProject("-java.util.Arrays.sort");
doTest();
}
finally {
@@ -68,11 +68,11 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
public void testRemoveAutoImportProject() {
try {
addStaticAutoImport("java.util.Arrays");
addStaticAutoImport("-java.util.Arrays.binarySearch");
addStaticAutoImportToProject("java.util.Arrays");
addStaticAutoImportToProject("-java.util.Arrays.binarySearch");
doTest();
IntentionAction intention = myFixture.getAvailableIntention(
JavaBundle.message("inspection.static.import.can.be.used.remove.from.auto.import.name", "java.util.Arrays.sort"));
JavaBundle.message("inspection.static.import.can.be.used.remove.from.auto.import.name", "java.util.Arrays.sort", 0));
myFixture.launchAction(intention);
checkAutoImportDoesntContain("java.util.Arrays");
}
@@ -81,13 +81,33 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
}
}
public void testExcludeAutoImportProject() {
public void testRemoveAutoImportProjectAdditionalImports() {
try {
addStaticAutoImport("java.util.Arrays");
addStaticAutoImport("-java.util.Arrays.binarySearch");
addStaticAutoImportToProject("java.util.Arrays");
addStaticAutoImportToProject("java.util.Arrays2");
addStaticAutoImportToProject("java.util.A");
addStaticAutoImportToProject("java.util");
doNamedTest("RemoveAutoImportProject");
IntentionAction intention = myFixture.getAvailableIntention(
JavaBundle.message("inspection.static.import.can.be.used.exclude.from.auto.import.name", "java.util.Arrays.sort"));
JavaBundle.message("inspection.static.import.can.be.used.remove.from.auto.import.name", "java.util.Arrays.sort", 0));
myFixture.launchAction(intention);
checkAutoImportDoesntContain("java.util.Arrays.sort");
checkAutoImportContains("java.util.Arrays2");
checkAutoImportContains("java.util.A");
checkAutoImportContains("java.util");
}
finally {
cleanTable();
}
}
public void testExcludeAutoImportProject() {
try {
addStaticAutoImportToProject("java.util.Arrays");
addStaticAutoImportToProject("-java.util.Arrays.binarySearch");
doNamedTest("RemoveAutoImportProject");
IntentionAction intention = myFixture.getAvailableIntention(
JavaBundle.message("inspection.static.import.can.be.used.exclude.from.auto.import.name", "java.util.Arrays.sort", 0));
myFixture.launchAction(intention);
checkAutoImportContains("java.util.Arrays");
checkAutoImportDoesntContain("java.util.Arrays.sort");
@@ -104,7 +124,7 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
addStaticAutoImportToIde("-java.util.Arrays.binarySearch");
doTest();
IntentionAction intention = myFixture.getAvailableIntention(
JavaBundle.message("inspection.static.import.can.be.used.remove.from.auto.import.name", "java.util.Arrays.sort"));
JavaBundle.message("inspection.static.import.can.be.used.remove.from.auto.import.name", "java.util.Arrays.sort", 1));
myFixture.launchAction(intention);
checkAutoImportDoesntContain("java.util.Arrays");
}
@@ -119,7 +139,7 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
addStaticAutoImportToIde("-java.util.Arrays.binarySearch");
doNamedTest("RemoveAutoImportIde");
IntentionAction intention = myFixture.getAvailableIntention(
JavaBundle.message("inspection.static.import.can.be.used.exclude.from.auto.import.name", "java.util.Arrays.sort"));
JavaBundle.message("inspection.static.import.can.be.used.exclude.from.auto.import.name", "java.util.Arrays.sort", 1));
myFixture.launchAction(intention);
checkAutoImportContains("java.util.Arrays");
checkAutoImportDoesntContain("java.util.Arrays.sort");
@@ -130,6 +150,18 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
}
}
public void testExcludeAutoImportIdeMethodAdded() {
try {
addStaticAutoImportToIde("java.util.Arrays.sort");
doNamedTest("RemoveAutoImportIde");
String message = JavaBundle.message("inspection.static.import.can.be.used.exclude.from.auto.import.name", "java.util.Arrays.sort", 1);
assertNull(myFixture.getAvailableIntention(message));
}
finally {
cleanTable();
}
}
private void checkAutoImportDoesntContain(@NotNull String fqn) {
assertFalse(JavaCodeStyleManager.getInstance(getProject()).isStaticAutoImportName(fqn));
}
@@ -139,7 +171,7 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
}
public void testSimpleWithOnDemand() {
addStaticAutoImport("java.util.Arrays");
addStaticAutoImportToProject("java.util.Arrays");
JavaCodeStyleSettings javaSettings = JavaCodeStyleSettings.getInstance(getProject());
PackageEntry entry = new PackageEntry(true, "java.util.Arrays", false);
PackageEntryTable onDemand = javaSettings.PACKAGES_TO_USE_IMPORT_ON_DEMAND;
@@ -157,7 +189,7 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
public void testSimpleMethod() {
try {
addStaticAutoImport("java.util.Arrays.sort");
addStaticAutoImportToProject("java.util.Arrays.sort");
doTest();
cleanupTest();
}
@@ -167,7 +199,7 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
}
public void testSimpleMethodOnDemand() {
addStaticAutoImport("java.util.Arrays.sort");
addStaticAutoImportToProject("java.util.Arrays.sort");
JavaCodeStyleSettings javaSettings = JavaCodeStyleSettings.getInstance(getProject());
PackageEntry entry = new PackageEntry(true, "java.util.Arrays", false);
PackageEntryTable onDemand = javaSettings.PACKAGES_TO_USE_IMPORT_ON_DEMAND;
@@ -185,7 +217,7 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
public void testWithConflicts() {
try {
addStaticAutoImport("java.util.Arrays");
addStaticAutoImportToProject("java.util.Arrays");
doTest();
}
finally {
@@ -195,7 +227,7 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
public void testWithConflicts2() {
try {
addStaticAutoImport("java.util.Arrays");
addStaticAutoImportToProject("java.util.Arrays");
myFixture.addClass("""
package org;
@@ -212,7 +244,7 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
public void testWithConflictsWithField() {
try {
addStaticAutoImport("org.Foo2");
addStaticAutoImportToProject("org.Foo2");
myFixture.addClass("""
package org;
@@ -237,7 +269,7 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
public void testWithConflictsWithClass() {
try {
addStaticAutoImport("org.Foo2");
addStaticAutoImportToProject("org.Foo2");
myFixture.addClass("""
package org;
@@ -262,7 +294,7 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
public void testAlreadyImported() {
try {
addStaticAutoImport("java.util.Arrays");
addStaticAutoImportToProject("java.util.Arrays");
doTest();
cleanupTest();
}
@@ -283,11 +315,11 @@ public class StaticImportCanBeUsedInspectionTest extends LightJavaInspectionTest
}
@SuppressWarnings("SameParameterValue")
private void addStaticAutoImport(@NotNull String name) {
private void addStaticAutoImportToProject(@NotNull String name) {
JavaProjectCodeInsightSettings.getSettings(getProject()).includedAutoStaticNames.add(name);
}
private static void addStaticAutoImportToIde(@NotNull String name) {
JavaIdeCodeInsightSettings.getInstance().includedAutoStaticNames.add(name);
}
}
}
@@ -1445,9 +1445,9 @@ inspection.slow.list.contains.all.fix.name=Wrap ''{0}'' in ''HashSet'' construct
inspection.static.import.can.be.used.display.name=Static import can be used based on the auto-import table
inspection.static.import.can.be.used.fix.name=Add static import
inspection.static.import.can.be.used.remove.from.auto.import.family.name=Remove on-demand static import from the auto-import table
inspection.static.import.can.be.used.remove.from.auto.import.name=Remove ''{0}'' and its parent from the auto-import table
inspection.static.import.can.be.used.remove.from.auto.import.name=Remove ''{0}'' and its parent from the {1, choice, 0#project|1#IDE} auto-import table
inspection.static.import.can.be.used.exclude.from.auto.import.family.name=Add exclusion for on-demand static import to the auto-import table
inspection.static.import.can.be.used.exclude.from.auto.import.name=Add exclusion for ''{0}'' to the auto-import table
inspection.static.import.can.be.used.exclude.from.auto.import.name=Add exclusion for ''{0}'' to the {1, choice, 0#project|1#IDE} auto-import table
slice.filter.parse.error.null.filter.not.applicable.for.primitive.type=''null'' filter is not applicable to primitive type {0}
slice.filter.parse.error.not.null.filter.not.applicable.for.primitive.type=''!null'' filter is not applicable to primitive type {0}