Add inspection to detect public API with implicit type

This commit is contained in:
Mikhail Glukhikh
2018-03-28 16:15:12 +02:00
parent 6a619d0aa5
commit 34de241154
9 changed files with 153 additions and 14 deletions
+9
View File
@@ -2761,6 +2761,15 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.PublicApiImplicitTypeInspection"
displayName="Public API declaration has implicit return type"
groupPath="Kotlin"
groupName="Other problems"
enabledByDefault="false"
level="WEAK WARNING"
language="kotlin"
/>
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention
import org.jetbrains.kotlin.psi.KtCallableDeclaration
abstract class AbstractImplicitTypeInspection(
additionalChecker: (KtCallableDeclaration, AbstractImplicitTypeInspection) -> Boolean
) : IntentionBasedInspection<KtCallableDeclaration>(
SpecifyTypeExplicitlyIntention::class,
{ element, inspection ->
with(inspection as AbstractImplicitTypeInspection) {
element.typeReference == null && additionalChecker(element, inspection)
}
}
) {
override fun inspectionTarget(element: KtCallableDeclaration) = element.nameIdentifier
}
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.IntentionWrapper
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel
import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention
import org.jetbrains.kotlin.idea.intentions.isFlexibleRecursive
@@ -30,27 +29,33 @@ import org.jetbrains.kotlin.types.TypeUtils
import javax.swing.JComponent
class HasPlatformTypeInspection(
@JvmField var publicAPIOnly: Boolean = true,
@JvmField var reportPlatformArguments: Boolean = false
) : IntentionBasedInspection<KtCallableDeclaration>(
SpecifyTypeExplicitlyIntention::class,
{ element, inspection ->
with(inspection as HasPlatformTypeInspection) {
SpecifyTypeExplicitlyIntention.dangerousFlexibleTypeOrNull(element, this.publicAPIOnly, this.reportPlatformArguments) != null
}
@JvmField var publicAPIOnly: Boolean = true,
@JvmField var reportPlatformArguments: Boolean = false
) : AbstractImplicitTypeInspection(
{ element, inspection ->
with(inspection as HasPlatformTypeInspection) {
SpecifyTypeExplicitlyIntention.dangerousFlexibleTypeOrNull(
element,
this.publicAPIOnly,
this.reportPlatformArguments
) != null
}
}
) {
override val problemText = "Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. " +
"Specify type explicitly as nullable or non-nullable."
"Specify type explicitly as nullable or non-nullable."
override fun additionalFixes(element: KtCallableDeclaration): List<LocalQuickFix>? {
val type = SpecifyTypeExplicitlyIntention.dangerousFlexibleTypeOrNull(element, publicAPIOnly, reportPlatformArguments) ?: return null
val type = SpecifyTypeExplicitlyIntention.dangerousFlexibleTypeOrNull(
element, publicAPIOnly, reportPlatformArguments
) ?: return null
if (TypeUtils.isNullableType(type)) {
val expression = element.node.findChildByType(KtTokens.EQ)?.psi?.getNextSiblingIgnoringWhitespaceAndComments()
if (expression != null &&
(!reportPlatformArguments || !TypeUtils.makeNotNullable(type).isFlexibleRecursive())) {
(!reportPlatformArguments || !TypeUtils.makeNotNullable(type).isFlexibleRecursive())
) {
return listOf(IntentionWrapper(AddExclExclCallFix(expression), element.containingFile))
}
}
@@ -58,8 +63,6 @@ class HasPlatformTypeInspection(
return null
}
override fun inspectionTarget(element: KtCallableDeclaration) = element.nameIdentifier
override fun createOptionsPanel(): JComponent? {
val panel = MultipleCheckboxOptionsPanel(this)
panel.addCheckbox("Apply only to public or protected members", "publicAPIOnly")
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.effectiveVisibility
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
class PublicApiImplicitTypeInspection : AbstractImplicitTypeInspection(
{ element, _ ->
element.containingClassOrObject?.isLocal != true &&
when (element) {
is KtFunction -> !element.isLocal
is KtProperty -> !element.isLocal
else -> false
} && run {
val callableMemberDescriptor = element.resolveToDescriptorIfAny() as? CallableMemberDescriptor
callableMemberDescriptor?.effectiveVisibility()?.toVisibility()?.isPublicAPI == true
}
}
) {
override val problemText = "For API stability, it's recommended to specify explicitly public & protected declaration types"
}