diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt index 4039a4a1394..5fb93c920f2 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt @@ -417,3 +417,12 @@ fun returnExpressionVisitor(block: (KtReturnExpression) -> Unit) = block(returnExpression) } } + + +fun delegatedSuperTypeEntry(block: (KtDelegatedSuperTypeEntry) -> Unit) = + object : KtVisitorVoid() { + override fun visitDelegatedSuperTypeEntry(delegatedSuperTypeEntry: KtDelegatedSuperTypeEntry) { + super.visitDelegatedSuperTypeEntry(delegatedSuperTypeEntry) + block(delegatedSuperTypeEntry) + } + } diff --git a/idea/resources/inspectionDescriptions/DelegationToVarProperty.html b/idea/resources/inspectionDescriptions/DelegationToVarProperty.html new file mode 100644 index 00000000000..512098fd8a5 --- /dev/null +++ b/idea/resources/inspectionDescriptions/DelegationToVarProperty.html @@ -0,0 +1,6 @@ + + +This inspection reports delegation to var property. +In fact, only starting value of var property is used for delegation so it's not recommended to do so. + + diff --git a/idea/src/META-INF/plugin-common.xml b/idea/src/META-INF/plugin-common.xml index 6c6f3723a53..69ed76bfccf 100644 --- a/idea/src/META-INF/plugin-common.xml +++ b/idea/src/META-INF/plugin-common.xml @@ -3084,6 +3084,15 @@ language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/DelegationToVarPropertyInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/DelegationToVarPropertyInspection.kt new file mode 100644 index 00000000000..3a2b78b2c28 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/DelegationToVarPropertyInspection.kt @@ -0,0 +1,40 @@ +/* + * 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 com.intellij.codeInspection.* +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.idea.quickfix.ChangeVariableMutabilityFix +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.delegatedSuperTypeEntry + +class DelegationToVarPropertyInspection : AbstractKotlinInspection() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = + delegatedSuperTypeEntry(fun(delegatedSuperTypeEntry) { + val parameter = delegatedSuperTypeEntry.delegateExpression?.mainReference?.resolve() as? KtParameter ?: return + if (parameter.valOrVarKeyword?.node?.elementType != KtTokens.VAR_KEYWORD) return + holder.registerProblem( + parameter, + "Delegating to 'var' property does not take its changes into account", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + IntentionWrapper(ChangeVariableMutabilityFix(parameter, false), parameter.containingFile), + RemoveVarKeyword() + ) + }) +} + +private class RemoveVarKeyword : LocalQuickFix { + override fun getName() = "Remove var" + + override fun getFamilyName() = name + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + (descriptor.psiElement as? KtParameter)?.valOrVarKeyword?.delete() + + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/delegationToVarProperty/.inspection b/idea/testData/inspectionsLocal/delegationToVarProperty/.inspection new file mode 100644 index 00000000000..19832897511 --- /dev/null +++ b/idea/testData/inspectionsLocal/delegationToVarProperty/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.DelegationToVarPropertyInspection diff --git a/idea/testData/inspectionsLocal/delegationToVarProperty/parameter.kt b/idea/testData/inspectionsLocal/delegationToVarProperty/parameter.kt new file mode 100644 index 00000000000..eff596e4ee6 --- /dev/null +++ b/idea/testData/inspectionsLocal/delegationToVarProperty/parameter.kt @@ -0,0 +1,9 @@ +// PROBLEM: none +interface A { + fun foo() +} +class B : A { + override fun foo() { + } +} +class C(b: B) : A by b \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/delegationToVarProperty/valParameter.kt b/idea/testData/inspectionsLocal/delegationToVarProperty/valParameter.kt new file mode 100644 index 00000000000..f65b1f28ba3 --- /dev/null +++ b/idea/testData/inspectionsLocal/delegationToVarProperty/valParameter.kt @@ -0,0 +1,8 @@ +// PROBLEM: none +interface A { + fun foo() +} +class B : A { + override fun foo() {} +} +class C(val b: B) : A by b \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter.kt b/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter.kt new file mode 100644 index 00000000000..7449b8cdc64 --- /dev/null +++ b/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter.kt @@ -0,0 +1,8 @@ +// FIX: Change to val +interface A { + fun foo() +} +class B : A { + override fun foo() {} +} +class C(var b: B) : A by b \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter.kt.after b/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter.kt.after new file mode 100644 index 00000000000..79835a5fccc --- /dev/null +++ b/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter.kt.after @@ -0,0 +1,8 @@ +// FIX: Change to val +interface A { + fun foo() +} +class B : A { + override fun foo() {} +} +class C(val b: B) : A by b \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter2.kt b/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter2.kt new file mode 100644 index 00000000000..ca9c76510a3 --- /dev/null +++ b/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter2.kt @@ -0,0 +1,8 @@ +// FIX: Remove var +interface A { + fun foo() +} +class B : A { + override fun foo() {} +} +class C(var b: B) : A by b \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter2.kt.after b/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter2.kt.after new file mode 100644 index 00000000000..4ae150a2761 --- /dev/null +++ b/idea/testData/inspectionsLocal/delegationToVarProperty/varParameter2.kt.after @@ -0,0 +1,8 @@ +// FIX: Remove var +interface A { + fun foo() +} +class B : A { + override fun foo() {} +} +class C(b: B) : A by b \ 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 93853fff690..ff9fe1fb22e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -2115,6 +2115,39 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } } + @TestMetadata("idea/testData/inspectionsLocal/delegationToVarProperty") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DelegationToVarProperty extends AbstractLocalInspectionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInDelegationToVarProperty() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/delegationToVarProperty"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("parameter.kt") + public void testParameter() throws Exception { + runTest("idea/testData/inspectionsLocal/delegationToVarProperty/parameter.kt"); + } + + @TestMetadata("valParameter.kt") + public void testValParameter() throws Exception { + runTest("idea/testData/inspectionsLocal/delegationToVarProperty/valParameter.kt"); + } + + @TestMetadata("varParameter.kt") + public void testVarParameter() throws Exception { + runTest("idea/testData/inspectionsLocal/delegationToVarProperty/varParameter.kt"); + } + + @TestMetadata("varParameter2.kt") + public void testVarParameter2() throws Exception { + runTest("idea/testData/inspectionsLocal/delegationToVarProperty/varParameter2.kt"); + } + } + @TestMetadata("idea/testData/inspectionsLocal/deprecatedCallableAddReplaceWith") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)