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"
|
||||
/>
|
||||
|
||||
<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 & 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);
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
Reference in New Issue
Block a user