Java: Improved messages and documentation for the inspection "Non-accessible class is exposed" (IDEA-166535)

This commit is contained in:
Pavel Dolgov
2017-02-08 17:19:01 +03:00
parent b88bca1cb8
commit 82abcad979
4 changed files with 54 additions and 42 deletions
@@ -73,13 +73,13 @@ public class Highlighted {
highlight("""package apiPkg;
public class Highlighted {
static { PackageLocal tmp = new PackageLocal(); System.out.println(tmp);}
public <warning descr="Class is not exported from the module">PackageLocal</warning> myVar;
public <warning descr="Class 'PackageLocal' is not exported from the module 'MAIN'">PackageLocal</warning> myVar;
protected Highlighted() {}
public Highlighted(<warning descr="Class is not exported from the module">PackageLocal</warning> var) {
public Highlighted(<warning descr="Class 'PackageLocal' is not exported from the module 'MAIN'">PackageLocal</warning> var) {
PackageLocal tmp = new PackageLocal(); myVar = var!= null ? var : tmp;
}
public <warning descr="Class is not exported from the module">PackageLocal</warning> getVar() {return myVar;}
protected void setVar(<warning descr="Class is not exported from the module">PackageLocal</warning> var) {myVar = var;}
public <warning descr="Class 'PackageLocal' is not exported from the module 'MAIN'">PackageLocal</warning> getVar() {return myVar;}
protected void setVar(<warning descr="Class 'PackageLocal' is not exported from the module 'MAIN'">PackageLocal</warning> var) {myVar = var;}
}
""")
}
@@ -174,13 +174,13 @@ public class Highlighted {
public class Highlighted {
class PackageLocalNested {}
{ PackageLocalNested tmp = new PackageLocalNested(); System.out.println(tmp);}
public <warning descr="Class is not exported from the module">PackageLocalNested</warning> myVar;
public <warning descr="Class 'PackageLocalNested' is not exported from the module 'MAIN'">PackageLocalNested</warning> myVar;
protected Highlighted() {}
public Highlighted(<warning descr="Class is not exported from the module">PackageLocalNested</warning> var) {
public Highlighted(<warning descr="Class 'PackageLocalNested' is not exported from the module 'MAIN'">PackageLocalNested</warning> var) {
PackageLocalNested tmp = new PackageLocalNested(); myVar = var!= null ? var : tmp;
}
public <warning descr="Class is not exported from the module">PackageLocalNested</warning> getVar() {return myVar;}
protected void setVar(<warning descr="Class is not exported from the module">PackageLocalNested</warning> var) {myVar = var;}
public <warning descr="Class 'PackageLocalNested' is not exported from the module 'MAIN'">PackageLocalNested</warning> getVar() {return myVar;}
protected void setVar(<warning descr="Class 'PackageLocalNested' is not exported from the module 'MAIN'">PackageLocalNested</warning> var) {myVar = var;}
}
""")
}
@@ -188,9 +188,9 @@ public class Highlighted {
fun testPackageLocalInInterface() {
highlight("""package apiPkg;
public interface Highlighted {
<warning descr="Class is not exported from the module">PackageLocal</warning> myVar = new PackageLocal();
<warning descr="Class is not exported from the module">PackageLocal</warning> getVar();
void setVar(<warning descr="Class is not exported from the module">PackageLocal</warning> var);
<warning descr="Class 'PackageLocal' is not exported from the module 'MAIN'">PackageLocal</warning> myVar = new PackageLocal();
<warning descr="Class 'PackageLocal' is not exported from the module 'MAIN'">PackageLocal</warning> getVar();
void setVar(<warning descr="Class 'PackageLocal' is not exported from the module 'MAIN'">PackageLocal</warning> var);
}
""")
}
@@ -210,11 +210,11 @@ public interface Highlighted {
highlight("""package apiPkg;
import implPkg.NotExported;
public class Highlighted {
public <warning descr="Class is not exported from the module">NotExported</warning> myVar;
public <warning descr="Class 'NotExported' is not exported from the module 'MAIN'">NotExported</warning> myVar;
protected Highlighted() {}
public Highlighted(<warning descr="Class is not exported from the module">NotExported</warning> var) {setVar(var);}
public <warning descr="Class is not exported from the module">NotExported</warning> getVar() {return myVar;}
protected void setVar(<warning descr="Class is not exported from the module">NotExported</warning> var) {myVar = var;}
public Highlighted(<warning descr="Class 'NotExported' is not exported from the module 'MAIN'">NotExported</warning> var) {setVar(var);}
public <warning descr="Class 'NotExported' is not exported from the module 'MAIN'">NotExported</warning> getVar() {return myVar;}
protected void setVar(<warning descr="Class 'NotExported' is not exported from the module 'MAIN'">NotExported</warning> var) {myVar = var;}
}
""")
}
@@ -229,8 +229,8 @@ public class Highlighted {
import apiPkg.PublicOuter.PackageLocal;
public class Highlighted {
private PackageLocal.DoubleNested myVar;
public <warning descr="Class is not exported from the module">PackageLocal.DoubleNested</warning> getVar() {return myVar;}
protected void setVar(<warning descr="Class is not exported from the module">PackageLocal.DoubleNested</warning> var) {myVar = var;}
public <warning descr="Class 'PackageLocal.DoubleNested' is not exported from the module 'MAIN'">PackageLocal.DoubleNested</warning> getVar() {return myVar;}
protected void setVar(<warning descr="Class 'PackageLocal.DoubleNested' is not exported from the module 'MAIN'">PackageLocal.DoubleNested</warning> var) {myVar = var;}
}
""")
}
@@ -255,11 +255,11 @@ public class Highlighted {
import implPkg.NotExported;
import java.util.*;
public class Highlighted {
public <warning descr="Class is not exported from the module">NotExported</warning>[] myVar;
protected Highlighted(List<<warning descr="Class is not exported from the module">NotExported</warning>[]> list) {Iterator<NotExported[]> it = list.iterator(); myVar = it.next();}
public Highlighted(<warning descr="Class is not exported from the module">NotExported</warning>[] var) {myVar = var;}
public <warning descr="Class is not exported from the module">NotExported</warning>[] getVar() {return myVar;}
protected void setVar(<warning descr="Class is not exported from the module">NotExported</warning>[][] var) {myVar = var[0];}
public <warning descr="Class 'NotExported' is not exported from the module 'MAIN'">NotExported</warning>[] myVar;
protected Highlighted(List<<warning descr="Class 'NotExported' is not exported from the module 'MAIN'">NotExported</warning>[]> list) {Iterator<NotExported[]> it = list.iterator(); myVar = it.next();}
public Highlighted(<warning descr="Class 'NotExported' is not exported from the module 'MAIN'">NotExported</warning>[] var) {myVar = var;}
public <warning descr="Class 'NotExported' is not exported from the module 'MAIN'">NotExported</warning>[] getVar() {return myVar;}
protected void setVar(<warning descr="Class 'NotExported' is not exported from the module 'MAIN'">NotExported</warning>[][] var) {myVar = var[0];}
}""")
}
@@ -273,9 +273,9 @@ import otherPkg.*;
import implPkg.*;
public class Highlighted {
public void throwsPublic() throws PublicException {}
public void throwsPackageLocal() throws <warning descr="Class is not exported from the module">PackageLocalException</warning> {}
public void throwsPackageLocal() throws <warning descr="Class 'PackageLocalException' is not exported from the module 'MAIN'">PackageLocalException</warning> {}
public void throwsOther() throws OtherException {}
public void throwsNotExported() throws <warning descr="Class is not exported from the module">NotExportedException</warning> {}
public void throwsNotExported() throws <warning descr="Class 'NotExportedException' is not exported from the module 'MAIN'">NotExportedException</warning> {}
}
""")
}
@@ -314,16 +314,16 @@ import java.util.*;
import implPkg.*;
public class Highlighted<T extends MyInterface> {
protected Set<T> get1() { return new HashSet<>();}
public Set<<warning descr="Class is not exported from the module">MyClass</warning>> get2() { return new HashSet<MyClass>();}
protected Set<? extends <warning descr="Class is not exported from the module">MyClass</warning>> get3() { return new HashSet<>();}
public <X extends Object&<warning descr="Class is not exported from the module">MyInterface</warning>> Set<X> get4() { return new HashSet<>();}
public Map<String, Set<<warning descr="Class is not exported from the module">MyInterface</warning>>> get5() {return new HashMap<>();}
public void copy1(Set<<warning descr="Class is not exported from the module">MyInterface</warning>> s) {}
public void copy2(Set<? super <warning descr="Class is not exported from the module">MyClass</warning>> s) {}
public Set<<warning descr="Class 'MyClass' is not exported from the module 'MAIN'">MyClass</warning>> get2() { return new HashSet<MyClass>();}
protected Set<? extends <warning descr="Class 'MyClass' is not exported from the module 'MAIN'">MyClass</warning>> get3() { return new HashSet<>();}
public <X extends Object&<warning descr="Class 'MyInterface' is not exported from the module 'MAIN'">MyInterface</warning>> Set<X> get4() { return new HashSet<>();}
public Map<String, Set<<warning descr="Class 'MyInterface' is not exported from the module 'MAIN'">MyInterface</warning>>> get5() {return new HashMap<>();}
public void copy1(Set<<warning descr="Class 'MyInterface' is not exported from the module 'MAIN'">MyInterface</warning>> s) {}
public void copy2(Set<? super <warning descr="Class 'MyClass' is not exported from the module 'MAIN'">MyClass</warning>> s) {}
public static class Nested1<T extends MyClass&
Iterable<MyInterface>> {
public Iterator<<warning descr="Class is not exported from the module">MyInterface</warning>> iterator() {return null;}
public Iterator<<warning descr="Class 'MyInterface' is not exported from the module 'MAIN'">MyInterface</warning>> iterator() {return null;}
}
public static class Nested2<T extends MyInterface&AutoCloseable> {
public void close() {}
@@ -338,9 +338,11 @@ use.stringtokenizer.problem.descriptor=<code>#ref</code> in an internationalized
time.tostring.call.display.name=Call to 'Time.toString()'
time.tostring.call.problem.descriptor=<code>Time.#ref()</code> in an internationalized context #loc
class.escapes.defined.scope.display.name=Non-accessible class is exposed
class.escapes.defined.scope.display.module.option=Module's API exposes not exported classes (Java 9+)
class.escapes.defined.scope.display.public.option=Public API exposes non-accessible classes
class.escapes.defined.scope.display.package.option=Package-local API exposes private classes
class.escapes.defined.scope.problem.descriptor=Class <code>#ref</code> is exposed outside its defined scope #loc
class.escapes.defined.scope.java9.modules.option=Check only the Java 9 module visibility rules
class.escapes.defined.scope.java9.modules.descriptor=Class is not exported from the module
class.escapes.defined.scope.java9.modules.descriptor=Class <code>#ref</code> is not exported from the module ''{0}''
field.name.hides.in.superclass.display.name=Field name hides field in superclass
field.name.hides.in.superclass.problem.descriptor=Field <code>#ref</code> hides field in superclass #loc
field.name.hides.in.superclass.ignore.option=Ignore superclass fields not visible from subclass
@@ -42,7 +42,7 @@ import java.util.Set;
public class ClassEscapesItsScopeInspection extends BaseJavaBatchLocalInspectionTool {
@SuppressWarnings("PublicField") public boolean checkModuleApi = true; // public & protected fields & methods within expoted packages
@SuppressWarnings("PublicField") public boolean checkModuleApi = true; // public & protected fields & methods within exported packages
@SuppressWarnings("PublicField") public boolean checkPublicApi; // All public & protected fields & methods
@SuppressWarnings("PublicField") public boolean checkPackageLocal;
@@ -63,9 +63,9 @@ public class ClassEscapesItsScopeInspection extends BaseJavaBatchLocalInspection
@Override
public JComponent createOptionsPanel() {
MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this);
panel.addCheckbox("Module's API exposes not exported classes (Java 9+)", "checkModuleApi");
panel.addCheckbox("Public API exposes non-accessible classes", "checkPublicApi");
panel.addCheckbox("Package-local API exposes private classes", "checkPackageLocal");
panel.addCheckbox(InspectionGadgetsBundle.message("class.escapes.defined.scope.display.module.option"), "checkModuleApi");
panel.addCheckbox(InspectionGadgetsBundle.message("class.escapes.defined.scope.display.public.option"), "checkPublicApi");
panel.addCheckbox(InspectionGadgetsBundle.message("class.escapes.defined.scope.display.package.option"), "checkPackageLocal");
return panel;
}
@@ -87,7 +87,7 @@ public class ClassEscapesItsScopeInspection extends BaseJavaBatchLocalInspection
Set<String> exportedPackageNames =
new THashSet<>(ContainerUtil.mapNotNull(psiModule.getExports(), PsiExportsStatement::getPackageName));
if (exportedPackageNames.contains(javaFile.getPackageName())) {
checkers.add(new Java9NonAccessibleTypeExposedVisitor(holder, module, exportedPackageNames));
checkers.add(new Java9NonAccessibleTypeExposedVisitor(holder, module, psiModule.getModuleName(), exportedPackageNames));
}
}
}
@@ -207,11 +207,14 @@ public class ClassEscapesItsScopeInspection extends BaseJavaBatchLocalInspection
private static class Java9NonAccessibleTypeExposedVisitor extends VisibilityChecker {
private final ModuleFileIndex myModuleFileIndex;
private final Set<String> myExportedPackageNames;
private final String myModuleName;
public Java9NonAccessibleTypeExposedVisitor(@NotNull ProblemsHolder holder,
@NotNull Module module,
@NotNull String moduleName,
@NotNull Set<String> exportedPackageNames) {
super(holder);
myModuleName = moduleName;
myModuleFileIndex = ModuleRootManager.getInstance(module).getFileIndex();
myExportedPackageNames = exportedPackageNames;
}
@@ -219,7 +222,8 @@ public class ClassEscapesItsScopeInspection extends BaseJavaBatchLocalInspection
@Override
public boolean checkVisibilityIssue(PsiMember member, PsiClass psiClass, PsiJavaCodeReferenceElement reference) {
if (isModulePublicApi(member) && !isModulePublicApi(psiClass) && isInModuleSource(psiClass)) {
myHolder.registerProblem(reference, InspectionGadgetsBundle.message("class.escapes.defined.scope.java9.modules.descriptor"));
myHolder.registerProblem(reference,
InspectionGadgetsBundle.message("class.escapes.defined.scope.java9.modules.descriptor", myModuleName));
return true;
}
return false;
@@ -1,7 +1,6 @@
<html>
<body>
Reports references to classes in field types and method signatures where a field or a method exposes a class
with more restricted visibility than the field or the method itself.
Reports usages of classes in field types and in method signatures where the class is less visible than the field or the method.
<p>
Examples are a <b>public</b> method which returns a <b>private</b> inner class, or a <b>protected</b> field
whose type is a package-visible class.<br>
@@ -10,6 +9,13 @@ with more restricted visibility than the field or the method itself.
In addition to that, in Java 9 a module may hide some of its classes by not exporting their packages.<br>
If the public API of a class in an exported package references a class from non-exported package,
such API isn't useful outside of the module.
<br>
<!-- tooltip end -->
<p>
The options of what is checked are:
<ul>
<li>The module API - the public&protected fields&methods within exported packages (for Java 9+)</li>
<li>The public&protected fields&methods (the "public API") in all packages</li>
<li>The package-local fields&methods</li>
</ul>
</body>
</html>