Add "Constructor has non-null self reference parameter" inspection (KT-29799)

#KT-29799 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-03-07 11:46:35 +03:00
committed by Nikolay Krasko
parent 6634d7ab15
commit bcfab83d1c
8 changed files with 104 additions and 0 deletions
@@ -3364,6 +3364,15 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.SelfReferenceConstructorParameterInspection"
displayName="Constructor has non-null self reference parameter"
groupPath="Kotlin"
groupName="Probable bugs"
enabledByDefault="true"
level="WARNING"
language="kotlin"
/>
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports constructor that has a non-null self reference parameter. A class with such a constructor can never be instantiated.
</body>
</html>
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2019 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 com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.setType
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtParameterList
import org.jetbrains.kotlin.psi.primaryConstructorVisitor
import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.makeNullable
class SelfReferenceConstructorParameterInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = primaryConstructorVisitor(fun(constructor) {
val parameter = constructor.valueParameterList?.selfReferenceParameter() ?: return
val rangeInElement = parameter.typeReference?.textRange?.shiftRight(-parameter.startOffset) ?: return
holder.registerProblem(
parameter,
rangeInElement,
"Constructor has non-null self reference parameter",
ConvertToNullableTypeFix()
)
})
private fun KtParameterList.selfReferenceParameter(): KtParameter? {
val containingClass = this.containingClass() ?: return null
val className = containingClass.name ?: return null
val parameter = this.parameters.firstOrNull { it.typeReference?.text == className } ?: return null
val typeReference = parameter.typeReference ?: return null
val context = analyze(BodyResolveMode.PARTIAL)
val type = context[BindingContext.TYPE, typeReference] ?: return null
if (type.isNullable()) return null
if (type.constructor.declarationDescriptor != context[BindingContext.DECLARATION_TO_DESCRIPTOR, containingClass]) return null
return parameter
}
private class ConvertToNullableTypeFix : LocalQuickFix {
override fun getName() = "Convert to nullable type"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val parameter = descriptor.psiElement as? KtParameter ?: return
val typeReference = parameter.typeReference ?: return
val type = parameter.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference] ?: return
parameter.setType(type.makeNullable())
}
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.SelfReferenceConstructorParameterInspection
@@ -0,0 +1 @@
class SelfRef(val ref: SelfRef<caret>)
@@ -0,0 +1 @@
class SelfRef(val ref: SelfRef?)
@@ -0,0 +1,2 @@
// PROBLEM: none
class SelfRef(val ref: <caret>SelfRef?)
@@ -7917,6 +7917,29 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
@TestMetadata("idea/testData/inspectionsLocal/selfReferenceConstructorParameter")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class SelfReferenceConstructorParameter extends AbstractLocalInspectionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInSelfReferenceConstructorParameter() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/selfReferenceConstructorParameter"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
}
@TestMetadata("basic.kt")
public void testBasic() throws Exception {
runTest("idea/testData/inspectionsLocal/selfReferenceConstructorParameter/basic.kt");
}
@TestMetadata("nullable.kt")
public void testNullable() throws Exception {
runTest("idea/testData/inspectionsLocal/selfReferenceConstructorParameter/nullable.kt");
}
}
@TestMetadata("idea/testData/inspectionsLocal/setterBackingFieldAssignment")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)