diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt index 955334a38ec..b5f03667165 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt @@ -454,3 +454,8 @@ fun checkReservedPrefixWord(sink: DiagnosticSink, element: PsiElement, word: Str } } +fun KtElement.nonStaticOuterClasses(): Sequence { + return generateSequence(containingClass()) { if (it.isInner()) it.containingClass() else null } +} + +fun KtElement.containingClass(): KtClass? = getStrictParentOfType() \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/MakeConstructorParameterPropertyFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/MakeConstructorParameterPropertyFix.kt new file mode 100644 index 00000000000..0f8a30f5956 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/MakeConstructorParameterPropertyFix.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNameReferenceExpression +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.psiUtil.containingClass +import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS +import org.jetbrains.kotlin.psi.psiUtil.nonStaticOuterClasses + +class MakeConstructorParameterPropertyFix( + element: KtParameter, private val kotlinValVar: KotlinValVar, private val className: String? +) : KotlinQuickFixAction(element) { + override fun getFamilyName() = "Make primary constructor parameter a property" + + private val suffix = if (className != null) " in class '$className'" else "" + override fun getText() = "Make primary constructor parameter '${element.name}' a property" + suffix + + override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean { + return super.isAvailable(project, editor, file) && !element.hasValOrVar() + } + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + element.addBefore(kotlinValVar.createKeyword(KtPsiFactory(project))!!, element.firstChild) + } + + companion object Factory : KotlinIntentionActionsFactory() { + + override fun doCreateActions(diagnostic: Diagnostic): List { + val ktReference = Errors.UNRESOLVED_REFERENCE.cast(diagnostic).a as? KtNameReferenceExpression ?: return emptyList() + + val valOrVar = if (ktReference.getAssignmentByLHS() != null) KotlinValVar.Var else KotlinValVar.Val + + val ktParameter = ktReference.getPrimaryConstructorParameterWithSameName() ?: return emptyList() + val containingClass = ktParameter.containingClass()!! + val className = if (containingClass != ktReference.containingClass()) containingClass.nameAsSafeName.asString() else null + + return listOf(MakeConstructorParameterPropertyFix(ktParameter, valOrVar, className)) + } + } +} + +fun KtNameReferenceExpression.getPrimaryConstructorParameterWithSameName(): KtParameter? { + return nonStaticOuterClasses() + .mapNotNull { it.getPrimaryConstructor()?.valueParameters?.firstOrNull { it.name == getReferencedName() } } + .firstOrNull() +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index d4013e4f74e..0d38e0f226e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -19,13 +19,9 @@ package org.jetbrains.kotlin.idea.quickfix import com.intellij.codeInsight.intention.IntentionAction import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors.* -import org.jetbrains.kotlin.idea.inspections.PlatformUnresolvedProvider import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementAsConstructorParameter import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler -import org.jetbrains.kotlin.idea.inspections.AddModifierFixFactory -import org.jetbrains.kotlin.idea.inspections.AddReflectionQuickFix -import org.jetbrains.kotlin.idea.inspections.AddTestLibQuickFix -import org.jetbrains.kotlin.idea.inspections.InfixCallFix +import org.jetbrains.kotlin.idea.inspections.* import org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterAction import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.* import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromCallWithConstructorCalleeActionFactory @@ -381,6 +377,8 @@ class QuickFixRegistrar : QuickFixContributor { NON_LOCAL_RETURN_NOT_ALLOWED.registerFactory(AddCrossInlineFix) + UNRESOLVED_REFERENCE.registerFactory(MakeConstructorParameterPropertyFix) + SUPERTYPE_IS_EXTENSION_FUNCTION_TYPE.registerFactory(ConvertExtensionToFunctionTypeFix) } } diff --git a/idea/testData/quickfix/makeConstructorParameterProperty/inner.kt b/idea/testData/quickfix/makeConstructorParameterProperty/inner.kt new file mode 100644 index 00000000000..d217ac834e3 --- /dev/null +++ b/idea/testData/quickfix/makeConstructorParameterProperty/inner.kt @@ -0,0 +1,10 @@ +// "Make primary constructor parameter 'bar' a property in class 'B'" "true" + +class B(bar: String) { + + inner class A { + fun foo() { + val a = bar + } + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/makeConstructorParameterProperty/inner.kt.after b/idea/testData/quickfix/makeConstructorParameterProperty/inner.kt.after new file mode 100644 index 00000000000..398d372bdfd --- /dev/null +++ b/idea/testData/quickfix/makeConstructorParameterProperty/inner.kt.after @@ -0,0 +1,10 @@ +// "Make primary constructor parameter 'bar' a property in class 'B'" "true" + +class B(val bar: String) { + + inner class A { + fun foo() { + val a = bar + } + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/makeConstructorParameterProperty/val.kt b/idea/testData/quickfix/makeConstructorParameterProperty/val.kt new file mode 100644 index 00000000000..62049c7ed2d --- /dev/null +++ b/idea/testData/quickfix/makeConstructorParameterProperty/val.kt @@ -0,0 +1,7 @@ +// "Make primary constructor parameter 'foo' a property" "true" + +class A(foo: String) { + fun bar() { + val a = foo + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/makeConstructorParameterProperty/val.kt.after b/idea/testData/quickfix/makeConstructorParameterProperty/val.kt.after new file mode 100644 index 00000000000..fb8e6431a8d --- /dev/null +++ b/idea/testData/quickfix/makeConstructorParameterProperty/val.kt.after @@ -0,0 +1,7 @@ +// "Make primary constructor parameter 'foo' a property" "true" + +class A(val foo: String) { + fun bar() { + val a = foo + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/makeConstructorParameterProperty/var.kt b/idea/testData/quickfix/makeConstructorParameterProperty/var.kt new file mode 100644 index 00000000000..e7e855b1837 --- /dev/null +++ b/idea/testData/quickfix/makeConstructorParameterProperty/var.kt @@ -0,0 +1,7 @@ +// "Make primary constructor parameter 'foo' a property" "true" + +class A(foo: String) { + fun bar() { + foo = "" + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/makeConstructorParameterProperty/var.kt.after b/idea/testData/quickfix/makeConstructorParameterProperty/var.kt.after new file mode 100644 index 00000000000..6450fa71612 --- /dev/null +++ b/idea/testData/quickfix/makeConstructorParameterProperty/var.kt.after @@ -0,0 +1,7 @@ +// "Make primary constructor parameter 'foo' a property" "true" + +class A(var foo: String) { + fun bar() { + foo = "" + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index b0e152610e6..7e7174b681c 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -5089,6 +5089,33 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/makeConstructorParameterProperty") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MakeConstructorParameterProperty extends AbstractQuickFixTest { + public void testAllFilesPresentInMakeConstructorParameterProperty() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/makeConstructorParameterProperty"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("inner.kt") + public void testInner() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/makeConstructorParameterProperty/inner.kt"); + doTest(fileName); + } + + @TestMetadata("val.kt") + public void testVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/makeConstructorParameterProperty/val.kt"); + doTest(fileName); + } + + @TestMetadata("var.kt") + public void testVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/makeConstructorParameterProperty/var.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/quickfix/migration") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)