diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties index e674fb6905c..cc90b3490ea 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -1,6 +1,5 @@ #quick fix messages add.function.body=Add function body -change.to.constructor.invocation=Change to constructor invocation add.return.type=Add return type declaration change.accessor.type=Change accessor type change.getter.type=Change getter type to {0} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/JetIntentionActionsFactory.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/JetIntentionActionsFactory.kt index 874042e07a4..8050f798d1d 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/JetIntentionActionsFactory.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/JetIntentionActionsFactory.kt @@ -28,8 +28,8 @@ public abstract class JetIntentionActionsFactory { public fun createActions(diagnostic: Diagnostic): List { if (diagnostic.getPsiElement().getContainingFile() is JetCodeFragment && !isApplicableForCodeFragment()) { - return Collections.emptyList() + return emptyList() } - return doCreateActions(diagnostic) ?: Collections.emptyList() + return doCreateActions(diagnostic) ?: emptyList() } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstructorParametersFromSuperFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstructorParametersFromSuperFix.kt deleted file mode 100644 index 8b03caff767..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstructorParametersFromSuperFix.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 - -import com.intellij.openapi.editor.Editor -import com.intellij.openapi.project.Project -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor -import org.jetbrains.kotlin.diagnostics.Diagnostic -import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade -import org.jetbrains.kotlin.idea.core.isVisible -import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers -import org.jetbrains.kotlin.idea.util.ShortenReferences -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor -import org.jetbrains.kotlin.types.TypeSubstitutor -import org.jetbrains.kotlin.types.TypeUtils -import java.util.ArrayList - -public class AddConstructorParametersFromSuperFix private( - element: JetDelegatorToSuperClass, - val classDeclaration: JetClass, - val superConstructor: ConstructorDescriptor -) : JetIntentionAction(element) { - - override fun getFamilyName() = "Add constructor parameters from superclass" - - override fun getText() = "Add constructor parameters and use them" - - override fun invoke(project: Project, editor: Editor?, file: JetFile) { - val factory = JetPsiFactory(project) - val renderer = IdeDescriptorRenderers.SOURCE_CODE - - val superParameters = superConstructor.getValueParameters() - val parameterNames = ArrayList() - val typeRefsToShorten = ArrayList() - if (!superParameters.isEmpty()) { - val parameterList = classDeclaration.getOrCreatePrimaryConstructorParameterList() - for (parameter in superParameters) { - //TODO: what if type is error? - val name = renderer.renderName(parameter.getName()) - val parameterText = name + ":" + renderer.renderType(parameter.getType()) - val newParameter = parameterList.addParameter(factory.createParameter(parameterText)) - typeRefsToShorten.add(newParameter.getTypeReference()) - parameterNames.add(name) - } - } - - val delegatorCall = factory.createDelegatorToSuperCall(element.getText() + "(" + parameterNames.joinToString(",") + ")") - element.replace(delegatorCall) - - ShortenReferences.DEFAULT.process(typeRefsToShorten) - } - - companion object : JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): JetIntentionAction? { - val delegator = diagnostic.getPsiElement() as JetDelegatorToSuperClass - val classDeclaration = delegator.getParent().getParent() as? JetClass ?: return null - - val typeRef = delegator.getTypeReference() ?: return null - val type = typeRef.analyze()[BindingContext.TYPE, typeRef] ?: return null - if (type.isError()) return null - - val superClass = (type.getConstructor().getDeclarationDescriptor() as? ClassDescriptor) ?: return null - val classDescriptor = delegator.getResolutionFacade().resolveToDescriptor(classDeclaration) as ClassDescriptor - val constructors = superClass.getConstructors().filter { it.isVisible(classDescriptor) } - val constructorToUse = constructors.singleOrNull() - ?: constructors.singleOrNull { it.isPrimary() } //TODO: should we select it automatically in this case? - ?: return null - //TODO: choose among multiple - if (constructorToUse.getValueParameters().isEmpty()) return null - - val superType = classDescriptor.getTypeConstructor().getSupertypes().first { it.getConstructor().getDeclarationDescriptor() == superClass } - val typeArgsMap = superClass.getTypeConstructor().getParameters().zip(superType.getArguments()).toMap() - val substitutor = TypeUtils.makeSubstitutorForTypeParametersMap(typeArgsMap) - val substitutedConstructor = constructorToUse.substitute(substitutor) as ConstructorDescriptor - - return AddConstructorParametersFromSuperFix(delegator, classDeclaration, substitutedConstructor) - } - } -} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToConstructorInvocationFix.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToConstructorInvocationFix.java deleted file mode 100644 index 1b8ce014eb4..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToConstructorInvocationFix.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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; - -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiFile; -import com.intellij.util.IncorrectOperationException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.descriptors.ClassDescriptor; -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor; -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; -import org.jetbrains.kotlin.diagnostics.Diagnostic; -import org.jetbrains.kotlin.idea.JetBundle; -import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; -import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.resolve.BindingContext; -import org.jetbrains.kotlin.types.JetType; -import org.jetbrains.kotlin.types.TypeUtils; - -import java.util.List; - -import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory; - -public class ChangeToConstructorInvocationFix extends JetIntentionAction { - - public ChangeToConstructorInvocationFix(@NotNull JetDelegatorToSuperClass element) { - super(element); - } - - @NotNull - @Override - public String getText() { - return JetBundle.message("change.to.constructor.invocation"); - } - - @NotNull - @Override - public String getFamilyName() { - return JetBundle.message("change.to.constructor.invocation"); - } - - @Override - public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - if (!super.isAvailable(project, editor, file)) { - return false; - } - - JetTypeReference typeReference = element.getTypeReference(); - BindingContext context = ResolvePackage.analyzeFully(typeReference); - JetType supertype = context.get(BindingContext.TYPE, typeReference); - if (supertype == null) return false; - ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(supertype); - if (classDescriptor == null) return false; - for (ConstructorDescriptor constructor : classDescriptor.getConstructors()) { - boolean needsParametersPassed = false; - for (ValueParameterDescriptor parameter : constructor.getValueParameters()) { - needsParametersPassed |= !parameter.hasDefaultValue() && parameter.getVarargElementType() == null; - } - if (!needsParametersPassed) return true; - } - return false; - } - - @Override - public void invoke(@NotNull Project project, Editor editor, JetFile file) throws IncorrectOperationException { - JetDelegatorToSuperClass delegator = (JetDelegatorToSuperClass) element.copy(); - JetClass aClass = JetPsiFactory(file).createClass("class A : " + delegator.getText() + "()"); - List delegationSpecifiers = aClass.getDelegationSpecifiers(); - assert delegationSpecifiers.size() == 1; - JetDelegationSpecifier specifier = delegationSpecifiers.iterator().next(); - element.replace(specifier); - } - - public static JetSingleIntentionActionFactory createFactory() { - return new JetSingleIntentionActionFactory() { - @Override - public JetIntentionAction createAction(Diagnostic diagnostic) { - if (diagnostic.getPsiElement() instanceof JetDelegatorToSuperClass) { - return new ChangeToConstructorInvocationFix((JetDelegatorToSuperClass) diagnostic.getPsiElement()); - } - return null; - } - }; - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java index 439fb67906b..5b0221d9bac 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java @@ -140,7 +140,7 @@ public class QuickFixRegistrar { JetSingleIntentionActionFactory removeImportFixFactory = RemovePsiElementSimpleFix.createRemoveImportFactory(); QuickFixes.factories.put(CONFLICTING_IMPORT, removeImportFixFactory); - QuickFixes.factories.put(SUPERTYPE_NOT_INITIALIZED, ChangeToConstructorInvocationFix.createFactory()); + QuickFixes.factories.put(SUPERTYPE_NOT_INITIALIZED, SuperClassNotInitialized.INSTANCE$); QuickFixes.factories.put(FUNCTION_CALL_EXPECTED, ChangeToFunctionInvocationFix.createFactory()); QuickFixes.factories.put(CANNOT_CHANGE_ACCESS_PRIVILEGE, ChangeVisibilityModifierFix.createFactory()); @@ -325,7 +325,5 @@ public class QuickFixRegistrar { QuickFixes.factories.put(DEPRECATED_ANNOTATION_SYNTAX, DeprecatedAnnotationSyntaxFix.Companion); QuickFixes.factories.put(DEPRECATED_ANNOTATION_SYNTAX, DeprecatedAnnotationSyntaxFix.Companion.createWholeProjectFixFactory()); - - QuickFixes.factories.put(SUPERTYPE_NOT_INITIALIZED, AddConstructorParametersFromSuperFix.Companion); } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/SuperClassNotInitialized.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/SuperClassNotInitialized.kt new file mode 100644 index 00000000000..1c671fd5de3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/SuperClassNotInitialized.kt @@ -0,0 +1,125 @@ +/* + * 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 + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import com.intellij.util.IncorrectOperationException +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.JetBundle +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.core.isVisible +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.idea.util.ShortenReferences +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.replaced +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.types.TypeUtils +import java.util.ArrayList + +public object SuperClassNotInitialized : JetIntentionActionsFactory() { + override fun doCreateActions(diagnostic: Diagnostic): List? { + val delegator = diagnostic.getPsiElement() as JetDelegatorToSuperClass + val classOrObjectDeclaration = delegator.getParent().getParent() as? JetClassOrObject ?: return null + + val typeRef = delegator.getTypeReference() ?: return null + val type = typeRef.analyze()[BindingContext.TYPE, typeRef] ?: return null + if (type.isError()) return null + + val superClass = (type.getConstructor().getDeclarationDescriptor() as? ClassDescriptor) ?: return null + val classDescriptor = delegator.getResolutionFacade().resolveToDescriptor(classOrObjectDeclaration) as ClassDescriptor + val constructors = superClass.getConstructors().filter { it.isVisible(classDescriptor) } + + val fixes = ArrayList() + + fixes.add(AddParenthesisFix(delegator, putCaretIntoParenthesis = constructors.singleOrNull()?.getValueParameters()?.isNotEmpty() ?: true)) + + if (classOrObjectDeclaration is JetClass) { + val constructorToUse = constructors.singleOrNull() + ?: constructors.singleOrNull { it.isPrimary() } //TODO: should we select it automatically in this case? + //TODO: multiple + if (constructorToUse != null && constructorToUse.getValueParameters().isNotEmpty()) { + val superType = classDescriptor.getTypeConstructor().getSupertypes().first { it.getConstructor().getDeclarationDescriptor() == superClass } + val typeArgsMap = superClass.getTypeConstructor().getParameters().zip(superType.getArguments()).toMap() + val substitutor = TypeUtils.makeSubstitutorForTypeParametersMap(typeArgsMap) + val substitutedConstructor = constructorToUse.substitute(substitutor) as ConstructorDescriptor + fixes.add(AddParametersFix(delegator, classOrObjectDeclaration, substitutedConstructor)) + } + } + + return fixes + } + + private class AddParenthesisFix( + element: JetDelegatorToSuperClass, + val putCaretIntoParenthesis: Boolean + ) : JetIntentionAction(element) { + + override fun getFamilyName() = "Change to constructor invocation" //TODO? + + override fun getText() = getFamilyName() + + override fun invoke(project: Project, editor: Editor?, file: JetFile) { + val newSpecifier = element.replaced(JetPsiFactory(project).createDelegatorToSuperCall(element.getText() + "()")) + + if (putCaretIntoParenthesis) { + editor?.moveCaret(newSpecifier.getValueArgumentList()!!.getLeftParenthesis()!!.getTextRange().getEndOffset()) + } + } + } + + private class AddParametersFix( + element: JetDelegatorToSuperClass, + val classDeclaration: JetClass, + val superConstructor: ConstructorDescriptor + ) : JetIntentionAction(element) { + + override fun getFamilyName() = "Add constructor parameters from superclass" + + override fun getText() = "Add constructor parameters and use them" + + override fun invoke(project: Project, editor: Editor?, file: JetFile) { + val factory = JetPsiFactory(project) + val renderer = IdeDescriptorRenderers.SOURCE_CODE + + val superParameters = superConstructor.getValueParameters() + val parameterNames = ArrayList() + val typeRefsToShorten = ArrayList() + if (!superParameters.isEmpty()) { + val parameterList = classDeclaration.getOrCreatePrimaryConstructorParameterList() + for (parameter in superParameters) { + //TODO: what if type is error? + val name = renderer.renderName(parameter.getName()) + val parameterText = name + ":" + renderer.renderType(parameter.getType()) + val newParameter = parameterList.addParameter(factory.createParameter(parameterText)) + typeRefsToShorten.add(newParameter.getTypeReference()) + parameterNames.add(name) + } + } + + val delegatorCall = factory.createDelegatorToSuperCall(element.getText() + "(" + parameterNames.joinToString(",") + ")") + element.replace(delegatorCall) + + ShortenReferences.DEFAULT.process(typeRefsToShorten) + } + } +} diff --git a/idea/testData/quickfix/supertypeInitialization/noParameters.kt b/idea/testData/quickfix/supertypeInitialization/noParameters.kt index 28231c5956e..ccbc2d003ed 100644 --- a/idea/testData/quickfix/supertypeInitialization/noParameters.kt +++ b/idea/testData/quickfix/supertypeInitialization/noParameters.kt @@ -1,4 +1,4 @@ -// "Add constructor parameters and use them" "false" +// "class org.jetbrains.kotlin.idea.quickfix.SuperClassNotInitialized$AddParametersFix" "false" // ACTION: Change to constructor invocation // ERROR: This type has a constructor, and thus must be initialized here open class Base diff --git a/idea/testData/quickfix/supertypeInitialization/supertypeNotInitialized.kt b/idea/testData/quickfix/supertypeInitialization/supertypeNotInitialized.kt index 8ec0c332702..da8772b3258 100644 --- a/idea/testData/quickfix/supertypeInitialization/supertypeNotInitialized.kt +++ b/idea/testData/quickfix/supertypeInitialization/supertypeNotInitialized.kt @@ -1,4 +1,4 @@ -// "class org.jetbrains.kotlin.idea.quickfix.ChangeToConstructorInvocationFix" "false" -// ERROR: This type has a constructor, and thus must be initialized here +// "Change to constructor invocation" "true" +// ERROR: No value passed for parameter x open class A(x : Int) {} class B : A {} diff --git a/idea/testData/quickfix/supertypeInitialization/supertypeNotInitialized.kt.after b/idea/testData/quickfix/supertypeInitialization/supertypeNotInitialized.kt.after new file mode 100644 index 00000000000..d797690d575 --- /dev/null +++ b/idea/testData/quickfix/supertypeInitialization/supertypeNotInitialized.kt.after @@ -0,0 +1,4 @@ +// "Change to constructor invocation" "true" +// ERROR: No value passed for parameter x +open class A(x : Int) {} +class B : A() {} diff --git a/idea/testData/quickfix/supertypeInitialization/supertypeNotInitializedDefaultParameters.kt.after b/idea/testData/quickfix/supertypeInitialization/supertypeNotInitializedDefaultParameters.kt.after index 300130a8557..dda33bcbef2 100644 --- a/idea/testData/quickfix/supertypeInitialization/supertypeNotInitializedDefaultParameters.kt.after +++ b/idea/testData/quickfix/supertypeInitialization/supertypeNotInitializedDefaultParameters.kt.after @@ -1,3 +1,3 @@ // "Change to constructor invocation" "true" open class A(x : Int = 42, vararg y : Int) {} -class B() : A() {} +class B() : A() {}