Inspection: convert initialized val to non-null type

^KT-35757 Fixed
This commit is contained in:
kvirolainen
2020-09-03 16:29:45 +02:00
committed by Vladimir Dolzhenko
parent 8b28e54584
commit 18fbf5729d
13 changed files with 149 additions and 0 deletions
@@ -2000,6 +2000,7 @@ action.ConfigureKotlinInProject.text=Configure Kotlin in Project
action.KotlinConsoleREPL.text=Kotlin REPL action.KotlinConsoleREPL.text=Kotlin REPL
action.LibraryToSourceDependencySupportToggleAction.text=Toggle library to source dependency support action.LibraryToSourceDependencySupportToggleAction.text=Toggle library to source dependency support
inspection.convert.initialized.val.to.non.null.type.display.name=Convert initialized 'val' to non-null type
inspection.unused.unary.operator.display.name=Unused unary operator inspection.unused.unary.operator.display.name=Unused unary operator
inspection.incomplete.destructuring.declaration.display.name=Incomplete destructuring declaration inspection.incomplete.destructuring.declaration.display.name=Incomplete destructuring declaration
inspection.replace.guard.clause.with.function.call.display.name=Replace guard clause with kotlin's function call inspection.replace.guard.clause.with.function.call.display.name=Replace guard clause with kotlin's function call
+8
View File
@@ -2503,6 +2503,14 @@
language="kotlin" language="kotlin"
key="inspection.unused.unary.operator.display.name" bundle="messages.KotlinBundle"/> key="inspection.unused.unary.operator.display.name" bundle="messages.KotlinBundle"/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ConvertInitializedValToNonNullTypeInspection"
groupPath="Kotlin"
groupName="Probable bugs"
enabledByDefault="true"
level="INFO"
language="kotlin"
key="inspection.convert.initialized.val.to.non.null.type.display.name" bundle="messages.KotlinBundle"/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.IncompleteDestructuringInspection" <localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.IncompleteDestructuringInspection"
groupPath="Kotlin" groupPath="Kotlin"
groupName="Probable bugs" groupName="Probable bugs"
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports nullable type of initialized 'val' should be converted into non-nullable type.
</body>
</html>
@@ -0,0 +1,70 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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 com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isNull
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.TypeUtils
class ConvertInitializedValToNonNullTypeInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
propertyVisitor(fun(property) {
val typeReference = property.typeReference ?: return
if (shouldConvertToNonNullType(property)) {
holder.registerProblem(
typeReference,
"Initialized 'val' should be converted to non-null type",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RemoveRedundantNullableTypeQuickfix()
)
}
})
companion object {
private fun shouldConvertToNonNullType(property: KtProperty): Boolean {
val initializer = property.initializer ?: return false
val type = property.resolveToDescriptorIfAny()?.type ?: return false
if (!TypeUtils.isNullableType(type)) return false
when (initializer) {
is KtConstantExpression -> {
return !initializer.isNull()
}
is KtStringTemplateExpression -> {
return true
}
is KtNameReferenceExpression, is KtCallExpression -> {
val context = initializer.analyze(BodyResolveMode.PARTIAL)
val assignedType = initializer.getType(context) ?: return false
return !TypeUtils.isNullableType(assignedType)
}
else -> return false
}
}
}
}
class RemoveRedundantNullableTypeQuickfix : LocalQuickFix {
override fun getName() = "Convert initialized 'val' to non-null type"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement
val factory = KtPsiFactory(project)
element.replace(factory.createType(element.text.removeSuffix("?")))
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.ConvertInitializedValToNonNullTypeInspection
@@ -0,0 +1,5 @@
fun foo() {
val s1: String?<caret> = bar()
}
fun bar(): String = ""
@@ -0,0 +1,5 @@
fun foo() {
val s1: String = bar()
}
fun bar(): String = ""
@@ -0,0 +1,4 @@
fun foo() {
val s0: String = ""
val s1: String?<caret> = s0
}
@@ -0,0 +1,4 @@
fun foo() {
val s0: String = ""
val s1: String = s0
}
@@ -0,0 +1,3 @@
fun foo() {
val s: String?<caret> = "Hello"
}
@@ -0,0 +1,3 @@
fun foo() {
val s: String = "Hello"
}
@@ -0,0 +1,7 @@
// PROBLEM: none
fun foo() {
val s1: String? = bar()
val s2: String?<caret> = null
}
fun bar(): String? = ""
@@ -3480,6 +3480,39 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
} }
} }
@TestMetadata("idea/testData/inspectionsLocal/convertInitializedValToNonNullType")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ConvertInitializedValToNonNullType extends AbstractLocalInspectionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInConvertInitializedValToNonNullType() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/inspectionsLocal/convertInitializedValToNonNullType"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), null, true);
}
@TestMetadata("assignToMethodCall.kt")
public void testAssignToMethodCall() throws Exception {
runTest("idea/testData/inspectionsLocal/convertInitializedValToNonNullType/assignToMethodCall.kt");
}
@TestMetadata("assignToVariable.kt")
public void testAssignToVariable() throws Exception {
runTest("idea/testData/inspectionsLocal/convertInitializedValToNonNullType/assignToVariable.kt");
}
@TestMetadata("basic.kt")
public void testBasic() throws Exception {
runTest("idea/testData/inspectionsLocal/convertInitializedValToNonNullType/basic.kt");
}
@TestMetadata("negative.kt")
public void testNegative() throws Exception {
runTest("idea/testData/inspectionsLocal/convertInitializedValToNonNullType/negative.kt");
}
}
@TestMetadata("idea/testData/inspectionsLocal/convertNaNEquality") @TestMetadata("idea/testData/inspectionsLocal/convertNaNEquality")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)