From 34de241154ee03e72698380e56b87307f1e6dd07 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 28 Mar 2018 16:15:12 +0200 Subject: [PATCH] Add inspection to detect public API with implicit type --- .../PublicApiImplicitType.html | 6 ++++ idea/src/META-INF/plugin.xml | 9 +++++ .../AbstractImplicitTypeInspection.kt | 22 ++++++++++++ .../inspections/HasPlatformTypeInspection.kt | 31 +++++++++-------- .../PublicApiImplicitTypeInspection.kt | 29 ++++++++++++++++ .../inspectionData/expected.xml | 34 +++++++++++++++++++ .../inspectionData/inspections.test | 1 + .../inspections/publicApiImplicitType/test.kt | 29 ++++++++++++++++ .../codeInsight/InspectionTestGenerated.java | 6 ++++ 9 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 idea/resources/inspectionDescriptions/PublicApiImplicitType.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/AbstractImplicitTypeInspection.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/PublicApiImplicitTypeInspection.kt create mode 100644 idea/testData/inspections/publicApiImplicitType/inspectionData/expected.xml create mode 100644 idea/testData/inspections/publicApiImplicitType/inspectionData/inspections.test create mode 100644 idea/testData/inspections/publicApiImplicitType/test.kt diff --git a/idea/resources/inspectionDescriptions/PublicApiImplicitType.html b/idea/resources/inspectionDescriptions/PublicApiImplicitType.html new file mode 100644 index 00000000000..a859adfe38b --- /dev/null +++ b/idea/resources/inspectionDescriptions/PublicApiImplicitType.html @@ -0,0 +1,6 @@ + + +This inspection reports public and protected functions and properties that have an implicit return type. +For API stability reasons, it's recommended to specify such types explicitly. + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index df2b1bc4531..8433620c001 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -2761,6 +2761,15 @@ language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/AbstractImplicitTypeInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/AbstractImplicitTypeInspection.kt new file mode 100644 index 00000000000..c768598b294 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/AbstractImplicitTypeInspection.kt @@ -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( + SpecifyTypeExplicitlyIntention::class, + { element, inspection -> + with(inspection as AbstractImplicitTypeInspection) { + element.typeReference == null && additionalChecker(element, inspection) + } + } +) { + override fun inspectionTarget(element: KtCallableDeclaration) = element.nameIdentifier +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/HasPlatformTypeInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/HasPlatformTypeInspection.kt index bc55988ae09..3e1aee4127b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/HasPlatformTypeInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/HasPlatformTypeInspection.kt @@ -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( - 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? { - 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") diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/PublicApiImplicitTypeInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/PublicApiImplicitTypeInspection.kt new file mode 100644 index 00000000000..9c12988a706 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/PublicApiImplicitTypeInspection.kt @@ -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" +} \ No newline at end of file diff --git a/idea/testData/inspections/publicApiImplicitType/inspectionData/expected.xml b/idea/testData/inspections/publicApiImplicitType/inspectionData/expected.xml new file mode 100644 index 00000000000..fff89a633ae --- /dev/null +++ b/idea/testData/inspections/publicApiImplicitType/inspectionData/expected.xml @@ -0,0 +1,34 @@ + + + test.kt + 4 + light_idea_test_case + + Public API declaration has implicit return type + For API stability, it's recommended to specify explicitly public & protected declaration types + + + test.kt + 14 + light_idea_test_case + + Public API declaration has implicit return type + For API stability, it's recommended to specify explicitly public & protected declaration types + + + test.kt + 17 + light_idea_test_case + + Public API declaration has implicit return type + For API stability, it's recommended to specify explicitly public & protected declaration types + + + test.kt + 20 + light_idea_test_case + + Public API declaration has implicit return type + For API stability, it's recommended to specify explicitly public & protected declaration types + + \ No newline at end of file diff --git a/idea/testData/inspections/publicApiImplicitType/inspectionData/inspections.test b/idea/testData/inspections/publicApiImplicitType/inspectionData/inspections.test new file mode 100644 index 00000000000..668da13bf75 --- /dev/null +++ b/idea/testData/inspections/publicApiImplicitType/inspectionData/inspections.test @@ -0,0 +1 @@ +// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.PublicApiImplicitTypeInspection \ No newline at end of file diff --git a/idea/testData/inspections/publicApiImplicitType/test.kt b/idea/testData/inspections/publicApiImplicitType/test.kt new file mode 100644 index 00000000000..22d0a8e92df --- /dev/null +++ b/idea/testData/inspections/publicApiImplicitType/test.kt @@ -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 = "" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java index 4e3f3e952b8..0695aba4fbe 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java @@ -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");