diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/impl/FunctionDescriptorUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/impl/FunctionDescriptorUtil.java index bfc76037343..1384d15a7b6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/impl/FunctionDescriptorUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/impl/FunctionDescriptorUtil.java @@ -21,7 +21,6 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.TraceBasedRedeclarationHandler; -import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; @@ -49,6 +48,9 @@ public class FunctionDescriptorUtil { } }); + private FunctionDescriptorUtil() { + } + public static Map createSubstitutionContext(@NotNull FunctionDescriptor functionDescriptor, List typeArguments) { if (functionDescriptor.getTypeParameters().isEmpty()) return Collections.emptyMap(); @@ -137,4 +139,47 @@ public class FunctionDescriptorUtil { public static D alphaConvertTypeParameters(D candidate) { return (D) candidate.substitute(MAKE_TYPE_PARAMETERS_FRESH); } + + /** + * Returns function's copy with new parameter list. Note that parameters may belong to other methods or have incorrect "index" property + * -- it will be fixed by this function. + */ + @NotNull + public static FunctionDescriptor replaceFunctionParameters( + @NotNull FunctionDescriptor function, + @NotNull List newParameters + ) { + FunctionDescriptorImpl descriptor = new SimpleFunctionDescriptorImpl( + function.getContainingDeclaration(), + function.getAnnotations(), + function.getName(), + function.getKind()); + List parameters = new ArrayList(newParameters.size()); + int idx = 0; + for (ValueParameterDescriptor parameter : newParameters) { + JetType returnType = parameter.getReturnType(); + assert returnType != null; + + parameters.add(new ValueParameterDescriptorImpl( + descriptor, + idx, + parameter.getAnnotations(), + parameter.getName(), + returnType, + parameter.declaresDefaultValue(), + parameter.getVarargElementType()) + ); + idx++; + } + ReceiverParameterDescriptor receiver = function.getReceiverParameter(); + descriptor.initialize( + receiver == null ? null : receiver.getType(), + function.getExpectedThisObject(), + function.getTypeParameters(), + parameters, + function.getReturnType(), + function.getModality(), + function.getVisibility()); + return descriptor; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java index 1cdb458e755..ce6fce5853b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java @@ -240,24 +240,12 @@ public class OverrideResolver { ) { Queue fromSuperQueue = new LinkedList(notOverridden); while (!fromSuperQueue.isEmpty()) { - CallableMemberDescriptor notOverriddenFromSuper = findMemberWithMaxVisibility(fromSuperQueue); + CallableMemberDescriptor notOverriddenFromSuper = VisibilityUtil.findMemberWithMaxVisibility(fromSuperQueue); Collection overridables = extractMembersOverridableBy(notOverriddenFromSuper, fromSuperQueue, sink); createAndBindFakeOverride(notOverriddenFromSuper, overridables, current, sink); } } - @NotNull - private static CallableMemberDescriptor findMemberWithMaxVisibility(@NotNull Queue descriptors) { - CallableMemberDescriptor descriptor = descriptors.element(); - for (CallableMemberDescriptor candidate : descriptors) { - Integer result = Visibilities.compare(descriptor.getVisibility(), candidate.getVisibility()); - if (result != null && result < 0) { - descriptor = candidate; - } - } - return descriptor; - } - private static void createAndBindFakeOverride( @NotNull CallableMemberDescriptor notOverriddenFromSuper, @NotNull Collection overridables, diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/VisibilityUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/VisibilityUtil.java new file mode 100644 index 00000000000..06da68f996d --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/VisibilityUtil.java @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2013 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.jet.lang.resolve; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor; +import org.jetbrains.jet.lang.descriptors.Visibilities; + +import java.util.Queue; + +public class VisibilityUtil { + @NotNull + public static CallableMemberDescriptor findMemberWithMaxVisibility(@NotNull Queue descriptors) { + CallableMemberDescriptor descriptor = descriptors.element(); + for (CallableMemberDescriptor candidate : descriptors) { + Integer result = Visibilities.compare(descriptor.getVisibility(), candidate.getVisibility()); + if (result != null && result < 0) { + descriptor = candidate; + } + } + return descriptor; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties index 30011027704..acc5d5bd7f3 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties +++ b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties @@ -144,11 +144,15 @@ surround.with.function.template={ } surround.with.cannot.perform.action=Cannot perform Surround With action to the current contextsurround.with.function.template={ } remove.variable.family.name=Remove variable remove.variable.action=Remove variable ''{0}'' - kotlin.code.transformations=Kotlin Code Transformations transform.if.statement.with.assignments.to.expression=Transform 'if' statement with assignments to expression transform.assignment.with.if.expression.to.statement=Transform assignment with 'if' expression to statement transform.if.statement.with.assignments.to.expression.family=Transform 'if' Statement with Assignments to Expression transform.assignment.with.if.expression.to.statement.family=Transform Assignment with 'if' Expression to Statement +change.function.signature.action.single=Change function signature to ''{0}'' +change.function.signature.action.multiple=Change function signature... +change.function.signature.family=Change function signature +change.function.signature.chooser.title=Choose signature +change.function.signature.action=Change function signature remove.unnecessary.parentheses=Remove unnecessary parentheses remove.unnecessary.parentheses.family=Remove Unnecessary Parentheses \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/actions/JetChangeFunctionSignatureAction.java b/idea/src/org/jetbrains/jet/plugin/actions/JetChangeFunctionSignatureAction.java new file mode 100644 index 00000000000..80b4baf9e3f --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/actions/JetChangeFunctionSignatureAction.java @@ -0,0 +1,165 @@ +/* + * Copyright 2010-2013 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.jet.plugin.actions; + +import com.intellij.codeInsight.hint.QuestionAction; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.util.PlatformIcons; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetNamedFunction; +import org.jetbrains.jet.lang.psi.JetPsiFactory; +import org.jetbrains.jet.plugin.JetBundle; +import org.jetbrains.jet.plugin.codeInsight.OverrideUtil; +import org.jetbrains.jet.plugin.codeInsight.ReferenceToClassesShortening; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Changes method signature to one of provided signatures. + * Based on {@link JetAddImportAction} + */ +public class JetChangeFunctionSignatureAction implements QuestionAction { + + private final Project project; + private final Editor editor; + private final JetNamedFunction element; + private final List signatures; + + /** + * @param project Project where action takes place. + * @param editor Editor where modification should be done. + * @param element Function element which signature should be changed. + * @param signatures Variants for new function signature. + */ + public JetChangeFunctionSignatureAction( + @NotNull Project project, + @NotNull Editor editor, + @NotNull JetNamedFunction element, + @NotNull Collection signatures + ) { + this.project = project; + this.editor = editor; + this.element = element; + this.signatures = new ArrayList(signatures); + } + + @Override + public boolean execute() { + PsiDocumentManager.getInstance(project).commitAllDocuments(); + + if (!element.isValid() || signatures.isEmpty()) { + return false; + } + + if (signatures.size() == 1 || !editor.getComponent().isShowing()) { + changeSignature(element, project, signatures.get(0)); + } + else { + chooseSignatureAndChange(); + } + + return true; + } + + private BaseListPopupStep getSignaturePopup() { + return new BaseListPopupStep( + JetBundle.message("change.function.signature.chooser.title"), signatures) { + @Override + public boolean isAutoSelectionEnabled() { + return false; + } + + @Override + public PopupStep onChosen(FunctionDescriptor selectedValue, boolean finalChoice) { + if (finalChoice) { + changeSignature(element, project, selectedValue); + } + return FINAL_CHOICE; + } + + @Override + public Icon getIconFor(FunctionDescriptor aValue) { + return PlatformIcons.FUNCTION_ICON; + } + + @NotNull + @Override + public String getTextFor(FunctionDescriptor aValue) { + return OverrideUtil.createOverridenFunctionSignatureStringFromDescriptor( + project, + aValue, + /* shortTypeNames = */ true); + } + }; + } + + private static void changeSignature(final JetNamedFunction element, final Project project, FunctionDescriptor signature) { + final String signatureString = OverrideUtil.createOverridenFunctionSignatureStringFromDescriptor( + project, + signature, + /* shortTypeNames = */ false); + + PsiDocumentManager.getInstance(project).commitAllDocuments(); + + CommandProcessor.getInstance().executeCommand(project, new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + JetExpression bodyExpression = element.getBodyExpression(); + JetNamedFunction newElement; + + if (bodyExpression != null) { + if (element.hasBlockBody()) { + newElement = JetPsiFactory.createFunction(project, signatureString + "{}"); + } + else { + newElement = JetPsiFactory.createFunction(project, signatureString + "= \"dummy\""); + } + JetExpression newBodyExpression = newElement.getBodyExpression(); + assert newBodyExpression != null; + newBodyExpression.replace(bodyExpression); + } + else { + newElement = JetPsiFactory.createFunction(project, signatureString); + } + newElement = (JetNamedFunction) element.replace(newElement); + ReferenceToClassesShortening.compactReferenceToClasses(Collections.singletonList(newElement)); + } + }); + } + }, JetBundle.message("change.function.signature.action"), null); + } + + private void chooseSignatureAndChange() { + JBPopupFactory.getInstance().createListPopup(getSignaturePopup()).showInBestPositionFor(editor); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideImplementMethodsHandler.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideImplementMethodsHandler.java index 5f006e202bf..d76479eb139 100644 --- a/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideImplementMethodsHandler.java +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideImplementMethodsHandler.java @@ -24,21 +24,20 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; +import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor; import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.plugin.project.WholeProjectAnalyzerFacade; -import org.jetbrains.jet.renderer.DescriptorRenderer; import java.util.ArrayList; import java.util.Collections; @@ -119,140 +118,18 @@ public abstract class OverrideImplementMethodsHandler implements LanguageCodeIns for (DescriptorClassMember selectedElement : selectedElements) { DeclarationDescriptor descriptor = selectedElement.getDescriptor(); if (descriptor instanceof SimpleFunctionDescriptor) { - overridingMembers.add(overrideFunction(file.getProject(), (SimpleFunctionDescriptor) descriptor)); + overridingMembers.add(OverrideUtil.createOverridenFunctionElementFromDescriptor(file.getProject(), + (SimpleFunctionDescriptor) descriptor, /* shortTypeNames = */ + false)); } else if (descriptor instanceof PropertyDescriptor) { - overridingMembers.add(overrideProperty(file.getProject(), (PropertyDescriptor) descriptor)); + overridingMembers.add( + OverrideUtil.createOverridenPropertyElementFromDescriptor(file.getProject(), (PropertyDescriptor) descriptor)); } } return overridingMembers; } - private static JetElement overrideProperty(Project project, PropertyDescriptor descriptor) { - StringBuilder bodyBuilder = new StringBuilder(); - bodyBuilder.append(displayableVisibility(descriptor)).append("override "); - if (descriptor.isVar()) { - bodyBuilder.append("var "); - } - else { - bodyBuilder.append("val "); - } - - addReceiverParameter(descriptor, bodyBuilder); - - bodyBuilder.append(descriptor.getName()).append(" : ").append( - DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor.getType())); - String initializer = CodeInsightUtils.defaultInitializer(descriptor.getType()); - if (initializer != null) { - bodyBuilder.append(" = ").append(initializer); - } - else { - bodyBuilder.append(" = ?"); - } - return JetPsiFactory.createProperty(project, bodyBuilder.toString()); - } - - private static String renderType(JetType type) { - return DescriptorRenderer.TEXT.renderType(type); - } - - private static JetElement overrideFunction(Project project, SimpleFunctionDescriptor descriptor) { - StringBuilder bodyBuilder = new StringBuilder(); - bodyBuilder.append(displayableVisibility(descriptor)); - bodyBuilder.append("override fun "); - - List whereRestrictions = new ArrayList(); - if (!descriptor.getTypeParameters().isEmpty()) { - bodyBuilder.append("<"); - boolean first = true; - for (TypeParameterDescriptor param : descriptor.getTypeParameters()) { - if (!first) { - bodyBuilder.append(", "); - } - - bodyBuilder.append(param.getName()); - Set upperBounds = param.getUpperBounds(); - if (!upperBounds.isEmpty()) { - boolean firstUpperBound = true; - for (JetType upperBound : upperBounds) { - String upperBoundText = " : " + renderType(upperBound); - if (!KotlinBuiltIns.getInstance().getDefaultBound().equals(upperBound)) { - if (firstUpperBound) { - bodyBuilder.append(upperBoundText); - } - else { - whereRestrictions.add(param.getName() + upperBoundText); - } - } - firstUpperBound = false; - } - } - - first = false; - } - bodyBuilder.append("> "); - } - - addReceiverParameter(descriptor, bodyBuilder); - - bodyBuilder.append(descriptor.getName()).append("("); - boolean isAbstractFun = descriptor.getModality() == Modality.ABSTRACT; - StringBuilder delegationBuilder = new StringBuilder(); - if (isAbstractFun) { - delegationBuilder.append("throw UnsupportedOperationException()"); - } - else { - delegationBuilder.append("super<").append(descriptor.getContainingDeclaration().getName()); - delegationBuilder.append(">.").append(descriptor.getName()).append("("); - } - boolean first = true; - for (ValueParameterDescriptor parameterDescriptor : descriptor.getValueParameters()) { - if (!first) { - bodyBuilder.append(","); - if (!isAbstractFun) { - delegationBuilder.append(","); - } - } - first = false; - bodyBuilder.append(parameterDescriptor.getName()); - bodyBuilder.append(" : "); - bodyBuilder.append(renderType(parameterDescriptor.getType())); - - if (!isAbstractFun) { - delegationBuilder.append(parameterDescriptor.getName()); - } - } - bodyBuilder.append(")"); - if (!isAbstractFun) { - delegationBuilder.append(")"); - } - JetType returnType = descriptor.getReturnType(); - KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance(); - - boolean returnsNotUnit = returnType != null && !builtIns.getUnitType().equals(returnType); - if (returnsNotUnit) { - bodyBuilder.append(" : ").append(renderType(returnType)); - } - if (!whereRestrictions.isEmpty()) { - bodyBuilder.append("\n").append("where ").append(StringUtil.join(whereRestrictions, ", ")); - } - bodyBuilder.append("{").append(returnsNotUnit && !isAbstractFun ? "return " : "").append(delegationBuilder.toString()).append("}"); - - return JetPsiFactory.createFunction(project, bodyBuilder.toString()); - } - - private static void addReceiverParameter(CallableDescriptor descriptor, StringBuilder bodyBuilder) { - ReceiverParameterDescriptor receiverParameter = descriptor.getReceiverParameter(); - if (receiverParameter != null) { - bodyBuilder.append(receiverParameter.getType()).append("."); - } - } - - private static String displayableVisibility(MemberDescriptor descriptor) { - Visibility visibility = descriptor.getVisibility().normalize(); - return visibility != Visibilities.INTERNAL ? visibility.toString() + " " : ""; - } - @NotNull public Set collectMethodsToGenerate(@NotNull JetClassOrObject classOrObject, BindingContext bindingContext) { DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject); diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideUtil.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideUtil.java new file mode 100644 index 00000000000..7d02f7b9532 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideUtil.java @@ -0,0 +1,184 @@ +/* + * Copyright 2010-2013 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.jet.plugin.codeInsight; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.psi.JetElement; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetNamedFunction; +import org.jetbrains.jet.lang.psi.JetPsiFactory; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; +import org.jetbrains.jet.renderer.DescriptorRenderer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class OverrideUtil { + + private OverrideUtil() { + } + + @NotNull + public static JetElement createOverridenPropertyElementFromDescriptor(@NotNull Project project, @NotNull PropertyDescriptor descriptor) { + StringBuilder bodyBuilder = new StringBuilder(); + bodyBuilder.append(displayableVisibility(descriptor)).append("override "); + if (descriptor.isVar()) { + bodyBuilder.append("var "); + } + else { + bodyBuilder.append("val "); + } + + addReceiverParameter(descriptor, bodyBuilder); + + bodyBuilder.append(descriptor.getName()).append(" : ").append( + DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor.getType())); + String initializer = CodeInsightUtils.defaultInitializer(descriptor.getType()); + if (initializer != null) { + bodyBuilder.append(" = ").append(initializer); + } + else { + bodyBuilder.append(" = ?"); + } + return JetPsiFactory.createProperty(project, bodyBuilder.toString()); + } + + private static String displayableVisibility(MemberDescriptor descriptor) { + Visibility visibility = descriptor.getVisibility().normalize(); + return visibility != Visibilities.INTERNAL ? visibility.toString() + " ": ""; + } + + private static String renderType(JetType type, boolean shortNames) { + if (shortNames) return DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(type); + else return DescriptorRenderer.TEXT.renderType(type); + } + + private static void addReceiverParameter(CallableDescriptor descriptor, StringBuilder bodyBuilder) { + ReceiverParameterDescriptor receiverParameter = descriptor.getReceiverParameter(); + if (receiverParameter != null) { + bodyBuilder.append(receiverParameter.getType()).append("."); + } + } + + @NotNull + public static String createOverridenFunctionSignatureStringFromDescriptor( + @NotNull Project project, + @NotNull FunctionDescriptor descriptor, + boolean shortTypeNames + ) { + JetNamedFunction functionElement = createOverridenFunctionElementFromDescriptor(project, descriptor, shortTypeNames); + JetExpression bodyExpression = functionElement.getBodyExpression(); + assert bodyExpression != null : "createOverridenFunctionElementFromDescriptor should always return function with body."; + bodyExpression.replace(JetPsiFactory.createWhiteSpace(project)); + return functionElement.getText().trim(); + } + + @NotNull + public static JetNamedFunction createOverridenFunctionElementFromDescriptor( + @NotNull Project project, + @NotNull FunctionDescriptor descriptor, + boolean shortNames + ) { + StringBuilder bodyBuilder = new StringBuilder(); + bodyBuilder.append(displayableVisibility(descriptor)); + bodyBuilder.append("override fun "); + + List whereRestrictions = new ArrayList(); + if (!descriptor.getTypeParameters().isEmpty()) { + bodyBuilder.append("<"); + boolean first = true; + for (TypeParameterDescriptor param : descriptor.getTypeParameters()) { + if (!first) { + bodyBuilder.append(", "); + } + + bodyBuilder.append(param.getName()); + Set upperBounds = param.getUpperBounds(); + if (!upperBounds.isEmpty()) { + boolean firstUpperBound = true; + for (JetType upperBound : upperBounds) { + String upperBoundText = ": " + renderType(upperBound, shortNames); + if (!KotlinBuiltIns.getInstance().getDefaultBound().equals(upperBound)) { + if (firstUpperBound) { + bodyBuilder.append(upperBoundText); + } + else { + whereRestrictions.add(param.getName() + upperBoundText); + } + } + firstUpperBound = false; + } + } + + first = false; + } + bodyBuilder.append("> "); + } + + addReceiverParameter(descriptor, bodyBuilder); + + bodyBuilder.append(descriptor.getName()).append("("); + boolean isAbstractFun = descriptor.getModality() == Modality.ABSTRACT; + StringBuilder delegationBuilder = new StringBuilder(); + if (isAbstractFun) { + delegationBuilder.append("throw UnsupportedOperationException()"); + } + else { + delegationBuilder.append("super<").append(descriptor.getContainingDeclaration().getName()); + delegationBuilder.append(">.").append(descriptor.getName()).append("("); + } + boolean first = true; + for (ValueParameterDescriptor parameterDescriptor : descriptor.getValueParameters()) { + if (!first) { + bodyBuilder.append(", "); + if (!isAbstractFun) { + delegationBuilder.append(", "); + } + } + first = false; + bodyBuilder.append(parameterDescriptor.getName()); + bodyBuilder.append(": "); + bodyBuilder.append(renderType(parameterDescriptor.getType(), shortNames)); + + if (!isAbstractFun) { + delegationBuilder.append(parameterDescriptor.getName()); + } + } + bodyBuilder.append(")"); + if (!isAbstractFun) { + delegationBuilder.append(")"); + } + JetType returnType = descriptor.getReturnType(); + KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance(); + + boolean returnsNotUnit = returnType != null && !builtIns.getUnitType().equals(returnType); + if (returnsNotUnit) { + bodyBuilder.append(": ").append(renderType(returnType, shortNames)); + } + if (!whereRestrictions.isEmpty()) { + bodyBuilder.append("\n").append("where ").append(StringUtil.join(whereRestrictions, ", ")); + } + bodyBuilder.append("{").append(returnsNotUnit && !isAbstractFun ? "return " : "").append(delegationBuilder.toString()).append("}"); + + return JetPsiFactory.createFunction(project, bodyBuilder.toString()); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeMemberFunctionSignatureFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeMemberFunctionSignatureFix.java new file mode 100644 index 00000000000..71041cc5448 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeMemberFunctionSignatureFix.java @@ -0,0 +1,276 @@ +/* + * Copyright 2010-2013 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.jet.plugin.quickfix; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Queues; +import com.intellij.codeInsight.hint.HintManager; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.openapi.command.CommandProcessor; +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.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.impl.FunctionDescriptorUtil; +import org.jetbrains.jet.lang.diagnostics.Diagnostic; +import org.jetbrains.jet.lang.psi.JetNamedFunction; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.VisibilityUtil; +import org.jetbrains.jet.lang.resolve.name.Name; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; +import org.jetbrains.jet.plugin.JetBundle; +import org.jetbrains.jet.plugin.actions.JetChangeFunctionSignatureAction; +import org.jetbrains.jet.plugin.caches.resolve.KotlinCacheManagerUtil; +import org.jetbrains.jet.plugin.codeInsight.OverrideUtil; + +import java.util.*; + +/** + * Fix that changes member function's signature to match one of super functions' signatures. + */ +public class ChangeMemberFunctionSignatureFix extends JetHintAction { + private final List possibleSignatures; + + public ChangeMemberFunctionSignatureFix(@NotNull JetNamedFunction element) { + super(element); + this.possibleSignatures = computePossibleSignatures(element); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + return super.isAvailable(project, editor, file) && !possibleSignatures.isEmpty(); + } + + @NotNull + @Override + public String getText() { + if (possibleSignatures.size() == 1) + return JetBundle.message("change.function.signature.action.single", + getFunctionSignatureString( + possibleSignatures.get(0), + /* shortTypeNames = */ true)); + else + return JetBundle.message("change.function.signature.action.multiple"); + } + + @NotNull + private String getFunctionSignatureString(@NotNull FunctionDescriptor functionSignature, boolean shortTypeNames) { + return OverrideUtil.createOverridenFunctionSignatureStringFromDescriptor( + element.getProject(), functionSignature, shortTypeNames); + } + + @NotNull + @Override + public String getFamilyName() { + return JetBundle.message("change.function.signature.family"); + } + + @Override + public void invoke(@NotNull final Project project, @NotNull final Editor editor, PsiFile file) + throws IncorrectOperationException { + CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { + @Override + public void run() { + createAction(project, editor).execute(); + } + }); + } + + @NotNull + private JetChangeFunctionSignatureAction createAction(@NotNull Project project, @NotNull Editor editor) { + return new JetChangeFunctionSignatureAction(project, editor, element, possibleSignatures); + } + + /** + * Computes all the signatures a 'functionElement' could be changed to in order to remove NOTHING_TO_OVERRIDE error. + */ + @NotNull + private List computePossibleSignatures(JetNamedFunction functionElement) { + BindingContext context = KotlinCacheManagerUtil.getDeclarationsFromProject(functionElement).getBindingContext(); + FunctionDescriptor functionDescriptor = context.get(BindingContext.FUNCTION, functionElement); + if (functionDescriptor == null) return Lists.newArrayList(); + List superFunctions = getPossibleSuperFunctionsDescriptors(functionDescriptor); + Map possibleSignatures = Maps.newHashMap(); + for (FunctionDescriptor superFunction : superFunctions) { + if (!superFunction.getKind().isReal()) continue; + FunctionDescriptor signature = changeSignatureToMatch(functionDescriptor, superFunction); + possibleSignatures.put(getFunctionSignatureString(signature, /* shortTypeNames = */ false), signature); + } + return Lists.newArrayList(possibleSignatures.values()); + } + + /** + * Changes function's signature to match superFunction's signature. Returns new descriptor. + */ + private static FunctionDescriptor changeSignatureToMatch(FunctionDescriptor function, FunctionDescriptor superFunction) { + List superParameters = superFunction.getValueParameters(); + List parameters = function.getValueParameters(); + List newParameters = Lists.newArrayList(superParameters); + + // Parameters in superFunction, which are matched in new function signature: + BitSet matched = new BitSet(superParameters.size()); + // Parameters in this function, which are used in new function signature: + BitSet used = new BitSet(superParameters.size()); + + matchParameters(MATCH_NAMES, superParameters, parameters, newParameters, matched, used); + matchParameters(MATCH_TYPES, superParameters, parameters, newParameters, matched, used); + + return FunctionDescriptorUtil.replaceFunctionParameters( + superFunction.copy( + function.getContainingDeclaration(), + Modality.OPEN, + getVisibility(function, superFunction), + CallableMemberDescriptor.Kind.DELEGATION, + /* copyOverrides = */ true), + newParameters); + } + + /** + * Returns new visibility for 'function' modified to override 'superFunction'. + */ + private static Visibility getVisibility(FunctionDescriptor function, FunctionDescriptor superFunction) { + ArrayDeque descriptors = + Queues.newArrayDeque(Arrays.asList(superFunction, function)); + return VisibilityUtil.findMemberWithMaxVisibility(descriptors).getVisibility(); + } + + /** Helper interface for matchParameters(..) method. */ + private interface ParameterChooser { + /** + * Checks if 'parameter' may be used to match 'superParameter'. + * If so, returns (possibly modified) descriptor to be used as the new parameter. + * If not, returns null. + */ + @Nullable + ValueParameterDescriptor choose(@NotNull ValueParameterDescriptor parameter, @NotNull ValueParameterDescriptor superParameter); + } + + private static final ParameterChooser MATCH_NAMES = new ParameterChooser() { + @Nullable + @Override + public ValueParameterDescriptor choose( + @NotNull ValueParameterDescriptor parameter, + @NotNull ValueParameterDescriptor superParameter + ) { + return parameter.getName().equals(superParameter.getName()) ? superParameter : null; + } + }; + + private static final ParameterChooser MATCH_TYPES = new ParameterChooser() { + @Nullable + @Override + public ValueParameterDescriptor choose( + @NotNull ValueParameterDescriptor parameter, + @NotNull ValueParameterDescriptor superParameter + ) { + return JetTypeChecker.INSTANCE.equalTypes(parameter.getType(), superParameter.getType()) ? parameter : null; + } + }; + + /** + * Match function's parameters with super function's parameters using parameterChooser. + * Doesn't have to preserve ordering, parameter names or types. + * @param superParameters - super function's parameters + * @param parameters - function's parameters + * @param newParameters - new parameters (may be modified by this function) + * @param matched - true iff this parameter in super function is matched by some parameter in function (may be modified by this function) + * @param used - true iff this parameter in function is used to match some parameter in super function (may be modified by this function) + */ + private static void matchParameters( + @NotNull ParameterChooser parameterChooser, + @NotNull List superParameters, + @NotNull List parameters, + @NotNull List newParameters, + @NotNull BitSet matched, + @NotNull BitSet used + ) { + for (ValueParameterDescriptor superParameter : superParameters) { + if (!matched.get(superParameter.getIndex())) { + for (ValueParameterDescriptor parameter : parameters) { + ValueParameterDescriptor choice = parameterChooser.choose(parameter, superParameter); + if (!used.get(parameter.getIndex()) && choice != null) { + used.set(parameter.getIndex(), true); + matched.set(superParameter.getIndex(), true); + newParameters.set(superParameter.getIndex(), choice); + break; + } + } + } + } + } + + /** + * Returns all open functions in superclasses which have the same name as 'functionDescriptor' (but possibly + * different parameters/return type). + */ + @NotNull + private static List getPossibleSuperFunctionsDescriptors(@NotNull FunctionDescriptor functionDescriptor) { + DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration(); + List superFunctions = Lists.newArrayList(); + if (!(containingDeclaration instanceof ClassDescriptor)) return superFunctions; + ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration; + + Name name = functionDescriptor.getName(); + for (ClassDescriptor superclass : DescriptorUtils.getSuperclassDescriptors(classDescriptor)) { + JetType type = superclass.getDefaultType(); + JetScope scope = type.getMemberScope(); + for (FunctionDescriptor function : scope.getFunctions(name)) { + if (!function.getKind().isReal()) continue; + if (function.getModality().isOverridable()) + superFunctions.add(function); + } + } + return superFunctions; + } + + @NotNull + public static JetIntentionActionFactory createFactory() { + return new JetIntentionActionFactory() { + @Nullable + @Override + public IntentionAction createAction(Diagnostic diagnostic) { + JetNamedFunction function = QuickFixUtil.getParentElementOfType(diagnostic, JetNamedFunction.class); + return function == null ? null : new ChangeMemberFunctionSignatureFix(function); + } + }; + } + + @Override + public boolean showHint(@NotNull Editor editor) { + if (possibleSignatures.isEmpty()) { + return false; + } + + Project project = editor.getProject(); + if (project == null) { + return false; + } + + if (HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)) { + return false; + } + + return true; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java index e9ad461fbaa..697388a2102 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java @@ -88,6 +88,7 @@ public class QuickFixes { factories.put(NON_MEMBER_FUNCTION_NO_BODY, addFunctionBodyFactory); factories.put(NOTHING_TO_OVERRIDE, RemoveModifierFix.createRemoveModifierFromListOwnerFactory(OVERRIDE_KEYWORD)); + factories.put(NOTHING_TO_OVERRIDE, ChangeMemberFunctionSignatureFix.createFactory()); factories.put(VIRTUAL_MEMBER_HIDDEN, AddModifierFix.createFactory(OVERRIDE_KEYWORD)); factories.put(USELESS_CAST_STATIC_ASSERT_IS_FINE, ReplaceOperationInBinaryExpressionFix.createChangeCastToStaticAssertFactory()); diff --git a/idea/testData/quickfix/override/nothingToOverride/afterAddParameter.kt b/idea/testData/quickfix/override/nothingToOverride/afterAddParameter.kt new file mode 100644 index 00000000000..4d7459cfad4 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterAddParameter.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: Int)'" "true" +trait A { + fun f(a: Int) +} + +class B : A { + override fun f(a: Int) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterAddParameterMultiple.kt b/idea/testData/quickfix/override/nothingToOverride/afterAddParameterMultiple.kt new file mode 100644 index 00000000000..65cd7fa672f --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterAddParameterMultiple.kt @@ -0,0 +1,10 @@ +// "Change function signature..." "true" +// ERROR: Class 'B' must be declared abstract or implement abstract member
abstract fun f(a: jet.String): jet.Unit defined in A +trait A { + fun f(a: Int) + fun f(a: String) +} + +class B : A { + override fun f(a: Int) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterAddParameterPreserveVisibility.kt b/idea/testData/quickfix/override/nothingToOverride/afterAddParameterPreserveVisibility.kt new file mode 100644 index 00000000000..224bddac74d --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterAddParameterPreserveVisibility.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'public override fun f(a: Int)'" "true" +trait A { + fun f(a: Int) +} + +class B : A { + public override fun f(a: Int) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterType.kt b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterType.kt new file mode 100644 index 00000000000..1c1bf701be9 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterType.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: Int)'" "true" +open class A { + open fun f(a: Int) {} +} + +class B : A(){ + override fun f(a: Int) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeAbstractSuperclass.kt b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeAbstractSuperclass.kt new file mode 100644 index 00000000000..03d64186794 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeAbstractSuperclass.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: Int)'" "true" +abstract class A { + abstract fun f(a: Int) +} + +class B : A(){ + override fun f(a: Int) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeInTrait.kt b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeInTrait.kt new file mode 100644 index 00000000000..c4468a9c43f --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeInTrait.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: Int)'" "true" +trait A { + fun f(a: Int) +} + +trait B : A { + override fun f(a: Int) +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeMatchNames.kt b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeMatchNames.kt new file mode 100644 index 00000000000..e843f654da4 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeMatchNames.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(x: Int, t: String, z: Double)'" "true" +open class A { + open fun f(x: Int, y: String, z: Double) {} +} + +class B : A(){ + override fun f(x: Int, t: String, z: Double) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeModifyOrder.kt b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeModifyOrder.kt new file mode 100644 index 00000000000..c280837de4b --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeModifyOrder.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(y: Int, x: String)'" "true" +open class A { + open fun f(a: Int, b: String) {} +} + +class B : A(){ + override fun f(y: Int, x: String) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeOpenOverride.kt b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeOpenOverride.kt new file mode 100644 index 00000000000..2ede413a7a2 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeOpenOverride.kt @@ -0,0 +1,12 @@ +// "Change function signature to 'override fun f()'" "true" +open class A { + open fun f() {} +} + +open class B : A() { + open override fun f() {} +} + +class C : B() { + override fun f() {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeSingleExpressionFunction.kt b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeSingleExpressionFunction.kt new file mode 100644 index 00000000000..b235b9d31df --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeSingleExpressionFunction.kt @@ -0,0 +1,10 @@ +// "Change function signature to 'override fun f(a: Int): Int'" "true" +open class A { + open fun f(a: Int): Int { + return 0 + } +} + +class B : A(){ + override fun f(a: Int): Int = 7 +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeSuperTrait.kt b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeSuperTrait.kt new file mode 100644 index 00000000000..4d7459cfad4 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterChangeParameterTypeSuperTrait.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: Int)'" "true" +trait A { + fun f(a: Int) +} + +class B : A { + override fun f(a: Int) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterChangeReturnType.kt b/idea/testData/quickfix/override/nothingToOverride/afterChangeReturnType.kt new file mode 100644 index 00000000000..141f66eecbf --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterChangeReturnType.kt @@ -0,0 +1,11 @@ +// "Change function signature to 'override fun f(a: Int): Int'" "true" +// ERROR: Type mismatch.
Required:jet.Int
Found:jet.String
+open class A { + open fun f(a: Int): Int = 0 +} + +class B : A(){ + // Note that when parameter types match, RETURN_TYPE_MISMATCH_ON_OVERRIDE error is reported + // and "Change function signature" quickfix is not present. + override fun f(a: Int): Int = "FOO" +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterNoOpenSuperFunction.kt b/idea/testData/quickfix/override/nothingToOverride/afterNoOpenSuperFunction.kt new file mode 100644 index 00000000000..b42fbdf5f47 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterNoOpenSuperFunction.kt @@ -0,0 +1,11 @@ +// "Change function signature to 'override fun f(a: Int)'" "false" +// ERROR: 'f' overrides nothing +// ACTION: Remove 'override' modifier +open class A { + open fun foo() {} + fun f(a: Int) {} +} + +class B : A(){ + override fun f(a: String) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterOverrideJavaMethod.kt b/idea/testData/quickfix/override/nothingToOverride/afterOverrideJavaMethod.kt new file mode 100644 index 00000000000..8ff6a3be928 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterOverrideJavaMethod.kt @@ -0,0 +1,6 @@ +// "Change function signature to 'protected override fun next(p0: Int): Int'" "true" +import java.util.Random + +class MyRandom : Random() { + protected override fun next(p0: Int): Int = 4 +} \ No newline at end of file diff --git a/idea/testData/quickfix/override/afterNothingToOverride.kt b/idea/testData/quickfix/override/nothingToOverride/afterRemoveOverride.kt similarity index 100% rename from idea/testData/quickfix/override/afterNothingToOverride.kt rename to idea/testData/quickfix/override/nothingToOverride/afterRemoveOverride.kt diff --git a/idea/testData/quickfix/override/nothingToOverride/afterRemoveParameter.kt b/idea/testData/quickfix/override/nothingToOverride/afterRemoveParameter.kt new file mode 100644 index 00000000000..44696b02571 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterRemoveParameter.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f()'" "true" +trait A { + fun f() +} + +class B : A { + override fun f() {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/afterRemoveParameterTwoTraits.kt b/idea/testData/quickfix/override/nothingToOverride/afterRemoveParameterTwoTraits.kt new file mode 100644 index 00000000000..0cb7b35402c --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/afterRemoveParameterTwoTraits.kt @@ -0,0 +1,11 @@ +// "Change function signature to 'override fun f()'" "true" +trait A { + fun f() +} +trait B { + fun f() +} + +class C : A, B { + override fun f() {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeAddParameter.kt b/idea/testData/quickfix/override/nothingToOverride/beforeAddParameter.kt new file mode 100644 index 00000000000..d5b66e27daf --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeAddParameter.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: Int)'" "true" +trait A { + fun f(a: Int) +} + +class B : A { + override fun f() {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeAddParameterMultiple.kt b/idea/testData/quickfix/override/nothingToOverride/beforeAddParameterMultiple.kt new file mode 100644 index 00000000000..5bdb7f456c9 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeAddParameterMultiple.kt @@ -0,0 +1,10 @@ +// "Change function signature..." "true" +// ERROR: Class 'B' must be declared abstract or implement abstract member
abstract fun f(a: jet.String): jet.Unit defined in A +trait A { + fun f(a: Int) + fun f(a: String) +} + +class B : A { + override fun f() {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeAddParameterPreserveVisibility.kt b/idea/testData/quickfix/override/nothingToOverride/beforeAddParameterPreserveVisibility.kt new file mode 100644 index 00000000000..a1e1e4146e7 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeAddParameterPreserveVisibility.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'public override fun f(a: Int)'" "true" +trait A { + fun f(a: Int) +} + +class B : A { + public override fun f() {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterType.kt b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterType.kt new file mode 100644 index 00000000000..8904a51e5c3 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterType.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: Int)'" "true" +open class A { + open fun f(a: Int) {} +} + +class B : A(){ + override fun f(a: String) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeAbstractSuperclass.kt b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeAbstractSuperclass.kt new file mode 100644 index 00000000000..c9148e272c6 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeAbstractSuperclass.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: Int)'" "true" +abstract class A { + abstract fun f(a: Int) +} + +class B : A(){ + override fun f(a: String) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeInTrait.kt b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeInTrait.kt new file mode 100644 index 00000000000..8f6da8f7bff --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeInTrait.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: Int)'" "true" +trait A { + fun f(a: Int) +} + +trait B : A { + override fun f(a: String) +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeMatchNames.kt b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeMatchNames.kt new file mode 100644 index 00000000000..87ce6ad8bee --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeMatchNames.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(x: Int, t: String, z: Double)'" "true" +open class A { + open fun f(x: Int, y: String, z: Double) {} +} + +class B : A(){ + override fun f(z: String, x: String, t: String) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeModifyOrder.kt b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeModifyOrder.kt new file mode 100644 index 00000000000..aa8b1ecea47 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeModifyOrder.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(y: Int, x: String)'" "true" +open class A { + open fun f(a: Int, b: String) {} +} + +class B : A(){ + override fun f(x: String, y: Int) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeOpenOverride.kt b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeOpenOverride.kt new file mode 100644 index 00000000000..83a19095b7b --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeOpenOverride.kt @@ -0,0 +1,12 @@ +// "Change function signature to 'override fun f()'" "true" +open class A { + open fun f() {} +} + +open class B : A() { + open override fun f() {} +} + +class C : B() { + override fun f(a : Int) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeSingleExpressionFunction.kt b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeSingleExpressionFunction.kt new file mode 100644 index 00000000000..1c09664622f --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeSingleExpressionFunction.kt @@ -0,0 +1,10 @@ +// "Change function signature to 'override fun f(a: Int): Int'" "true" +open class A { + open fun f(a: Int): Int { + return 0 + } +} + +class B : A(){ + override fun f(a: String): Int = 7 +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeSuperTrait.kt b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeSuperTrait.kt new file mode 100644 index 00000000000..b8b4b0c7be8 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeSuperTrait.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: Int)'" "true" +trait A { + fun f(a: Int) +} + +class B : A { + override fun f(a: String) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeChangeReturnType.kt b/idea/testData/quickfix/override/nothingToOverride/beforeChangeReturnType.kt new file mode 100644 index 00000000000..d785e7bde19 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeChangeReturnType.kt @@ -0,0 +1,11 @@ +// "Change function signature to 'override fun f(a: Int): Int'" "true" +// ERROR: Type mismatch.
Required:jet.Int
Found:jet.String
+open class A { + open fun f(a: Int): Int = 0 +} + +class B : A(){ + // Note that when parameter types match, RETURN_TYPE_MISMATCH_ON_OVERRIDE error is reported + // and "Change function signature" quickfix is not present. + override fun f(a : String): String = "FOO" +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeNoOpenSuperFunction.kt b/idea/testData/quickfix/override/nothingToOverride/beforeNoOpenSuperFunction.kt new file mode 100644 index 00000000000..b42fbdf5f47 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeNoOpenSuperFunction.kt @@ -0,0 +1,11 @@ +// "Change function signature to 'override fun f(a: Int)'" "false" +// ERROR: 'f' overrides nothing +// ACTION: Remove 'override' modifier +open class A { + open fun foo() {} + fun f(a: Int) {} +} + +class B : A(){ + override fun f(a: String) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeOverrideJavaMethod.kt b/idea/testData/quickfix/override/nothingToOverride/beforeOverrideJavaMethod.kt new file mode 100644 index 00000000000..a16bc538fa9 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeOverrideJavaMethod.kt @@ -0,0 +1,6 @@ +// "Change function signature to 'protected override fun next(p0: Int): Int'" "true" +import java.util.Random + +class MyRandom : Random() { + override fun next(): Int = 4 +} \ No newline at end of file diff --git a/idea/testData/quickfix/override/beforeNothingToOverride.kt b/idea/testData/quickfix/override/nothingToOverride/beforeRemoveOverride.kt similarity index 100% rename from idea/testData/quickfix/override/beforeNothingToOverride.kt rename to idea/testData/quickfix/override/nothingToOverride/beforeRemoveOverride.kt diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeRemoveParameter.kt b/idea/testData/quickfix/override/nothingToOverride/beforeRemoveParameter.kt new file mode 100644 index 00000000000..0ddc0348ce8 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeRemoveParameter.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f()'" "true" +trait A { + fun f() +} + +class B : A { + override fun f(a: Int) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/beforeRemoveParameterTwoTraits.kt b/idea/testData/quickfix/override/nothingToOverride/beforeRemoveParameterTwoTraits.kt new file mode 100644 index 00000000000..36567bcab4c --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/beforeRemoveParameterTwoTraits.kt @@ -0,0 +1,11 @@ +// "Change function signature to 'override fun f()'" "true" +trait A { + fun f() +} +trait B { + fun f() +} + +class C : A, B { + override fun f(a: String) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/import.after.kt b/idea/testData/quickfix/override/nothingToOverride/import.after.kt new file mode 100644 index 00000000000..6248ee66df3 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/import.after.kt @@ -0,0 +1,9 @@ +// "Change function signature to 'override fun f(a: A)'" "true" +// ERROR: 'f' overrides nothing +// ERROR: 'f' overrides nothing +import a.B +import a.A + +class BB : B() { + override fun f(a: A) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/import.before.Main.kt b/idea/testData/quickfix/override/nothingToOverride/import.before.Main.kt new file mode 100644 index 00000000000..7b981a0662c --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/import.before.Main.kt @@ -0,0 +1,7 @@ +// "Change function signature to 'override fun f(a: A)'" "true" +// ERROR: 'f' overrides nothing +// ERROR: 'f' overrides nothing +import a.B +class BB : B() { + override fun f() {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/import.before.data.Sample.kt b/idea/testData/quickfix/override/nothingToOverride/import.before.data.Sample.kt new file mode 100644 index 00000000000..9740027e46d --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/import.before.data.Sample.kt @@ -0,0 +1,5 @@ +package a +class A {} +open class B { + override fun f(a: A) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/twoPackages.after.kt b/idea/testData/quickfix/override/nothingToOverride/twoPackages.after.kt new file mode 100644 index 00000000000..2648db8ea96 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/twoPackages.after.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: A)'" "true" +// ERROR: 'f' overrides nothing +// ERROR: 'f' overrides nothing +import a.B +class A {} +class BB : B() { + override fun f(a: a.A) {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/twoPackages.before.Main.kt b/idea/testData/quickfix/override/nothingToOverride/twoPackages.before.Main.kt new file mode 100644 index 00000000000..05a57cbca70 --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/twoPackages.before.Main.kt @@ -0,0 +1,8 @@ +// "Change function signature to 'override fun f(a: A)'" "true" +// ERROR: 'f' overrides nothing +// ERROR: 'f' overrides nothing +import a.B +class A {} +class BB : B() { + override fun f() {} +} diff --git a/idea/testData/quickfix/override/nothingToOverride/twoPackages.before.data.Sample.kt b/idea/testData/quickfix/override/nothingToOverride/twoPackages.before.data.Sample.kt new file mode 100644 index 00000000000..9740027e46d --- /dev/null +++ b/idea/testData/quickfix/override/nothingToOverride/twoPackages.before.data.Sample.kt @@ -0,0 +1,5 @@ +package a +class A {} +open class B { + override fun f(a: A) {} +} diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java index f9fdac26f71..42777588b35 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java @@ -203,15 +203,34 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } @TestMetadata("idea/testData/quickfix/override") - @InnerTestClasses({}) + @InnerTestClasses({Override.NothingToOverride.class}) public static class Override extends AbstractQuickFixMultiFileTest { public void testAllFilesPresentInOverride() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/quickfix/override"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true); } + @TestMetadata("idea/testData/quickfix/override/nothingToOverride") + public static class NothingToOverride extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInNothingToOverride() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/quickfix/override/nothingToOverride"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true); + } + + @TestMetadata("import.before.Main.kt") + public void testImport() throws Exception { + doTestWithExtraFile("idea/testData/quickfix/override/nothingToOverride/import.before.Main.kt"); + } + + @TestMetadata("twoPackages.before.Main.kt") + public void testTwoPackages() throws Exception { + doTestWithExtraFile("idea/testData/quickfix/override/nothingToOverride/twoPackages.before.Main.kt"); + } + + } + public static Test innerSuite() { TestSuite suite = new TestSuite("Override"); suite.addTestSuite(Override.class); + suite.addTestSuite(NothingToOverride.class); return suite; } } diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java index 0f87cf6a5e8..364e4ef93f7 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java @@ -16,17 +16,14 @@ package org.jetbrains.jet.plugin.quickfix; -import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestSuite; - -import java.io.File; -import java.util.regex.Pattern; import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.test.InnerTestClasses; import org.jetbrains.jet.test.TestMetadata; -import org.jetbrains.jet.plugin.quickfix.AbstractQuickFixTest; +import java.io.File; +import java.util.regex.Pattern; /** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @@ -715,7 +712,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } @TestMetadata("idea/testData/quickfix/override") - @InnerTestClasses({Override.TypeMismatchOnOverride.class}) + @InnerTestClasses({Override.NothingToOverride.class, Override.TypeMismatchOnOverride.class}) public static class Override extends AbstractQuickFixTest { public void testAllFilesPresentInOverride() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/quickfix/override"), Pattern.compile("^before(\\w+)\\.kt$"), true); @@ -726,11 +723,6 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest("idea/testData/quickfix/override/beforeChangeToInvocation.kt"); } - @TestMetadata("beforeNothingToOverride.kt") - public void testNothingToOverride() throws Exception { - doTest("idea/testData/quickfix/override/beforeNothingToOverride.kt"); - } - @TestMetadata("beforeOverriddingMultipleFinalMethods.kt") public void testOverriddingMultipleFinalMethods() throws Exception { doTest("idea/testData/quickfix/override/beforeOverriddingMultipleFinalMethods.kt"); @@ -776,6 +768,99 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest("idea/testData/quickfix/override/beforeVirtualMethodHidden.kt"); } + @TestMetadata("idea/testData/quickfix/override/nothingToOverride") + public static class NothingToOverride extends AbstractQuickFixTest { + @TestMetadata("beforeAddParameter.kt") + public void testAddParameter() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeAddParameter.kt"); + } + + @TestMetadata("beforeAddParameterMultiple.kt") + public void testAddParameterMultiple() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeAddParameterMultiple.kt"); + } + + @TestMetadata("beforeAddParameterPreserveVisibility.kt") + public void testAddParameterPreserveVisibility() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeAddParameterPreserveVisibility.kt"); + } + + public void testAllFilesPresentInNothingToOverride() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/quickfix/override/nothingToOverride"), Pattern.compile("^before(\\w+)\\.kt$"), true); + } + + @TestMetadata("beforeChangeParameterType.kt") + public void testChangeParameterType() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterType.kt"); + } + + @TestMetadata("beforeChangeParameterTypeAbstractSuperclass.kt") + public void testChangeParameterTypeAbstractSuperclass() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeAbstractSuperclass.kt"); + } + + @TestMetadata("beforeChangeParameterTypeInTrait.kt") + public void testChangeParameterTypeInTrait() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeInTrait.kt"); + } + + @TestMetadata("beforeChangeParameterTypeMatchNames.kt") + public void testChangeParameterTypeMatchNames() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeMatchNames.kt"); + } + + @TestMetadata("beforeChangeParameterTypeModifyOrder.kt") + public void testChangeParameterTypeModifyOrder() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeModifyOrder.kt"); + } + + @TestMetadata("beforeChangeParameterTypeOpenOverride.kt") + public void testChangeParameterTypeOpenOverride() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeOpenOverride.kt"); + } + + @TestMetadata("beforeChangeParameterTypeSingleExpressionFunction.kt") + public void testChangeParameterTypeSingleExpressionFunction() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeSingleExpressionFunction.kt"); + } + + @TestMetadata("beforeChangeParameterTypeSuperTrait.kt") + public void testChangeParameterTypeSuperTrait() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeChangeParameterTypeSuperTrait.kt"); + } + + @TestMetadata("beforeChangeReturnType.kt") + public void testChangeReturnType() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeChangeReturnType.kt"); + } + + @TestMetadata("beforeNoOpenSuperFunction.kt") + public void testNoOpenSuperFunction() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeNoOpenSuperFunction.kt"); + } + + @TestMetadata("beforeOverrideJavaMethod.kt") + public void testOverrideJavaMethod() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeOverrideJavaMethod.kt"); + } + + @TestMetadata("beforeRemoveOverride.kt") + public void testRemoveOverride() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeRemoveOverride.kt"); + } + + @TestMetadata("beforeRemoveParameter.kt") + public void testRemoveParameter() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeRemoveParameter.kt"); + } + + @TestMetadata("beforeRemoveParameterTwoTraits.kt") + public void testRemoveParameterTwoTraits() throws Exception { + doTest("idea/testData/quickfix/override/nothingToOverride/beforeRemoveParameterTwoTraits.kt"); + } + + } + @TestMetadata("idea/testData/quickfix/override/typeMismatchOnOverride") public static class TypeMismatchOnOverride extends AbstractQuickFixTest { public void testAllFilesPresentInTypeMismatchOnOverride() throws Exception { @@ -827,6 +912,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { public static Test innerSuite() { TestSuite suite = new TestSuite("Override"); suite.addTestSuite(Override.class); + suite.addTestSuite(NothingToOverride.class); suite.addTestSuite(TypeMismatchOnOverride.class); return suite; }