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
@@ -0,0 +1,6 @@
<html>
<body>
This inspection reports <b>public</b> and <b>protected</b> functions and properties that have an implicit return type.
For API stability reasons, it's recommended to specify such types explicitly.
</body>
</html>
+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"
}
@@ -0,0 +1,34 @@
<problems>
<problem>
<file>test.kt</file>
<line>4</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Public API declaration has implicit return type</problem_class>
<description>For API stability, it's recommended to specify explicitly public &amp; protected declaration types</description>
</problem>
<problem>
<file>test.kt</file>
<line>14</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Public API declaration has implicit return type</problem_class>
<description>For API stability, it's recommended to specify explicitly public &amp; protected declaration types</description>
</problem>
<problem>
<file>test.kt</file>
<line>17</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Public API declaration has implicit return type</problem_class>
<description>For API stability, it's recommended to specify explicitly public &amp; protected declaration types</description>
</problem>
<problem>
<file>test.kt</file>
<line>20</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Public API declaration has implicit return type</problem_class>
<description>For API stability, it's recommended to specify explicitly public &amp; protected declaration types</description>
</problem>
</problems>
@@ -0,0 +1 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.PublicApiImplicitTypeInspection
+29
View File
@@ -0,0 +1,29 @@
fun foo(): Int = 42
// !
fun bar() = 42
fun baz() {
fun bar() = 42
}
class My {
fun foo(): Int = 42
// !
fun bar() = 42
// !
protected val x = ""
// !
val y get() = true
private val z = 0
}
private class Your {
fun bar() = 42
val x = ""
}
@@ -276,6 +276,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
doTest(fileName);
}
@TestMetadata("publicApiImplicitType/inspectionData/inspections.test")
public void testPublicApiImplicitType_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/publicApiImplicitType/inspectionData/inspections.test");
doTest(fileName);
}
@TestMetadata("recursivePropertyAccessor/inspectionData/inspections.test")
public void testRecursivePropertyAccessor_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/recursivePropertyAccessor/inspectionData/inspections.test");