Add inspection to detect public API with implicit type
This commit is contained in:
@@ -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>
|
||||||
@@ -2761,6 +2761,15 @@
|
|||||||
language="kotlin"
|
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"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<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.IntentionWrapper
|
||||||
import com.intellij.codeInspection.LocalQuickFix
|
import com.intellij.codeInspection.LocalQuickFix
|
||||||
import com.intellij.codeInspection.ProblemHighlightType
|
|
||||||
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel
|
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel
|
||||||
import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention
|
import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.isFlexibleRecursive
|
import org.jetbrains.kotlin.idea.intentions.isFlexibleRecursive
|
||||||
@@ -30,27 +29,33 @@ import org.jetbrains.kotlin.types.TypeUtils
|
|||||||
import javax.swing.JComponent
|
import javax.swing.JComponent
|
||||||
|
|
||||||
class HasPlatformTypeInspection(
|
class HasPlatformTypeInspection(
|
||||||
@JvmField var publicAPIOnly: Boolean = true,
|
@JvmField var publicAPIOnly: Boolean = true,
|
||||||
@JvmField var reportPlatformArguments: Boolean = false
|
@JvmField var reportPlatformArguments: Boolean = false
|
||||||
) : IntentionBasedInspection<KtCallableDeclaration>(
|
) : AbstractImplicitTypeInspection(
|
||||||
SpecifyTypeExplicitlyIntention::class,
|
{ element, inspection ->
|
||||||
{ element, inspection ->
|
with(inspection as HasPlatformTypeInspection) {
|
||||||
with(inspection as HasPlatformTypeInspection) {
|
SpecifyTypeExplicitlyIntention.dangerousFlexibleTypeOrNull(
|
||||||
SpecifyTypeExplicitlyIntention.dangerousFlexibleTypeOrNull(element, this.publicAPIOnly, this.reportPlatformArguments) != null
|
element,
|
||||||
}
|
this.publicAPIOnly,
|
||||||
|
this.reportPlatformArguments
|
||||||
|
) != null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override val problemText = "Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. " +
|
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>? {
|
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)) {
|
if (TypeUtils.isNullableType(type)) {
|
||||||
val expression = element.node.findChildByType(KtTokens.EQ)?.psi?.getNextSiblingIgnoringWhitespaceAndComments()
|
val expression = element.node.findChildByType(KtTokens.EQ)?.psi?.getNextSiblingIgnoringWhitespaceAndComments()
|
||||||
if (expression != null &&
|
if (expression != null &&
|
||||||
(!reportPlatformArguments || !TypeUtils.makeNotNullable(type).isFlexibleRecursive())) {
|
(!reportPlatformArguments || !TypeUtils.makeNotNullable(type).isFlexibleRecursive())
|
||||||
|
) {
|
||||||
return listOf(IntentionWrapper(AddExclExclCallFix(expression), element.containingFile))
|
return listOf(IntentionWrapper(AddExclExclCallFix(expression), element.containingFile))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,8 +63,6 @@ class HasPlatformTypeInspection(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun inspectionTarget(element: KtCallableDeclaration) = element.nameIdentifier
|
|
||||||
|
|
||||||
override fun createOptionsPanel(): JComponent? {
|
override fun createOptionsPanel(): JComponent? {
|
||||||
val panel = MultipleCheckboxOptionsPanel(this)
|
val panel = MultipleCheckboxOptionsPanel(this)
|
||||||
panel.addCheckbox("Apply only to public or protected members", "publicAPIOnly")
|
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 & 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 & 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 & 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 & protected declaration types</description>
|
||||||
|
</problem>
|
||||||
|
</problems>
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.PublicApiImplicitTypeInspection
|
||||||
@@ -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 = ""
|
||||||
|
}
|
||||||
+6
@@ -276,6 +276,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("recursivePropertyAccessor/inspectionData/inspections.test")
|
||||||
public void testRecursivePropertyAccessor_inspectionData_Inspections_test() throws Exception {
|
public void testRecursivePropertyAccessor_inspectionData_Inspections_test() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/recursivePropertyAccessor/inspectionData/inspections.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/recursivePropertyAccessor/inspectionData/inspections.test");
|
||||||
|
|||||||
Reference in New Issue
Block a user