Refactored "Change to constructor invocation" and new "Add constructor parameters and use them" quickfixes into one factory, making the first one always available

This commit is contained in:
Valentin Kipyatkov
2015-05-08 20:27:39 +03:00
parent cd419c2ddf
commit a5dabac296
10 changed files with 136 additions and 207 deletions
@@ -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}
@@ -28,8 +28,8 @@ public abstract class JetIntentionActionsFactory {
public fun createActions(diagnostic: Diagnostic): List<IntentionAction> {
if (diagnostic.getPsiElement().getContainingFile() is JetCodeFragment && !isApplicableForCodeFragment()) {
return Collections.emptyList()
return emptyList()
}
return doCreateActions(diagnostic) ?: Collections.emptyList()
return doCreateActions(diagnostic) ?: emptyList()
}
}
@@ -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<JetDelegatorToSuperClass>(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<String>()
val typeRefsToShorten = ArrayList<JetTypeReference>()
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<JetDelegatorToSuperClass>? {
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)
}
}
}
@@ -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<JetDelegatorToSuperClass> {
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<JetDelegationSpecifier> 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<JetDelegatorToSuperClass> createAction(Diagnostic diagnostic) {
if (diagnostic.getPsiElement() instanceof JetDelegatorToSuperClass) {
return new ChangeToConstructorInvocationFix((JetDelegatorToSuperClass) diagnostic.getPsiElement());
}
return null;
}
};
}
}
@@ -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);
}
}
@@ -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<IntentionAction>? {
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<IntentionAction>()
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<JetDelegatorToSuperClass>(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<JetDelegatorToSuperClass>(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<String>()
val typeRefsToShorten = ArrayList<JetTypeReference>()
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)
}
}
}
@@ -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
@@ -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<caret> {}
@@ -0,0 +1,4 @@
// "Change to constructor invocation" "true"
// ERROR: No value passed for parameter x
open class A(x : Int) {}
class B : A(<caret>) {}
@@ -1,3 +1,3 @@
// "Change to constructor invocation" "true"
open class A(x : Int = 42, vararg y : Int) {}
class B() : A<caret>() {}
class B() : A(<caret>) {}