UnstableTypeUsedInSignatureInspection: do not report if declaration is not accessible (private), or in the same package

GitOrigin-RevId: ac1182eefc5894b9893481015d3b3355785b37a0
This commit is contained in:
Sergey Patrikeev
2019-06-24 15:57:49 +03:00
committed by intellij-monorepo-bot
parent 7d53c40603
commit c9eae61072
7 changed files with 122 additions and 19 deletions

View File

@@ -4,10 +4,9 @@ package com.intellij.codeInspection
import com.intellij.analysis.JvmAnalysisBundle
import com.intellij.codeInspection.UnstableApiUsageInspection.Companion.DEFAULT_UNSTABLE_API_ANNOTATIONS
import com.intellij.codeInspection.util.SpecialAnnotationsUtil
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiType
import com.intellij.psi.PsiTypeParameterListOwner
import com.intellij.psi.PsiWildcardType
import com.intellij.lang.jvm.JvmModifier
import com.intellij.psi.*
import com.intellij.psi.util.PropertyUtil
import com.intellij.uast.UastVisitorAdapter
import com.intellij.util.ui.FormBuilder
import com.siyeh.ig.ui.ExternalizableStringSet
@@ -37,7 +36,7 @@ class UnstableTypeUsedInSignatureInspection : LocalInspectionTool() {
) : AbstractUastNonRecursiveVisitor() {
override fun visitClass(node: UClass): Boolean {
if (isMarkedUnstable(node)) {
if (!isAccessibleDeclaration(node) || isMarkedUnstable(node)) {
return true
}
checkTypeParameters(node.javaPsi, node)
@@ -45,7 +44,7 @@ class UnstableTypeUsedInSignatureInspection : LocalInspectionTool() {
}
override fun visitMethod(node: UMethod): Boolean {
if (isMarkedUnstable(node)) {
if (!isAccessibleDeclaration(node) || isMarkedUnstable(node)) {
return true
}
for (uastParameter in node.uastParameters) {
@@ -61,7 +60,7 @@ class UnstableTypeUsedInSignatureInspection : LocalInspectionTool() {
}
override fun visitField(node: UField): Boolean {
if (isMarkedUnstable(node)) {
if (!isAccessibleDeclaration(node) || isMarkedUnstable(node)) {
return true
}
checkReferencesUnstableType(node.type, node)
@@ -80,12 +79,46 @@ class UnstableTypeUsedInSignatureInspection : LocalInspectionTool() {
return false
}
private tailrec fun isMarkedUnstable(node: UDeclaration): Boolean {
if (node.annotations.any { it.qualifiedName in unstableApiAnnotations }) {
private fun isAccessibleDeclaration(node: UDeclaration): Boolean {
if (node.visibility == UastVisibility.PRIVATE) {
if (node is UField) {
//Kotlin properties are UField with accompanying getters\setters.
val psiField = node.javaPsi
if (psiField is PsiField) {
val getter = PropertyUtil.findGetterForField(psiField)
val setter = PropertyUtil.findSetterForField(psiField)
return getter != null && !getter.hasModifier(JvmModifier.PRIVATE) || setter != null && !setter.hasModifier(JvmModifier.PRIVATE)
}
}
return false
}
val containingUClass = node.getContainingUClass()
if (containingUClass != null) {
return isAccessibleDeclaration(containingUClass)
}
return true
}
val containingClass = node.getContainingUClass() ?: return false
return isMarkedUnstable(containingClass)
private fun isMarkedUnstable(node: UDeclaration) = findUnstableAnnotation(node) != null
private fun findUnstableAnnotation(node: UDeclaration): String? {
val unstableAnnotation = node.annotations.find { it.qualifiedName in unstableApiAnnotations }
if (unstableAnnotation != null) {
return unstableAnnotation.qualifiedName
}
val containingClass = node.getContainingUClass()
if (containingClass != null) {
return findUnstableAnnotation(containingClass)
}
val containingUFile = node.getContainingUFile()
if (containingUFile != null) {
val packageName = containingUFile.packageName
val psiPackage = JavaPsiFacade.getInstance(problemsHolder.project).findPackage(packageName)
if (psiPackage != null) {
return unstableApiAnnotations.find { psiPackage.hasAnnotation(it) }
}
}
return null
}
private fun checkReferencesUnstableType(psiType: PsiType, declaration: UDeclaration): Boolean {
@@ -103,11 +136,11 @@ class UnstableTypeUsedInSignatureInspection : LocalInspectionTool() {
//Returns <class name> and <unstable annotation name>
private fun findReferencedUnstableType(psiType: PsiType): Pair<String, String>? {
if (psiType is PsiClassType) {
val psiClass = psiType.resolve()
if (psiClass != null) {
val unstableApiAnnotation = unstableApiAnnotations.find { psiClass.hasAnnotation(it) }
val uClass = psiType.resolve()?.toUElement(UClass::class.java)
if (uClass != null) {
val unstableApiAnnotation = findUnstableAnnotation(uClass)
if (unstableApiAnnotation != null) {
val className = psiClass.qualifiedName ?: psiType.className
val className = uClass.qualifiedName ?: psiType.className
return className to unstableApiAnnotation
}
}

View File

@@ -0,0 +1,6 @@
package experimentalPackage;
import org.jetbrains.annotations.ApiStatus;
public class ClassInExperimentalPackage {
}

View File

@@ -0,0 +1,23 @@
package experimentalPackage;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Experimental
class ExperimentalClass { }
// No warnings because the 'experimentalPackage', to which this class belongs, is also experimental.
class NoWarnings {
public ExperimentalClass field;
public void methodWithParam(ExperimentalClass param) {}
public ExperimentalClass methodWithReturnType() { return null; }
public ClassInExperimentalPackage fieldWithSamePackageType;
public void methodWithSamePackageParam(ClassInExperimentalPackage param) {}
public ClassInExperimentalPackage methodWithSamePackageReturnType() { return null; }
}

View File

@@ -0,0 +1,2 @@
@org.jetbrains.annotations.ApiStatus.Experimental
package experimentalPackage;

View File

@@ -2,7 +2,7 @@ package test;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import experimentalPackage.ClassInExperimentalPackage;
@ApiStatus.Experimental
class ExperimentalClass { }
@@ -45,6 +45,12 @@ class Warnings {
public void <warning descr="Method must be marked with '@org.jetbrains.annotations.ApiStatus.Experimental' annotation because its signature references unstable type 'test.ExperimentalClass'">methodWithUnstableTypeParameterSuperWildcard</warning>(List<? super ExperimentalClass> list) {
}
public ClassInExperimentalPackage <warning descr="Field must be marked with '@org.jetbrains.annotations.ApiStatus.Experimental' annotation because its type references unstable type 'experimentalPackage.ClassInExperimentalPackage'">fieldWithTypeFromExperimentalPackage</warning>;
public void <warning descr="Method must be marked with '@org.jetbrains.annotations.ApiStatus.Experimental' annotation because its signature references unstable type 'experimentalPackage.ClassInExperimentalPackage'">methodWithParamTypeFromExperimentalPackage</warning>(ClassInExperimentalPackage param) { }
public ClassInExperimentalPackage <warning descr="Method must be marked with '@org.jetbrains.annotations.ApiStatus.Experimental' annotation because its signature references unstable type 'experimentalPackage.ClassInExperimentalPackage'">methodWithReturnTypeFromExperimentalPackage</warning>() { return null; }
}
class <warning descr="Class must be marked with '@org.jetbrains.annotations.ApiStatus.Experimental' annotation because its declaration references unstable type 'test.ExperimentalClass'">WarningTypeParameter</warning><T extends ExperimentalClass> {
@@ -65,7 +71,7 @@ class NoWarningsClassLevel {
}
}
// No warnings should be produced because methods and fields are already marked with @ApiStatus.Experimental annotation.
// No warnings should be produced because methods and fields are already marked with @ApiStatus.Experimental annotation or are inaccessible.
class NoWarnings {
@@ -80,4 +86,11 @@ class NoWarnings {
public ExperimentalClass methodWithExperimentalReturnType() {
return null;
}
private ExperimentalClass privateField;
private void privateMethodWithParam(ExperimentalClass param) {
}
private ExperimentalClass privateMethodWithReturnType() { return null; }
}

View File

@@ -3,6 +3,7 @@
package test
import org.jetbrains.annotations.ApiStatus
import experimentalPackage.ClassInExperimentalPackage
@ApiStatus.Experimental
open class ExperimentalClass
@@ -40,6 +41,12 @@ class Warnings {
fun <warning descr="Method must be marked with '@org.jetbrains.annotations.ApiStatus.Experimental' annotation because its signature references unstable type 'test.ExperimentalClass'">methodWithUnstableTypeParameterExtendsWildcard</warning>(list: List<ExperimentalClass>) {}
fun <warning descr="Method must be marked with '@org.jetbrains.annotations.ApiStatus.Experimental' annotation because its signature references unstable type 'test.ExperimentalClass'">methodWithUnstableTypeParameterSuperWildcard</warning>(list: List<ExperimentalClass>) {}
var <warning descr="Field must be marked with '@org.jetbrains.annotations.ApiStatus.Experimental' annotation because its type references unstable type 'experimentalPackage.ClassInExperimentalPackage'">fieldWithTypeFromExperimentalPackage</warning>: ClassInExperimentalPackage? = null
fun <warning descr="Method must be marked with '@org.jetbrains.annotations.ApiStatus.Experimental' annotation because its signature references unstable type 'experimentalPackage.ClassInExperimentalPackage'">methodWithParamTypeFromExperimentalPackage</warning>(param: ClassInExperimentalPackage) {}
fun <warning descr="Method must be marked with '@org.jetbrains.annotations.ApiStatus.Experimental' annotation because its signature references unstable type 'experimentalPackage.ClassInExperimentalPackage'">methodWithReturnTypeFromExperimentalPackage</warning>(): ClassInExperimentalPackage? = null
}
class <warning descr="Class must be marked with '@org.jetbrains.annotations.ApiStatus.Experimental' annotation because its declaration references unstable type 'test.ExperimentalClass'">WarningTypeParameter</warning><T : ExperimentalClass>
@@ -58,7 +65,7 @@ class NoWarningsClassLevel {
}
}
// No warnings should be produced because methods and fields are already marked with @ApiStatus.Experimental annotation.
// No warnings should be produced because methods and fields are already marked with @ApiStatus.Experimental annotation or are inaccessible.
class NoWarnings {
@@ -73,4 +80,10 @@ class NoWarnings {
fun methodWithExperimentalReturnType(): ExperimentalClass? {
return null
}
private val privateField: ExperimentalClass? = null
private fun privateMethodWithParam(param: ExperimentalClass) {}
private fun privateMethodWithReturnType(): ExperimentalClass? = null
}

View File

@@ -18,6 +18,15 @@ class UnstableTypeUsedInSignatureTest : JavaCodeInsightFixtureTestCase() {
inspection.unstableApiAnnotations.clear()
inspection.unstableApiAnnotations.add(ApiStatus.Experimental::class.java.canonicalName)
myFixture.enableInspections(inspection)
configureAnnotatedFiles()
}
private fun configureAnnotatedFiles() {
listOf(
"experimentalPackage/ClassInExperimentalPackage.java",
"experimentalPackage/package-info.java",
"experimentalPackage/NoWarnings.java"
).forEach { myFixture.copyFileToProject(it) }
}
override fun tuneFixture(moduleBuilder: JavaModuleFixtureBuilder<*>) {
@@ -32,4 +41,8 @@ class UnstableTypeUsedInSignatureTest : JavaCodeInsightFixtureTestCase() {
myFixture.testHighlighting("unstableTypeUsedInSignature.java")
}
fun `test no warnings produced in experimental package`() {
myFixture.testHighlighting("experimentalPackage/NoWarnings.java")
}
}