diff --git a/idea/resources/META-INF/plugin-common.xml b/idea/resources/META-INF/plugin-common.xml index acc64b27dc0..829d4f918ed 100644 --- a/idea/resources/META-INF/plugin-common.xml +++ b/idea/resources/META-INF/plugin-common.xml @@ -3408,6 +3408,15 @@ language="kotlin" /> + + diff --git a/idea/resources/inspectionDescriptions/LateinitVarOverridesLateinitVar.html b/idea/resources/inspectionDescriptions/LateinitVarOverridesLateinitVar.html new file mode 100644 index 00000000000..2d44a658ba4 --- /dev/null +++ b/idea/resources/inspectionDescriptions/LateinitVarOverridesLateinitVar.html @@ -0,0 +1,6 @@ + + +This inspection reports when one lateinit var property overrides another lateinit var property. +An instance of a subclass will have two fields for the single property and the one from a superclass remains effectively unused. + + \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/LateinitVarOverridesLateinitVarInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/LateinitVarOverridesLateinitVarInspection.kt new file mode 100644 index 00000000000..cc399012cd3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/LateinitVarOverridesLateinitVarInspection.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2019 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.ProblemsHolder +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.classOrObjectRecursiveVisitor + +class LateinitVarOverridesLateinitVarInspection : AbstractKotlinInspection() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = classOrObjectRecursiveVisitor(fun(klass) { + for (declaration in klass.declarations) { + val property = declaration as? KtProperty ?: continue + if (!property.hasModifier(KtTokens.OVERRIDE_KEYWORD) || !property.hasModifier(KtTokens.LATEINIT_KEYWORD) || !property.isVar) { + continue + } + val identifier = property.nameIdentifier ?: continue + val descriptor = property.resolveToDescriptorIfAny() ?: continue + if (descriptor.overriddenDescriptors.any { (it as? PropertyDescriptor)?.let { d -> d.isLateInit && d.isVar } == true }) { + holder.registerProblem( + identifier, + "lateinit var overrides lateinit var" + ) + } + } + }) +} diff --git a/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/.inspection b/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/.inspection new file mode 100644 index 00000000000..b71ca2012a5 --- /dev/null +++ b/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.LateinitVarOverridesLateinitVarInspection \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/basic.kt b/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/basic.kt new file mode 100644 index 00000000000..3004b5f8dfd --- /dev/null +++ b/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/basic.kt @@ -0,0 +1,8 @@ +// FIX: none +open class A1 { + open lateinit var a: String +} + +class A2 : A1() { + override lateinit var a: String +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/notLateinit.kt b/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/notLateinit.kt new file mode 100644 index 00000000000..f2ed1f96b1b --- /dev/null +++ b/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/notLateinit.kt @@ -0,0 +1,8 @@ +// PROBLEM: none +open class A1 { + open var a: String = "" +} + +class A2 : A1() { + override lateinit var a: String +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/notLateinit2.kt b/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/notLateinit2.kt new file mode 100644 index 00000000000..3ad9282146d --- /dev/null +++ b/idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/notLateinit2.kt @@ -0,0 +1,8 @@ +// PROBLEM: none +open class A1 { + open lateinit var a: String +} + +class A2 : A1() { + override var a: String = "" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 8fbc501bb4e..ab0e6a9001f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -4475,6 +4475,34 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } } + @TestMetadata("idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class LateinitVarOverridesLateinitVar extends AbstractLocalInspectionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInLateinitVarOverridesLateinitVar() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + runTest("idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/basic.kt"); + } + + @TestMetadata("notLateinit.kt") + public void testNotLateinit() throws Exception { + runTest("idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/notLateinit.kt"); + } + + @TestMetadata("notLateinit2.kt") + public void testNotLateinit2() throws Exception { + runTest("idea/testData/inspectionsLocal/lateinitVarOverridesLateinitVar/notLateinit2.kt"); + } + } + @TestMetadata("idea/testData/inspectionsLocal/leakingThis") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)