mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java] module 'exports' statement highlighting
This commit is contained in:
+12
@@ -1621,6 +1621,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(ModuleHighlightUtil.checkFileName(module, myFile));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(ModuleHighlightUtil.checkFileDuplicates(module, myFile));
|
||||
if (!myHolder.hasErrorResults()) myHolder.addAll(ModuleHighlightUtil.checkDuplicateRequires(module));
|
||||
if (!myHolder.hasErrorResults()) myHolder.addAll(ModuleHighlightUtil.checkDuplicateExports(module));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(ModuleHighlightUtil.checkFileLocation(module, myFile));
|
||||
}
|
||||
|
||||
@@ -1634,6 +1635,17 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitExportsStatement(PsiExportsStatement statement) {
|
||||
super.visitExportsStatement(statement);
|
||||
if (PsiUtil.isLanguageLevel9OrHigher(myFile)) {
|
||||
PsiJavaModule container = (PsiJavaModule)statement.getParent();
|
||||
PsiJavaCodeReferenceElement ref = statement.getPackageReference();
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(ModuleHighlightUtil.checkPackageReference(ref));
|
||||
if (!myHolder.hasErrorResults()) myHolder.addAll(ModuleHighlightUtil.checkExportTargets(statement, container));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private HighlightInfo checkFeature(@NotNull PsiElement element, @NotNull Feature feature) {
|
||||
return HighlightUtil.checkFeature(element, feature, myLanguageLevel, myFile);
|
||||
|
||||
+86
@@ -148,6 +148,78 @@ public class ModuleHighlightUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static HighlightInfo checkPackageReference(@Nullable PsiJavaCodeReferenceElement refElement) {
|
||||
if (refElement != null) {
|
||||
PsiElement target = refElement.resolve();
|
||||
if (target instanceof PsiPackage) {
|
||||
Module module = ModuleUtilCore.findModuleForPsiElement(refElement);
|
||||
if (module != null) {
|
||||
String packageName = ((PsiPackage)target).getQualifiedName();
|
||||
PsiDirectory[] directories = ((PsiPackage)target).getDirectories(new ModulesScope(module));
|
||||
if (directories.length == 0) {
|
||||
String message = JavaErrorMessages.message("package.not.found", packageName);
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).description(message).create();
|
||||
}
|
||||
if (isEmpty(directories, packageName)) {
|
||||
String message = JavaErrorMessages.message("package.is.empty", packageName);
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).description(message).create();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static List<HighlightInfo> checkExportTargets(@NotNull PsiExportsStatement statement, @NotNull PsiJavaModule container) {
|
||||
List<HighlightInfo> results = ContainerUtil.newSmartList();
|
||||
|
||||
Set<String> targets = ContainerUtil.newHashSet();
|
||||
for (PsiJavaModuleReferenceElement refElement : psiTraverser().children(statement).filter(PsiJavaModuleReferenceElement.class)) {
|
||||
String refText = refElement.getReferenceText();
|
||||
PsiPolyVariantReference ref = refElement.getReference();
|
||||
assert ref != null : statement;
|
||||
PsiElement target = ref.resolve();
|
||||
if (!(target instanceof PsiJavaModule)) {
|
||||
String message = JavaErrorMessages.message("module.not.found", refText);
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(refElement).description(message).create());
|
||||
}
|
||||
else if (!targets.add(refText)) {
|
||||
String message = JavaErrorMessages.message("module.duplicate.export", refText);
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).description(message).create());
|
||||
}
|
||||
else if (target == container) {
|
||||
String message = JavaErrorMessages.message("module.self.export");
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(refElement).description(message).create());
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static List<HighlightInfo> checkDuplicateExports(@NotNull PsiJavaModule module) {
|
||||
List<HighlightInfo> results = ContainerUtil.newSmartList();
|
||||
|
||||
Set<String> names = ContainerUtil.newHashSet();
|
||||
for (PsiExportsStatement statement : psiTraverser().children(module).filter(PsiExportsStatement.class)) {
|
||||
PsiJavaCodeReferenceElement ref = statement.getPackageReference();
|
||||
if (ref != null) {
|
||||
String text = PsiNameHelper.getQualifiedClassName(ref.getText(), true);
|
||||
if (!names.add(text)) {
|
||||
String message = JavaErrorMessages.message("module.duplicate.export", text);
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).description(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, new DeleteElementFix(statement));
|
||||
results.add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static QuickFixFactory factory() {
|
||||
return QuickFixFactory.getInstance();
|
||||
}
|
||||
@@ -155,4 +227,18 @@ public class ModuleHighlightUtil {
|
||||
private static TextRange range(PsiJavaModule module) {
|
||||
return new TextRange(module.getTextOffset(), module.getNameElement().getTextRange().getEndOffset());
|
||||
}
|
||||
|
||||
private static boolean isEmpty(PsiDirectory[] directories, String packageName) {
|
||||
for (PsiDirectory directory : directories) {
|
||||
for (PsiFile file : directory.getFiles()) {
|
||||
if (file instanceof PsiClassOwner &&
|
||||
packageName.equals(((PsiClassOwner)file).getPackageName()) &&
|
||||
((PsiClassOwner)file).getClasses().length > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,13 @@
|
||||
*/
|
||||
package com.intellij.psi;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents an {@code exports} statement of a Java module declaration.
|
||||
*
|
||||
* @since 2016.3
|
||||
*/
|
||||
public interface PsiExportsStatement extends PsiElement {
|
||||
@Nullable PsiJavaCodeReferenceElement getPackageReference();
|
||||
}
|
||||
@@ -18,15 +18,24 @@ package com.intellij.psi.impl.source;
|
||||
import com.intellij.psi.JavaElementVisitor;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.PsiExportsStatement;
|
||||
import com.intellij.psi.PsiJavaCodeReferenceElement;
|
||||
import com.intellij.psi.impl.source.tree.CompositePsiElement;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PsiExportsStatementImpl extends CompositePsiElement implements PsiExportsStatement {
|
||||
public PsiExportsStatementImpl() {
|
||||
super(JavaElementType.EXPORTS_STATEMENT);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiJavaCodeReferenceElement getPackageReference() {
|
||||
return PsiTreeUtil.getChildOfType(this, PsiJavaCodeReferenceElement.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(@NotNull PsiElementVisitor visitor) {
|
||||
if (visitor instanceof JavaElementVisitor) {
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
|
||||
return CLASS_OR_PACKAGE_NAME_KIND; // incomplete code
|
||||
}
|
||||
}
|
||||
if (i == JavaElementType.PACKAGE_STATEMENT) {
|
||||
if (i == JavaElementType.PACKAGE_STATEMENT || i == JavaElementType.EXPORTS_STATEMENT) {
|
||||
return PACKAGE_NAME_KIND;
|
||||
}
|
||||
if (i == JavaElementType.IMPORT_STATEMENT) {
|
||||
|
||||
@@ -398,6 +398,10 @@ module.file.wrong.location=Module declaration should be located in a module's so
|
||||
module.open.duplicate.text=Go to duplicate
|
||||
module.not.found=Module not found: {0}
|
||||
module.cyclic.dependence=Cyclic dependence: {0}
|
||||
package.not.found=Package not found: {0}
|
||||
package.is.empty=Package is empty: {0}
|
||||
module.duplicate.export=Duplicate export: {0}
|
||||
module.self.export=Exports to itself
|
||||
|
||||
feature.generics=Generics
|
||||
feature.annotations=Annotations
|
||||
|
||||
@@ -59,6 +59,28 @@ class ModuleHighlightingTest : LightCodeInsightFixtureTestCase() {
|
||||
}""".trimIndent(), true)
|
||||
}
|
||||
|
||||
fun testExports() {
|
||||
addFile("pkg/empty/package-info.java", "package pkg.empty;")
|
||||
addFile("pkg/main/C.java", "package pkg.main;\nclass C { }")
|
||||
addFile("pkg/other/C.groovy", "package pkg.other\nclass C { }")
|
||||
doTest("""
|
||||
module M {
|
||||
exports pkg.<error descr="Cannot resolve symbol 'missing'">missing</error>;
|
||||
exports <error descr="Package is empty: pkg.empty">pkg.empty</error>;
|
||||
exports pkg.main to <error descr="Module not found: M.missing">M.missing</error>, M2, <error descr="Duplicate export: M2">M2</error>;
|
||||
exports pkg.other to <warning descr="Exports to itself">M</warning>;
|
||||
}""".trimIndent())
|
||||
}
|
||||
|
||||
fun testDuplicateExports() {
|
||||
addFile("pkg/main/C.java", "package pkg.main;\nclass C { }")
|
||||
doTest("""
|
||||
module M {
|
||||
exports pkg.main;
|
||||
<error descr="Duplicate export: pkg.main">exports pkg. main;</error>
|
||||
}""".trimIndent())
|
||||
}
|
||||
|
||||
//<editor-fold desc="Helpers.">
|
||||
private fun addFile(path: String, text: String) = VfsTestUtil.createFile(LightPlatformTestCase.getSourceRoot(), path, text)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user