From e9e8be5b4e71e8be79aa9de54caf3670c7327968 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Thu, 26 Mar 2015 13:44:46 +0300 Subject: [PATCH] Create from Usage: Generate secondary constructors by delegator to super-calls --- .../idea/quickfix/QuickFixRegistrar.java | 3 + ...orFromDelegatorToSuperCallActionFactory.kt | 64 +++++++++++++++++++ .../afterDelegatorToSuperCall.kt | 12 ++++ .../beforeDelegatorToSuperCall.kt | 9 +++ .../beforeDelegatorToSuperCallNoClass.kt | 10 +++ ...oSuperCallJavaClass.after.data.Sample.java | 6 ++ .../delegatorToSuperCallJavaClass.after.kt | 6 ++ ...legatorToSuperCallJavaClass.before.Main.kt | 6 ++ ...SuperCallJavaClass.before.data.Sample.java | 3 + .../QuickFixMultiFileTestGenerated.java | 6 ++ .../idea/quickfix/QuickFixTestGenerated.java | 12 ++++ 11 files changed, 137 insertions(+) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegatorToSuperCallActionFactory.kt create mode 100644 idea/testData/quickfix/createFromUsage/createSecondaryConstructor/afterDelegatorToSuperCall.kt create mode 100644 idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeDelegatorToSuperCall.kt create mode 100644 idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeDelegatorToSuperCallNoClass.kt create mode 100644 idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.after.data.Sample.java create mode 100644 idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.after.kt create mode 100644 idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.before.Main.kt create mode 100644 idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.before.data.Sample.java diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java index 3908d3e8550..8acf6e79913 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java @@ -261,6 +261,9 @@ public class QuickFixRegistrar { QuickFixes.factories.put(NO_VALUE_FOR_PARAMETER, CreateConstructorFromDelegationCallActionFactory.INSTANCE$); QuickFixes.factories.put(TOO_MANY_ARGUMENTS, CreateConstructorFromDelegationCallActionFactory.INSTANCE$); + QuickFixes.factories.put(NO_VALUE_FOR_PARAMETER, CreateConstructorFromDelegatorToSuperCallActionFactory.INSTANCE$); + QuickFixes.factories.put(TOO_MANY_ARGUMENTS, CreateConstructorFromDelegatorToSuperCallActionFactory.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/CreateConstructorFromDelegatorToSuperCallActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegatorToSuperCallActionFactory.kt new file mode 100644 index 00000000000..87781ee98ec --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegatorToSuperCallActionFactory.kt @@ -0,0 +1,64 @@ +/* + * 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.analyze +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.JetDelegatorToSuperCall +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.types.Variance + +object CreateConstructorFromDelegatorToSuperCallActionFactory : JetSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val delegationCall = diagnostic.getPsiElement().getStrictParentOfType() ?: return null + val typeReference = delegationCall.getCalleeExpression().getTypeReference() ?: return null + + val project = delegationCall.getProject() + + val superType = typeReference.analyze()[BindingContext.TYPE, typeReference] ?: return null + val superClassDescriptor = superType.getConstructor().getDeclarationDescriptor() as? ClassDescriptor ?: return null + if (superClassDescriptor.getKind() != ClassKind.CLASS) return null + val targetClass = 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/afterDelegatorToSuperCall.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/afterDelegatorToSuperCall.kt new file mode 100644 index 00000000000..c595c19452d --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/afterDelegatorToSuperCall.kt @@ -0,0 +1,12 @@ +// "Create secondary constructor" "true" + +open class A { + constructor(i: Int) { + //To change body of created constructors use File | Settings | File Templates. + } + +} + +class B: A(1) { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeDelegatorToSuperCall.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeDelegatorToSuperCall.kt new file mode 100644 index 00000000000..a6681b970ee --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeDelegatorToSuperCall.kt @@ -0,0 +1,9 @@ +// "Create secondary constructor" "true" + +open class A { + +} + +class B: A(1) { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeDelegatorToSuperCallNoClass.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeDelegatorToSuperCallNoClass.kt new file mode 100644 index 00000000000..de9d2eab2f9 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeDelegatorToSuperCallNoClass.kt @@ -0,0 +1,10 @@ +// "Create secondary constructor" "false" +// ERROR: This class does not have a constructor + +trait T { + +} + +class A: T(1) { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.after.data.Sample.java b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.after.data.Sample.java new file mode 100644 index 00000000000..34daf278dc2 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.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/delegatorToSuperCallJavaClass.after.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.after.kt new file mode 100644 index 00000000000..3b2f74c7868 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.after.kt @@ -0,0 +1,6 @@ +// "Create secondary constructor" "true" +// ERROR: Too many arguments for public/*package*/ constructor J() defined in J + +class B: J(1) { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.before.Main.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.before.Main.kt new file mode 100644 index 00000000000..f2c42eae46a --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.before.Main.kt @@ -0,0 +1,6 @@ +// "Create secondary constructor" "true" +// ERROR: Too many arguments for public/*package*/ constructor J() defined in J + +class B: J(1) { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.before.data.Sample.java b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.before.data.Sample.java new file mode 100644 index 00000000000..cfa2a621482 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.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 e1a098db3b1..e29344657bb 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -765,6 +765,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createSecondaryConstructor"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true); } + @TestMetadata("delegatorToSuperCallJavaClass.before.Main.kt") + public void testDelegatorToSuperCallJavaClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/delegatorToSuperCallJavaClass.before.Main.kt"); + doTestWithExtraFile(fileName); + } + @TestMetadata("groovyConstructor.before.Main.kt") public void testGroovyConstructor() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/groovyConstructor.before.Main.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index f8f76ab56ef..849f0bfcd11 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -2299,6 +2299,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("beforeDelegatorToSuperCall.kt") + public void testDelegatorToSuperCall() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeDelegatorToSuperCall.kt"); + doTest(fileName); + } + + @TestMetadata("beforeDelegatorToSuperCallNoClass.kt") + public void testDelegatorToSuperCallNoClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeDelegatorToSuperCallNoClass.kt"); + doTest(fileName); + } + @TestMetadata("beforeSuperCall.kt") public void testSuperCall() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeSuperCall.kt");