mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: Handle generic parameters in the inspection "Non-accessible type is exposed" (IDEA-162768)
This commit is contained in:
+66
-18
@@ -71,7 +71,9 @@ public class Java9NonAccessibleTypeExposedInspection extends BaseJavaLocalInspec
|
||||
private final ModuleFileIndex myModuleFileIndex;
|
||||
private final Set<String> myExportedPackageNames;
|
||||
|
||||
public NonAccessibleTypeExposedVisitor(ProblemsHolder holder, Module module, Set<String> exportedPackageNames) {
|
||||
public NonAccessibleTypeExposedVisitor(@NotNull ProblemsHolder holder,
|
||||
@NotNull Module module,
|
||||
@NotNull Set<String> exportedPackageNames) {
|
||||
myHolder = holder;
|
||||
myModuleFileIndex = ModuleRootManager.getInstance(module).getFileIndex();
|
||||
myExportedPackageNames = exportedPackageNames;
|
||||
@@ -84,6 +86,7 @@ public class Java9NonAccessibleTypeExposedInspection extends BaseJavaLocalInspec
|
||||
if (!method.isConstructor()) {
|
||||
checkType(method.getReturnType(), method.getReturnTypeElement());
|
||||
}
|
||||
checkTypeParameters(method.getTypeParameterList());
|
||||
for (PsiParameter parameter : method.getParameterList().getParameters()) {
|
||||
checkType(parameter.getType(), parameter.getTypeElement());
|
||||
}
|
||||
@@ -128,22 +131,68 @@ public class Java9NonAccessibleTypeExposedInspection extends BaseJavaLocalInspec
|
||||
}
|
||||
}
|
||||
|
||||
private void checkType(@Nullable PsiType type, @Nullable PsiTypeElement typeElement) {
|
||||
if (typeElement != null) {
|
||||
PsiClass psiClass = PsiUtil.resolveClassInType(type);
|
||||
checkType(psiClass, typeElement);
|
||||
@Override
|
||||
public void visitClass(PsiClass aClass) {
|
||||
super.visitClass(aClass);
|
||||
if (isModulePublicApi(aClass)) {
|
||||
checkTypeParameters(aClass.getTypeParameterList());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkType(PsiClass psiClass, @NotNull PsiElement typeElement) {
|
||||
private void checkType(@Nullable PsiType type, @Nullable PsiTypeElement typeElement) {
|
||||
if (typeElement != null) {
|
||||
if (type instanceof PsiWildcardType) {
|
||||
type = ((PsiWildcardType)type).getBound();
|
||||
PsiElement lastChild = typeElement.getLastChild();
|
||||
if (lastChild instanceof PsiTypeElement) {
|
||||
typeElement = (PsiTypeElement)lastChild;
|
||||
}
|
||||
}
|
||||
PsiClass psiClass = PsiUtil.resolveClassInType(type);
|
||||
checkType(psiClass, typeElement);
|
||||
if (type instanceof PsiClassType && !(psiClass instanceof PsiTypeParameter)) {
|
||||
PsiJavaCodeReferenceElement referenceElement = typeElement.getInnermostComponentReferenceElement();
|
||||
if (referenceElement != null) {
|
||||
checkTypeParameters(referenceElement.getParameterList());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkType(@Nullable PsiClass psiClass, @NotNull PsiElement typeElement) {
|
||||
if (psiClass != null && !(psiClass instanceof PsiTypeParameter) && isInModuleSource(psiClass) && !isModulePublicApi(psiClass)) {
|
||||
myHolder.registerProblem(typeElement, CLASS_IS_NOT_EXPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkTypeParameters(@Nullable PsiReferenceParameterList parameterList) {
|
||||
if (parameterList != null) {
|
||||
PsiTypeElement[] typeParameterElements = parameterList.getTypeParameterElements();
|
||||
for (PsiTypeElement typeParameterElement : typeParameterElements) {
|
||||
checkType(typeParameterElement.getType(), typeParameterElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkTypeParameters(@Nullable PsiTypeParameterList parameterList) {
|
||||
if (parameterList != null) {
|
||||
for (PsiTypeParameter typeParameter : parameterList.getTypeParameters()) {
|
||||
for (PsiJavaCodeReferenceElement referenceElement : typeParameter.getExtendsList().getReferenceElements()) {
|
||||
PsiElement resolved = referenceElement.resolve();
|
||||
if (resolved instanceof PsiClass) {
|
||||
checkType((PsiClass)resolved, referenceElement);
|
||||
}
|
||||
checkTypeParameters(referenceElement.getParameterList());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
private boolean isModulePublicApi(@Nullable PsiMember member) {
|
||||
if (member != null && (member.hasModifierProperty(PsiModifier.PUBLIC) || member.hasModifierProperty(PsiModifier.PROTECTED))) {
|
||||
if (member != null &&
|
||||
!(member instanceof PsiTypeParameter) &&
|
||||
(member.hasModifierProperty(PsiModifier.PUBLIC) || member.hasModifierProperty(PsiModifier.PROTECTED))) {
|
||||
PsiElement parent = member.getParent();
|
||||
if (parent instanceof PsiClass) {
|
||||
return isModulePublicApi((PsiClass)parent);
|
||||
@@ -160,27 +209,26 @@ public class Java9NonAccessibleTypeExposedInspection extends BaseJavaLocalInspec
|
||||
private boolean isModulePublicApi(@Nullable PsiAnnotationOwner owner) {
|
||||
if (owner instanceof PsiModifierList) {
|
||||
PsiElement parent = ((PsiModifierList)owner).getParent();
|
||||
if (parent instanceof PsiMember) {
|
||||
if (parent instanceof PsiMember) { // class or field or method
|
||||
return isModulePublicApi((PsiMember)parent);
|
||||
}
|
||||
if (parent instanceof PsiParameter) {
|
||||
if (parent instanceof PsiParameter) { // method parameter
|
||||
PsiElement declarationScope = ((PsiParameter)parent).getDeclarationScope();
|
||||
if (declarationScope instanceof PsiMethod) {
|
||||
return isModulePublicApi((PsiMethod)declarationScope);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (owner instanceof PsiTypeElement) {
|
||||
PsiElement parent = ((PsiTypeElement)owner).getParent();
|
||||
if (parent instanceof PsiMember) {
|
||||
return isModulePublicApi((PsiMember)parent);
|
||||
}
|
||||
if (parent instanceof PsiReferenceParameterList) {
|
||||
PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class);
|
||||
return isModulePublicApi(method);
|
||||
else if (owner instanceof PsiTypeElement) { // type argument (aka type_use)
|
||||
PsiElement grandParent = PsiTreeUtil.skipParentsOfType(((PsiTypeElement)owner),
|
||||
PsiParameter.class, PsiParameterList.class, PsiTypeElement.class,
|
||||
PsiReferenceList.class, PsiReferenceParameterList.class,
|
||||
PsiJavaCodeReferenceElement.class);
|
||||
if (grandParent instanceof PsiMember) {
|
||||
return isModulePublicApi((PsiMember)grandParent);
|
||||
}
|
||||
}
|
||||
else if (owner instanceof PsiTypeParameter) {
|
||||
else if (owner instanceof PsiTypeParameter) { // type parameter declaration
|
||||
PsiElement parent = ((PsiTypeParameter)owner).getParent();
|
||||
if (parent instanceof PsiTypeParameterList) {
|
||||
PsiElement grandParent = parent.getParent();
|
||||
|
||||
+74
-6
@@ -31,6 +31,7 @@ class Java9NonAccessibleTypeExposedTest : LightJava9ModulesCodeInsightFixtureTes
|
||||
super.setUp()
|
||||
myFixture.enableInspections(Java9NonAccessibleTypeExposedInspection())
|
||||
addFile("module-info.java", "module MAIN { exports apiPkg; exports otherPkg; requires M2; }", MAIN)
|
||||
addFile("module-info.java", "module M2 { exports m2Pkg; }", M2)
|
||||
add("apiPkg", "PublicApi", "public class PublicApi {}")
|
||||
add("apiPkg", "PackageLocal", "class PackageLocal {}")
|
||||
add("otherPkg", "PublicOther", "public class PublicOther {}")
|
||||
@@ -40,13 +41,18 @@ class Java9NonAccessibleTypeExposedTest : LightJava9ModulesCodeInsightFixtureTes
|
||||
highlight("""package apiPkg;
|
||||
public class Highlighted {
|
||||
public int i;
|
||||
protected int getInt() {return 1;}
|
||||
public int getInt() {return i;}
|
||||
public void setInt(int n) {i=n;}
|
||||
|
||||
protected Long l;
|
||||
protected Long getLong() {return l;}
|
||||
protected void setLong(Long n) {l=n;}
|
||||
|
||||
public void run() {}
|
||||
}""")
|
||||
}
|
||||
|
||||
fun testImported() {
|
||||
addFile("module-info.java", "module M2 { exports m2Pkg; }", M2)
|
||||
fun testExported() {
|
||||
addFile("m2Pkg/Exported.java", "package m2Pkg; public class Exported {}", M2)
|
||||
|
||||
highlight("""package apiPkg;
|
||||
@@ -271,17 +277,79 @@ public class Highlighted {
|
||||
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @interface PackageLocalAnnotation {}
|
||||
|
||||
public class C1<@PublicAnnotation T> {
|
||||
public List<@PublicAnnotation String> text;
|
||||
public void foo(Set<@PublicAnnotation String> s) {}
|
||||
protected <@PublicAnnotation X> void bar(X x) {}
|
||||
protected List<@PublicAnnotation T> baz() { return new ArrayList<@PublicAnnotation T>();}
|
||||
protected Set<@PublicAnnotation T> baz() {return new HashSet<@PublicAnnotation T>();}
|
||||
public List<@PublicAnnotation String> text() {return new ArrayList<@PublicAnnotation String>();}
|
||||
}
|
||||
public class C2<@<warning descr="The class is not exported from the module">PackageLocalAnnotation</warning> T> {
|
||||
public List<@<warning descr="The class is not exported from the module">PackageLocalAnnotation</warning> String> text;
|
||||
public void foo(Set<@<warning descr="The class is not exported from the module">PackageLocalAnnotation</warning> String> s) {}
|
||||
protected <@<warning descr="The class is not exported from the module">PackageLocalAnnotation</warning> X> void bar(X x) {}
|
||||
protected List<@<warning descr="The class is not exported from the module">PackageLocalAnnotation</warning> T> baz() {
|
||||
return new ArrayList<@<warning descr="The class is not exported from the module">PackageLocalAnnotation</warning> T>();
|
||||
protected Set<@<warning descr="The class is not exported from the module">PackageLocalAnnotation</warning> T> baz() {
|
||||
return new HashSet<@PackageLocalAnnotation T>();
|
||||
}
|
||||
public List<@<warning descr="The class is not exported from the module">PackageLocalAnnotation</warning> String> text() {
|
||||
return new ArrayList<@PackageLocalAnnotation String>();
|
||||
}
|
||||
}
|
||||
public interface I1 extends List<@PublicAnnotation Highlighted> {}
|
||||
public interface I2 extends List<@<warning descr="The class is not exported from the module">PackageLocalAnnotation</warning> Highlighted> {}
|
||||
}
|
||||
""")
|
||||
}
|
||||
|
||||
fun testGenericPublic() {
|
||||
add("apiPkg", "MyInterface", "public interface MyInterface {}")
|
||||
add("apiPkg", "MyClass", "public class MyClass implements MyInterface {}")
|
||||
highlight("""package apiPkg;
|
||||
import java.util.*;
|
||||
public class Highlighted<T extends MyInterface> {
|
||||
protected Set<T> get1() { return new HashSet<>();}
|
||||
public Set<MyClass> get2() { return new HashSet<MyClass>();}
|
||||
protected Set<? extends MyClass> get3() { return new HashSet<>();}
|
||||
public <X extends Object&MyInterface> Set<X> get4() { return new HashSet<>();}
|
||||
public Map<String, Set<MyInterface>> get5() {return new HashMap<>();}
|
||||
public void copy1(Set<MyInterface> s) {}
|
||||
public void copy2(Set<? super MyClass> s) {}
|
||||
|
||||
public static class Nested1<T extends MyClass&Iterable<MyInterface>> {
|
||||
public Iterator<MyInterface> iterator() {return null;}
|
||||
}
|
||||
public static class Nested2<T extends MyInterface&AutoCloseable> {
|
||||
public void close(){}
|
||||
}
|
||||
public interface Nested3<X extends Iterable<MyInterface>> {}
|
||||
public interface Nested4<X extends Iterable<? extends MyInterface>> {}
|
||||
}
|
||||
""")
|
||||
}
|
||||
|
||||
fun testGenericNotExported() {
|
||||
add("implPkg", "MyInterface", "public interface MyInterface {}")
|
||||
add("implPkg", "MyClass", "public class MyClass implements MyInterface {}")
|
||||
highlight("""package apiPkg;
|
||||
import java.util.*;
|
||||
import implPkg.*;
|
||||
public class Highlighted<T extends <warning descr="The class is not exported from the module">MyInterface</warning>> {
|
||||
protected Set<T> get1() { return new HashSet<>();}
|
||||
public Set<<warning descr="The class is not exported from the module">MyClass</warning>> get2() { return new HashSet<MyClass>();}
|
||||
protected Set<? extends <warning descr="The class is not exported from the module">MyClass</warning>> get3() { return new HashSet<>();}
|
||||
public <X extends Object&<warning descr="The class is not exported from the module">MyInterface</warning>> Set<X> get4() { return new HashSet<>();}
|
||||
public Map<String, Set<<warning descr="The class is not exported from the module">MyInterface</warning>>> get5() {return new HashMap<>();}
|
||||
public void copy1(Set<<warning descr="The class is not exported from the module">MyInterface</warning>> s) {}
|
||||
public void copy2(Set<? super <warning descr="The class is not exported from the module">MyClass</warning>> s) {}
|
||||
|
||||
public static class Nested1<T extends <warning descr="The class is not exported from the module">MyClass</warning>&
|
||||
Iterable<<warning descr="The class is not exported from the module">MyInterface</warning>>> {
|
||||
public Iterator<<warning descr="The class is not exported from the module">MyInterface</warning>> iterator() {return null;}
|
||||
}
|
||||
public static class Nested2<T extends <warning descr="The class is not exported from the module">MyInterface</warning>&AutoCloseable> {
|
||||
public void close() {}
|
||||
}
|
||||
public interface Nested3<X extends Iterable<<warning descr="The class is not exported from the module">MyInterface</warning>>> {}
|
||||
public interface Nested4<X extends Iterable<? extends <warning descr="The class is not exported from the module">MyInterface</warning>>> {}
|
||||
}
|
||||
""")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user