diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java index 26bc5c5bcff..3908d3e8550 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java @@ -258,6 +258,9 @@ public class QuickFixRegistrar { QuickFixes.factories.put(TOO_MANY_ARGUMENTS, CreateCallableFromCallActionFactory.INSTANCE$); QuickFixes.factories.put(EXPRESSION_EXPECTED_PACKAGE_FOUND, CreateCallableFromCallActionFactory.INSTANCE$); + QuickFixes.factories.put(NO_VALUE_FOR_PARAMETER, CreateConstructorFromDelegationCallActionFactory.INSTANCE$); + QuickFixes.factories.put(TOO_MANY_ARGUMENTS, CreateConstructorFromDelegationCallActionFactory.INSTANCE$); + QuickFixes.factories.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, CreateClassFromConstructorCallActionFactory.INSTANCE$); QuickFixes.factories.put(UNRESOLVED_REFERENCE, CreateClassFromConstructorCallActionFactory.INSTANCE$); QuickFixes.factories.put(EXPRESSION_EXPECTED_PACKAGE_FOUND, CreateClassFromConstructorCallActionFactory.INSTANCE$); diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt index 63fb1396bc0..8e49b8d0908 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt @@ -32,12 +32,16 @@ import java.util.Collections import java.util.HashSet public class CreateCallableFromUsageFix( - originalExpression: JetExpression, + originalElement: JetElement, val callableInfos: List, val isExtension: Boolean -) : CreateFromUsageFixBase(originalExpression) { - { - assert (callableInfos.isNotEmpty(), "No CallableInfos: ${JetPsiUtil.getElementTextWithContext(originalExpression)}") +) : CreateFromUsageFixBase(originalElement) { + constructor(originalExpression: JetElement, + callableInfo: CallableInfo, + isExtension: Boolean) : this(originalExpression, Collections.singletonList(callableInfo), isExtension) { } + + init { + assert (callableInfos.isNotEmpty(), "No CallableInfos: ${JetPsiUtil.getElementTextWithContext(originalElement)}") if (callableInfos.size > 1) { val receiverSet = callableInfos.mapTo(HashSet()) { it.receiverTypeInfo } if (receiverSet.size > 1) throw AssertionError("All functions must have common receiver: $receiverSet") @@ -103,7 +107,7 @@ public class CreateCallableFromUsageFix( val callableInfo = callableInfos.first() val callableBuilder = - CallableBuilderConfiguration(callableInfos, element as JetExpression, file!!, editor!!, isExtension).createBuilder() + CallableBuilderConfiguration(callableInfos, element as JetElement, file!!, editor!!, isExtension).createBuilder() fun runBuilder(placement: CallablePlacement) { callableBuilder.placement = placement diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegationCallActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegationCallActionFactory.kt new file mode 100644 index 00000000000..6f85cd81f7f --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegationCallActionFactory.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2015 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.createFromUsage.createCallable + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.psi.PsiClass +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde +import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.SecondaryConstructorInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo +import org.jetbrains.kotlin.idea.refactoring.canRefactor +import org.jetbrains.kotlin.psi.JetClass +import org.jetbrains.kotlin.psi.JetConstructorDelegationCall +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.types.Variance + +object CreateConstructorFromDelegationCallActionFactory : JetSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val delegationCall = diagnostic.getPsiElement().getStrictParentOfType() ?: return null + val calleeExpression = delegationCall.getCalleeExpression() ?: return null + val currentClass = delegationCall.getStrictParentOfType() ?: return null + + val project = currentClass.getProject() + + val classDescriptor = currentClass.resolveToDescriptor() as? ClassDescriptor ?: return null + + val targetClass = if (calleeExpression.isThis()) { + currentClass + } + else { + val superClassDescriptor = DescriptorUtils.getSuperclassDescriptors(classDescriptor) + .singleOrNull { it.getKind() == ClassKind.CLASS } ?: return null + DescriptorToSourceUtilsIde.getAnyDeclaration(project, superClassDescriptor) ?: return null + } + if (!(targetClass.canRefactor() && (targetClass is JetClass || targetClass is PsiClass))) return null + + val anyType = KotlinBuiltIns.getInstance().getNullableAnyType() + val parameters = delegationCall.getValueArguments().map { + ParameterInfo( + it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE), + it.getArgumentName()?.getReferenceExpression()?.getReferencedName() + ) + } + + return CreateCallableFromUsageFix(delegationCall, SecondaryConstructorInfo(parameters, targetClass), false) + } +} diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/afterSuperCall.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/afterSuperCall.kt new file mode 100644 index 00000000000..b01ee4583ed --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/afterSuperCall.kt @@ -0,0 +1,14 @@ +// "Create secondary constructor" "true" + +open class A { + constructor(i: Int) { + //To change body of created constructors use File | Settings | File Templates. + } + +} + +class B: A { + constructor(): super(1) { + + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/afterThisCall.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/afterThisCall.kt new file mode 100644 index 00000000000..58ed14e95e0 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/afterThisCall.kt @@ -0,0 +1,11 @@ +// "Create secondary constructor" "true" + +class A { + constructor(): this(1) { + + } + + constructor(i: Int) { + //To change body of created constructors use File | Settings | File Templates. + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeSuperCall.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeSuperCall.kt new file mode 100644 index 00000000000..1fd3126a6fc --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeSuperCall.kt @@ -0,0 +1,11 @@ +// "Create secondary constructor" "true" + +open class A { + +} + +class B: A { + constructor(): super(1) { + + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeSuperCallNoClass.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeSuperCallNoClass.kt new file mode 100644 index 00000000000..01af0abe100 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeSuperCallNoClass.kt @@ -0,0 +1,12 @@ +// "Create secondary constructor" "false" +// ERROR: Too many arguments for public constructor Any() defined in kotlin.Any + +trait T { + +} + +class A: T { + constructor(): super(1) { + + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeThisCall.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeThisCall.kt new file mode 100644 index 00000000000..0595029b82c --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeThisCall.kt @@ -0,0 +1,7 @@ +// "Create secondary constructor" "true" + +class A { + constructor(): this(1) { + + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.after.data.Sample.java b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.after.data.Sample.java new file mode 100644 index 00000000000..34daf278dc2 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.after.data.Sample.java @@ -0,0 +1,6 @@ +class J { + + public J(int i) { + + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.after.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.after.kt new file mode 100644 index 00000000000..d74910b850e --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.after.kt @@ -0,0 +1,8 @@ +// "Create secondary constructor" "true" +// ERROR: Too many arguments for public/*package*/ constructor J() defined in J + +class B: J { + constructor(): super(1) { + + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.before.Main.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.before.Main.kt new file mode 100644 index 00000000000..de67d8f11b1 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.before.Main.kt @@ -0,0 +1,8 @@ +// "Create secondary constructor" "true" +// ERROR: Too many arguments for public/*package*/ constructor J() defined in J + +class B: J { + constructor(): super(1) { + + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.before.data.Sample.java b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.before.data.Sample.java new file mode 100644 index 00000000000..cfa2a621482 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.before.data.Sample.java @@ -0,0 +1,3 @@ +class J { + +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index b10de5b0f79..e1a098db3b1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -776,6 +776,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/javaConstructor.before.Main.kt"); doTestWithExtraFile(fileName); } + + @TestMetadata("superCallJavaClass.before.Main.kt") + public void testSuperCallJavaClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallJavaClass.before.Main.kt"); + doTestWithExtraFile(fileName); + } } @TestMetadata("idea/testData/quickfix/createFromUsage/createVariable") diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index abd05090685..f8f76ab56ef 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -2299,6 +2299,24 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("beforeSuperCall.kt") + public void testSuperCall() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeSuperCall.kt"); + doTest(fileName); + } + + @TestMetadata("beforeSuperCallNoClass.kt") + public void testSuperCallNoClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeSuperCallNoClass.kt"); + doTest(fileName); + } + + @TestMetadata("beforeThisCall.kt") + public void testThisCall() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeThisCall.kt"); + doTest(fileName); + } + @TestMetadata("beforeWrongExpectedType.kt") public void testWrongExpectedType() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeWrongExpectedType.kt");