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");