[java-highlighting] IDEA-341371 highlight duplicated classes with implicit classes

GitOrigin-RevId: 37e436871498d0c9470f164daaf2edb892082667
This commit is contained in:
Mikhail Pyltsin
2023-12-19 17:41:02 +01:00
committed by intellij-monorepo-bot
parent 7cbbfe6b03
commit 39b2be10ff
5 changed files with 94 additions and 8 deletions

View File

@@ -29,6 +29,7 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.java.stubs.index.JavaImplicitClassIndex;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
@@ -173,6 +174,7 @@ public final class HighlightClassUtil {
}
static HighlightInfo.Builder checkDuplicateTopLevelClass(@NotNull PsiClass aClass) {
if (aClass instanceof PsiImplicitClass) return null; //check in HighlightImplicitClassUtil
if (!(aClass.getParent() instanceof PsiFile)) return null;
String qualifiedName = aClass.getQualifiedName();
if (qualifiedName == null) return null;
@@ -181,12 +183,31 @@ public final class HighlightClassUtil {
qualifiedName = qualifiedName.replace('$', '.');
numOfClassesToFind = 1;
}
PsiManager manager = aClass.getManager();
Module module = ModuleUtilCore.findModuleForPsiElement(aClass);
if (module == null) return null;
PsiClass[] classes = JavaPsiFacade.getInstance(aClass.getProject()).findClasses(qualifiedName, GlobalSearchScope.moduleScope(module).intersectWith(aClass.getResolveScope()));
GlobalSearchScope scope = GlobalSearchScope.moduleScope(module).intersectWith(aClass.getResolveScope());
PsiClass[] classes = JavaPsiFacade.getInstance(aClass.getProject()).findClasses(qualifiedName, scope);
if (aClass.getContainingFile() instanceof PsiJavaFile javaFile && javaFile.getPackageStatement() == null) {
Collection<? extends PsiClass> implicitClasses =
JavaImplicitClassIndex.getInstance().getElements(qualifiedName, javaFile.getProject(), scope);
if (!implicitClasses.isEmpty()) {
ArrayList<PsiClass> newClasses = new ArrayList<>();
ContainerUtil.addAll(newClasses, classes);
ContainerUtil.addAll(newClasses, implicitClasses);
classes = newClasses.toArray(PsiClass.EMPTY_ARRAY);
}
}
if (classes.length < numOfClassesToFind) return null;
return checkDuplicateClasses(aClass, classes);
}
@Nullable
static HighlightInfo.Builder checkDuplicateClasses(@NotNull PsiClass aClass, @NotNull PsiClass @NotNull[] classes) {
PsiManager manager = aClass.getManager();
Module module = ModuleUtilCore.findModuleForPsiElement(aClass);
if (module == null) return null;
ModuleFileIndex fileIndex = ModuleRootManager.getInstance(module).getFileIndex();
VirtualFile virtualFile = PsiUtilCore.getVirtualFile(aClass);
if (virtualFile == null) return null;
@@ -362,12 +383,27 @@ public final class HighlightClassUtil {
@NotNull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String key) {
String message = JavaErrorBundle.message(key, name);
PsiIdentifier identifier = aClass.getNameIdentifier();
if (identifier == null) return null;
TextRange textRange = identifier.getTextRange();
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(message);
IntentionAction action = QuickFixFactory.getInstance().createRenameFix(aClass);
if (action != null) {
info.registerFix(action, null, null, null, null);
HighlightInfo.Builder info;
if (aClass instanceof PsiImplicitClass) {
info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(aClass)
.fileLevelAnnotation()
.description(message);
IntentionAction action = QuickFixFactory.getInstance().createRenameFix(aClass);
if (action != null) {
info.registerFix(action, null, null, null, null);
}
}
else {
if (identifier == null) return null;
TextRange textRange = identifier.getTextRange();
info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(textRange)
.descriptionAndTooltip(message);
IntentionAction action = QuickFixFactory.getInstance().createRenameFix(aClass);
if (action != null) {
info.registerFix(action, null, null, null, null);
}
}
return info;
}

View File

@@ -5,7 +5,10 @@ import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.JavaImplicitClassUtil;
import com.intellij.psi.util.PsiMethodUtil;
import com.intellij.util.containers.ContainerUtil;
@@ -63,4 +66,22 @@ public final class HighlightImplicitClassUtil {
}
return null;
}
@Nullable
static HighlightInfo.Builder checkDuplicateClasses(@NotNull PsiJavaFile file) {
if (!HighlightingFeature.IMPLICIT_CLASSES.isAvailable(file)) return null;
PsiImplicitClass implicitClass = JavaImplicitClassUtil.getImplicitClassFor(file);
if (implicitClass == null) return null;
Module module = ModuleUtilCore.findModuleForPsiElement(file);
if (module == null) return null;
GlobalSearchScope scope = GlobalSearchScope.moduleScope(module).intersectWith(implicitClass.getResolveScope());
String qualifiedName = implicitClass.getQualifiedName();
if (qualifiedName == null) {
return null;
}
PsiClass[] classes = JavaPsiFacade.getInstance(implicitClass.getProject()).findClasses(qualifiedName, scope);
return HighlightClassUtil.checkDuplicateClasses(implicitClass, classes);
}
}

View File

@@ -375,6 +375,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
super.visitJavaFile(file);
if (!hasErrorResults()) add(HighlightImplicitClassUtil.checkImplicitClassHasMainMethod(file));
if (!hasErrorResults()) add(HighlightImplicitClassUtil.checkImplicitClassFileIsValidIdentifier(file));
if (!hasErrorResults()) add(HighlightImplicitClassUtil.checkDuplicateClasses(file));
}
@Override

View File

@@ -0,0 +1,3 @@
public static void main(String[] args) {
System.out.println("I am an implicitly declared class");
}

View File

@@ -4,6 +4,7 @@ package com.intellij.java.codeInsight.daemon
import com.intellij.JavaTestUtil
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.IdeaTestUtil
import com.intellij.testFramework.UsefulTestCase
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
class ImplicitClassHighlightingTest : LightJavaCodeInsightFixtureTestCase() {
@@ -41,6 +42,30 @@ class ImplicitClassHighlightingTest : LightJavaCodeInsightFixtureTestCase() {
doTest()
}
fun testDuplicateImplicitClass() {
myFixture.configureByText("T.java", """
class DuplicateImplicitClass {
}
""".trimIndent())
myFixture.configureByFile(getTestName(false) + ".java")
val highlightings = myFixture.doHighlighting().filter { it?.description?.contains("Duplicate class") ?: false }
UsefulTestCase.assertNotEmpty(highlightings)
}
fun testDuplicateImplicitClass2() {
myFixture.configureByText("DuplicateImplicitClass2.java", """
public static void main(String[] args) {
System.out.println("I am an implicitly declared class");
}
""".trimIndent())
myFixture.configureByText("T.java", """
class DuplicateImplicitClass2 {}
""".trimIndent())
val highlightings = myFixture.doHighlighting().filter { it?.description?.contains("Duplicate class") ?: false }
UsefulTestCase.assertNotEmpty(highlightings)
}
private fun doTest() {
myFixture.configureByFile(getTestName(false) + ".java")
myFixture.checkHighlighting()