diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetModifierList.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetModifierList.java
index 9109d52cc7b..963e37911d4 100644
--- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetModifierList.java
+++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetModifierList.java
@@ -18,6 +18,7 @@ package org.jetbrains.jet.lang.psi;
import com.google.common.collect.Lists;
import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes;
@@ -62,6 +63,11 @@ public class JetModifierList extends JetElementImpl {
return getModifierNode(token) != null;
}
+ @Nullable
+ public PsiElement getModifier(JetToken token) {
+ return findChildByType(token);
+ }
+
@Nullable
public ASTNode getModifierNode(JetToken token) {
ASTNode node = getNode().getFirstChildNode();
@@ -71,4 +77,6 @@ public class JetModifierList extends JetElementImpl {
}
return null;
}
+
+
}
diff --git a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java
index 97c4d158a39..f2ec4c9a962 100644
--- a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java
+++ b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java
@@ -402,7 +402,10 @@ public class GenerateTests {
"JetSafeDeleteTestGenerated",
AbstractJetSafeDeleteTest.class,
testModel("idea/testData/safeDelete/deleteClass", "doClassTest"),
- testModel("idea/testData/safeDelete/deleteObject", "doObjectTest")
+ testModel("idea/testData/safeDelete/deleteObject", "doObjectTest"),
+ testModel("idea/testData/safeDelete/deleteFunction", "doFunctionTest"),
+ testModel("idea/testData/safeDelete/deleteFunctionWithJavaUsages", "doFunctionTestWithJava"),
+ testModel("idea/testData/safeDelete/deleteJavaMethod", "doJavaMethodTest")
);
}
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 77ba08121c1..9e8fd2c469c 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -227,7 +227,10 @@
-
+
diff --git a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties
index 47bb855cc04..b2d2d9f78db 100644
--- a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties
+++ b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties
@@ -253,4 +253,9 @@ usageType.function.call = Function call
usageType.extension.receiver.type = Extension receiver type
usageType.super.type.qualifier = Super type qualifier
usageType.receiver = Receiver
-usageType.selector = Selector
\ No newline at end of file
+usageType.selector = Selector
+
+x.in.y={0} in {1}
+x.implements.y={0} in {1} implements {2} in {3}.
+x.overrides.y.in.class.list={0} in {1} overrides functions in the following classes/traits: {2} Do you want to delete (with usage search) the base methods?
+unused.overriding.methods.title=Unused Overriding Methods
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringSupportProvider.java b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringSupportProvider.java
index b814bc4086a..4e976396942 100644
--- a/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringSupportProvider.java
+++ b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringSupportProvider.java
@@ -33,7 +33,7 @@ import org.jetbrains.jet.plugin.refactoring.safeDelete.KotlinSafeDeleteProcessor
public class JetRefactoringSupportProvider extends RefactoringSupportProvider {
@Override
public boolean isSafeDeleteAvailable(PsiElement element) {
- return KotlinSafeDeleteProcessor.checkElement(element);
+ return KotlinSafeDeleteProcessor.canDeleteElement(element);
}
@Override
diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinOverridingMethodsDialog.java b/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinOverridingMethodsDialog.java
new file mode 100644
index 00000000000..f8016dbafce
--- /dev/null
+++ b/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinOverridingMethodsDialog.java
@@ -0,0 +1,295 @@
+/*
+ * 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.refactoring.safeDelete;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.help.HelpManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Splitter;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.psi.PsiMethod;
+import com.intellij.refactoring.HelpID;
+import com.intellij.refactoring.RefactoringBundle;
+import com.intellij.ui.BooleanTableCellRenderer;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.usageView.UsageInfo;
+import com.intellij.usages.impl.UsagePreviewPanel;
+import com.intellij.ui.table.JBTable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
+import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
+import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
+import org.jetbrains.jet.lang.psi.JetFile;
+import org.jetbrains.jet.lang.psi.JetNamedFunction;
+import org.jetbrains.jet.lang.resolve.BindingContext;
+import org.jetbrains.jet.lang.resolve.java.JetClsMethod;
+import org.jetbrains.jet.plugin.JetBundle;
+import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
+import org.jetbrains.jet.renderer.DescriptorRenderer;
+
+import javax.swing.*;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableColumnModel;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/*
+* Mostly copied from com.intellij.refactoring.safeDelete.OverridingMethodsDialog
+* Revision: 14aa2e2
+* (replace PsiMethod formatting)
+*/
+class KotlinOverridingMethodsDialog extends DialogWrapper {
+ private final List myOverridingMethods;
+ private final String[] myMethodText;
+ private final boolean[] myChecked;
+
+ private static final int CHECK_COLUMN = 0;
+ private JBTable myTable;
+ private final UsagePreviewPanel myUsagePreviewPanel;
+
+ public KotlinOverridingMethodsDialog(Project project, List overridingMethods) {
+ super(project, true);
+ myOverridingMethods = overridingMethods;
+ myChecked = new boolean[myOverridingMethods.size()];
+ for (int i = 0; i < myChecked.length; i++) {
+ myChecked[i] = true;
+ }
+
+ myMethodText = new String[myOverridingMethods.size()];
+ for (int i = 0; i < myMethodText.length; i++) {
+ myMethodText[i] = formatMethod(((KotlinSafeDeleteOverridingMethodUsageInfo) myOverridingMethods.get(i)).getOverridingMethod());
+ }
+ myUsagePreviewPanel = new UsagePreviewPanel(project);
+ setTitle(JetBundle.message("unused.overriding.methods.title"));
+ init();
+ }
+
+ private static String formatMethod(PsiMethod method) {
+ if (method instanceof JetClsMethod) {
+ JetNamedFunction function = (JetNamedFunction) ((JetClsMethod) method).getOrigin();
+
+ BindingContext bindingContext =
+ AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) function.getContainingFile()).getBindingContext();
+
+ DeclarationDescriptor declarationDescriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function);
+ if (declarationDescriptor instanceof FunctionDescriptor) {
+ DeclarationDescriptor containingDescriptor = declarationDescriptor.getContainingDeclaration();
+ if (containingDescriptor instanceof ClassDescriptor) {
+ return JetBundle.message(
+ "x.in.y",
+ DescriptorRenderer.COMPACT.render(declarationDescriptor),
+ DescriptorRenderer.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(containingDescriptor)
+ );
+ }
+ }
+ }
+
+ return KotlinSafeDeleteProcessor.formatPsiMethod(method, true, false);
+ }
+
+ @Override
+ protected String getDimensionServiceKey() {
+ return "#org.jetbrains.jet.plugin.refactoring.safeDelete.KotlinOverridingMethodsDialog";
+ }
+
+ public ArrayList getSelected() {
+ ArrayList result = new ArrayList();
+ for (int i = 0; i < myChecked.length; i++) {
+ if (myChecked[i]) {
+ result.add(myOverridingMethods.get(i));
+ }
+ }
+ return result;
+ }
+
+ @NotNull
+ @Override
+ protected Action[] createActions() {
+ return new Action[] {getOKAction(), getCancelAction()};
+ }
+
+ @Override
+ protected void doHelpAction() {
+ HelpManager.getInstance().invokeHelp(HelpID.SAFE_DELETE_OVERRIDING);
+ }
+
+ @Override
+ protected JComponent createNorthPanel() {
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ panel.add(new JLabel(RefactoringBundle.message("there.are.unused.methods.that.override.methods.you.delete")));
+ panel.add(new JLabel(RefactoringBundle.message("choose.the.ones.you.want.to.be.deleted")));
+ return panel;
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myTable;
+ }
+
+ @Override
+ protected void dispose() {
+ Disposer.dispose(myUsagePreviewPanel);
+ super.dispose();
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.setBorder(BorderFactory.createEmptyBorder(8, 0, 4, 0));
+ final MyTableModel tableModel = new MyTableModel();
+ myTable = new JBTable(tableModel);
+ myTable.setShowGrid(false);
+
+ TableColumnModel columnModel = myTable.getColumnModel();
+ int checkBoxWidth = new JCheckBox().getPreferredSize().width;
+ columnModel.getColumn(CHECK_COLUMN).setCellRenderer(new BooleanTableCellRenderer());
+ columnModel.getColumn(CHECK_COLUMN).setMaxWidth(checkBoxWidth);
+ columnModel.getColumn(CHECK_COLUMN).setMinWidth(checkBoxWidth);
+
+
+ // make SPACE check/uncheck selected rows
+ InputMap inputMap = myTable.getInputMap();
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "enable_disable");
+ ActionMap actionMap = myTable.getActionMap();
+ actionMap.put("enable_disable", new AbstractAction() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (myTable.isEditing()) return;
+ int[] rows = myTable.getSelectedRows();
+ if (rows.length > 0) {
+ boolean valueToBeSet = false;
+ for (int row : rows) {
+ if (!myChecked[row]) {
+ valueToBeSet = true;
+ break;
+ }
+ }
+ for (int row : rows) {
+ myChecked[row] = valueToBeSet;
+ }
+
+ tableModel.updateData();
+ }
+ }
+ });
+
+ panel.setLayout(new BorderLayout());
+
+ JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTable);
+
+ panel.add(scrollPane, BorderLayout.CENTER);
+ ListSelectionListener selectionListener = new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ int index = myTable.getSelectionModel().getLeadSelectionIndex();
+ if (index != -1) {
+ UsageInfo usageInfo = myOverridingMethods.get(index);
+ myUsagePreviewPanel.updateLayout(Collections.singletonList(usageInfo));
+ }
+ else {
+ myUsagePreviewPanel.updateLayout(null);
+ }
+ }
+ };
+ myTable.getSelectionModel().addListSelectionListener(selectionListener);
+
+ final Splitter splitter = new Splitter(true, 0.3f);
+ splitter.setFirstComponent(panel);
+ splitter.setSecondComponent(myUsagePreviewPanel);
+ myUsagePreviewPanel.updateLayout(null);
+
+ Disposer.register(myDisposable, new Disposable() {
+ @Override
+ public void dispose() {
+ splitter.dispose();
+ }
+ });
+
+ if (tableModel.getRowCount() != 0) {
+ myTable.getSelectionModel().addSelectionInterval(0, 0);
+ }
+ return splitter;
+ }
+
+ class MyTableModel extends AbstractTableModel {
+ @Override
+ public int getRowCount() {
+ return myChecked.length;
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ switch (column) {
+ case CHECK_COLUMN:
+ return " ";
+ default:
+ return RefactoringBundle.message("method.column");
+ }
+ }
+
+ @Override
+ public Class getColumnClass(int columnIndex) {
+ switch (columnIndex) {
+ case CHECK_COLUMN:
+ return Boolean.class;
+ default:
+ return String.class;
+ }
+ }
+
+
+ @Override
+ public int getColumnCount() {
+ return 2;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ if (columnIndex == CHECK_COLUMN) {
+ return Boolean.valueOf(myChecked[rowIndex]);
+ }
+ else {
+ return myMethodText[rowIndex];
+ }
+ }
+
+ @Override
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ if (columnIndex == CHECK_COLUMN) {
+ myChecked[rowIndex] = ((Boolean) aValue).booleanValue();
+ }
+ }
+
+ @Override
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return columnIndex == CHECK_COLUMN;
+ }
+
+ void updateData() {
+ fireTableDataChanged();
+ }
+ }
+}
+
diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteOverridingMethodUsageInfo.java b/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteOverridingMethodUsageInfo.java
new file mode 100644
index 00000000000..77e48b30ca6
--- /dev/null
+++ b/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteOverridingMethodUsageInfo.java
@@ -0,0 +1,50 @@
+/*
+ * 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.refactoring.safeDelete;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteCustomUsageInfo;
+import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteUsageInfo;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.jet.asJava.LightClassUtil;
+import org.jetbrains.jet.lang.psi.JetNamedFunction;
+
+public class KotlinSafeDeleteOverridingMethodUsageInfo extends SafeDeleteUsageInfo implements SafeDeleteCustomUsageInfo {
+ public KotlinSafeDeleteOverridingMethodUsageInfo(PsiElement overridingMethod, PsiElement method) {
+ super(toPsiMethod(overridingMethod), toPsiMethod(method));
+ }
+
+ protected static PsiMethod toPsiMethod(PsiElement element) {
+ if (element instanceof JetNamedFunction) {
+ element = LightClassUtil.getLightClassMethod((JetNamedFunction) element);
+ }
+ return element instanceof PsiMethod ? (PsiMethod) element : null;
+ }
+
+ public PsiMethod getOverridingMethod() {
+ return toPsiMethod(getElement());
+ }
+
+ @Override
+ public void performRefactoring() throws IncorrectOperationException {
+ PsiElement element = getElement();
+ if (element != null) {
+ element.delete();
+ }
+ }
+}
diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteProcessor.java b/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteProcessor.java
index 1921af9663f..6109a9426e7 100644
--- a/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteProcessor.java
+++ b/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteProcessor.java
@@ -16,37 +16,54 @@
package org.jetbrains.jet.plugin.refactoring.safeDelete;
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiReference;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
+import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
-import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.search.searches.SuperMethodsSearch;
+import com.intellij.psi.util.*;
import com.intellij.refactoring.safeDelete.JavaSafeDeleteProcessor;
import com.intellij.refactoring.safeDelete.NonCodeUsageSearchInfo;
import com.intellij.refactoring.safeDelete.SafeDeleteProcessor;
+import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteOverridingMethodUsageInfo;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceSimpleDeleteUsageInfo;
import com.intellij.usageView.UsageInfo;
-import com.intellij.util.Processor;
+import com.intellij.util.*;
+import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.jetbrains.jet.lang.psi.JetClassOrObject;
-import org.jetbrains.jet.lang.psi.JetImportDirective;
-import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
-import org.jetbrains.jet.lang.psi.JetObjectDeclarationName;
+import org.jetbrains.jet.asJava.LightClassUtil;
+import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
+import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
+import org.jetbrains.jet.lang.descriptors.Modality;
+import org.jetbrains.jet.lang.psi.*;
+import org.jetbrains.jet.lang.resolve.BindingContext;
+import org.jetbrains.jet.lang.resolve.BindingContextUtils;
+import org.jetbrains.jet.lang.resolve.java.JetClsMethod;
+import org.jetbrains.jet.lexer.JetTokens;
+import org.jetbrains.jet.plugin.JetBundle;
+import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
+import org.jetbrains.jet.renderer.DescriptorRenderer;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor {
- public static boolean checkElement(PsiElement element) {
+ public static boolean canDeleteElement(PsiElement element) {
return element instanceof JetClassOrObject
- || element instanceof JetObjectDeclarationName;
+ || element instanceof JetObjectDeclarationName
+ || element instanceof JetNamedFunction
+ || element instanceof PsiMethod;
}
@Override
public boolean handlesElement(PsiElement element) {
- return checkElement(element);
+ return canDeleteElement(element);
}
protected static NonCodeUsageSearchInfo getDefaultNonCodeUsageSearchInfo(
@@ -61,12 +78,25 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor {
if (element instanceof JetClassOrObject) {
return findClassOrObjectUsages(element, (JetClassOrObject) element, allElementsToDelete, result);
}
- if (element instanceof JetObjectDeclarationName && element.getParent() instanceof JetObjectDeclaration) {
- return findClassOrObjectUsages(element, (JetObjectDeclaration) element.getParent(), allElementsToDelete, result);
+ if (element instanceof JetObjectDeclarationName) {
+ PsiElement parent = getObjectDeclarationOrFail(element);
+ return findClassOrObjectUsages(element, (JetObjectDeclaration) parent, allElementsToDelete, result);
+ }
+ if (element instanceof JetNamedFunction) {
+ return findFunctionUsages((JetNamedFunction) element, allElementsToDelete, result);
+ }
+ if (element instanceof PsiMethod) {
+ return findPsiMethodUsages((PsiMethod) element, allElementsToDelete, result);
}
return getDefaultNonCodeUsageSearchInfo(element, allElementsToDelete);
}
+ private static PsiElement getObjectDeclarationOrFail(PsiElement element) {
+ PsiElement parent = element.getParent();
+ assert parent instanceof JetObjectDeclaration;
+ return parent;
+ }
+
@SuppressWarnings("MethodOverridesPrivateMethodOfSuperclass")
protected static boolean isInside(PsiElement place, PsiElement[] ancestors) {
return isInside(place, Arrays.asList(ancestors));
@@ -80,6 +110,14 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor {
return false;
}
+ @SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
+ public static boolean isInside(PsiElement place, PsiElement ancestor) {
+ if (ancestor instanceof JetClsMethod) {
+ ancestor = ((JetClsMethod) ancestor).getOrigin();
+ }
+ return JavaSafeDeleteProcessor.isInside(place, ancestor);
+ }
+
protected static NonCodeUsageSearchInfo findClassOrObjectUsages(
PsiElement referencedElement,
final JetClassOrObject classOrObject,
@@ -107,18 +145,441 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor {
return getDefaultNonCodeUsageSearchInfo(referencedElement, allElementsToDelete);
}
+ protected NonCodeUsageSearchInfo findPsiMethodUsages(
+ PsiMethod method,
+ PsiElement[] allElementsToDelete,
+ List result
+ ) {
+ List javaUsages = new ArrayList();
+ NonCodeUsageSearchInfo searchInfo = super.findUsages(method, allElementsToDelete, javaUsages);
+
+ for (UsageInfo usageInfo : javaUsages) {
+ if (usageInfo instanceof SafeDeleteOverridingMethodUsageInfo) {
+ SafeDeleteOverridingMethodUsageInfo overrideUsageInfo = (SafeDeleteOverridingMethodUsageInfo) usageInfo;
+ usageInfo = new KotlinSafeDeleteOverridingMethodUsageInfo(
+ overrideUsageInfo.getSmartPointer().getElement(), overrideUsageInfo.getReferencedElement()
+ );
+ }
+ result.add(usageInfo);
+ }
+
+ return searchInfo;
+ }
+
+ private static List difference(Collection from, T[] a) {
+ List list = new ArrayList(from);
+ list.removeAll(Arrays.asList(a));
+ return list;
+ }
+
+ protected static NonCodeUsageSearchInfo findFunctionUsages(
+ JetNamedFunction function,
+ final PsiElement[] allElementsToDelete,
+ List result
+ ) {
+ PsiMethod lightMethod = LightClassUtil.getLightClassMethod(function);
+ if (lightMethod == null) {
+ return getDefaultNonCodeUsageSearchInfo(function, allElementsToDelete);
+ }
+
+ Collection references = ReferencesSearch.search(function).findAll();
+ List overridingMethods = difference(OverridingMethodsSearch.search(lightMethod, true).findAll(), allElementsToDelete);
+
+ for (PsiReference reference : references) {
+ PsiElement element = reference.getElement();
+ if (!isInside(element, allElementsToDelete) && !isInside(element, overridingMethods)) {
+ JetImportDirective importDirective = PsiTreeUtil.getParentOfType(element, JetImportDirective.class, false);
+ if (importDirective != null) {
+ result.add(new SafeDeleteImportDirectiveUsageInfo(importDirective, function));
+ }
+ else {
+ result.add(new SafeDeleteReferenceSimpleDeleteUsageInfo(element, function, false));
+ }
+ }
+ }
+
+ HashMap> methodToReferences = new HashMap>();
+ for (PsiMethod overridingMethod : overridingMethods) {
+ Collection overridingReferences =
+ ReferencesSearch.search(
+ overridingMethod instanceof JetClsMethod ? ((JetClsMethod) overridingMethod).getOrigin() : overridingMethod
+ ).findAll();
+ methodToReferences.put(overridingMethod, overridingReferences);
+ }
+ final Set safeOverriding =
+ filterSafeOverridingMethods(lightMethod, references, overridingMethods, methodToReferences, result, allElementsToDelete);
+
+ return new NonCodeUsageSearchInfo(
+ new Condition() {
+ @Override
+ public boolean value(PsiElement usage) {
+ if (usage instanceof JetFile) return false;
+ return isInside(usage, allElementsToDelete) || isInside(usage, safeOverriding);
+ }
+ },
+ function
+ );
+ }
+
+ /*
+ * Mostly copied from JavaSafeDeleteProcessor.validateOverridingMethods
+ * Revision: d4fc033
+ * (simplified and implemented proper treatment of light methods)
+ */
+ private static Set filterSafeOverridingMethods(
+ PsiMethod originalMethod, Collection originalReferences,
+ Collection overridingMethods, HashMap> methodToReferences,
+ List usages,
+ PsiElement[] allElementsToDelete
+ ) {
+ Set validOverriding = new LinkedHashSet(overridingMethods);
+ boolean anyNewBadRefs;
+ do {
+ anyNewBadRefs = false;
+ for (PsiMethod overridingMethod : overridingMethods) {
+ if (validOverriding.contains(overridingMethod)) {
+ Collection overridingReferences = methodToReferences.get(overridingMethod);
+ boolean anyOverridingRefs = false;
+ for (PsiReference overridingReference : overridingReferences) {
+ PsiElement element = overridingReference.getElement();
+ if (!isInside(element, allElementsToDelete) && !isInside(element, validOverriding)) {
+ anyOverridingRefs = true;
+ break;
+ }
+ }
+
+ if (!anyOverridingRefs && isMultipleInterfacesImplementation(overridingMethod, originalMethod, allElementsToDelete)) {
+ anyOverridingRefs = true;
+ }
+
+ if (anyOverridingRefs) {
+ validOverriding.remove(overridingMethod);
+ anyNewBadRefs = true;
+
+ for (PsiReference reference : originalReferences) {
+ PsiElement element = reference.getElement();
+ if (!isInside(element, allElementsToDelete) && !isInside(element, overridingMethods)) {
+ validOverriding.clear();
+ }
+ }
+ }
+ }
+ }
+ }
+ while (anyNewBadRefs && !validOverriding.isEmpty());
+
+ for (PsiMethod method : validOverriding) {
+ if (method != originalMethod) {
+ usages.add(new KotlinSafeDeleteOverridingMethodUsageInfo(method, originalMethod));
+ }
+ }
+
+ return validOverriding;
+ }
+
+ @SuppressWarnings("MethodOverridesPrivateMethodOfSuperclass")
+ private static boolean isMultipleInterfacesImplementation(PsiMethod method, PsiMethod originalMethod, PsiElement[] ignore) {
+ PsiMethod[] methods = method.findSuperMethods();
+ for (PsiMethod superMethod: methods) {
+ PsiElement relevantElement = superMethod instanceof JetClsMethod ? ((JetClsMethod) superMethod).getOrigin() : superMethod;
+ if (ArrayUtilRt.find(ignore, relevantElement) < 0 && !MethodSignatureUtil.isSuperMethod(originalMethod, superMethod)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ private static String wrapOrSkip(String s, boolean inCode) {
+ return inCode ? "" + s + "" : s;
+ }
+
+ private static String formatClass(DeclarationDescriptor classDescriptor, BindingContext bindingContext, boolean inCode) {
+ PsiElement element = BindingContextUtils.descriptorToDeclaration(bindingContext, classDescriptor);
+ if (element instanceof PsiClass) {
+ return formatPsiClass((PsiClass) element, false, inCode);
+ }
+
+ return wrapOrSkip(formatClassDescriptor(classDescriptor), inCode);
+ }
+
+ private static String formatFunction(DeclarationDescriptor functionDescriptor, BindingContext bindingContext, boolean inCode) {
+ PsiElement element = BindingContextUtils.descriptorToDeclaration(bindingContext, functionDescriptor);
+ if (element instanceof PsiMethod) {
+ return formatPsiMethod((PsiMethod) element, false, inCode);
+ }
+
+ return wrapOrSkip(formatFunctionDescriptor(functionDescriptor), inCode);
+ }
+
+ private static String formatClassDescriptor(DeclarationDescriptor classDescriptor) {
+ return DescriptorRenderer.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(classDescriptor);
+ }
+
+ private static String formatFunctionDescriptor(DeclarationDescriptor functionDescriptor) {
+ return DescriptorRenderer.COMPACT.render(functionDescriptor);
+ }
+
+ public static String formatPsiClass(PsiClass psiClass, boolean markAsJava, boolean inCode) {
+ String description;
+
+ String kind = psiClass.isInterface() ? "interface " : "class ";
+ description = kind + PsiFormatUtil.formatClass(
+ psiClass,
+ PsiFormatUtilBase.SHOW_CONTAINING_CLASS
+ | PsiFormatUtilBase.SHOW_NAME
+ | PsiFormatUtilBase.SHOW_PARAMETERS
+ | PsiFormatUtilBase.SHOW_TYPE
+ );
+ description = wrapOrSkip(description, inCode);
+
+ return markAsJava ? "[Java] " + description : description;
+ }
+
+ public static String formatPsiMethod(PsiMethod psiMethod, boolean showContainingClass, boolean inCode) {
+ int options = PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_PARAMETERS | PsiFormatUtilBase.SHOW_TYPE;
+ if (showContainingClass) {
+ options |= PsiFormatUtilBase.SHOW_CONTAINING_CLASS;
+ }
+
+ String description = PsiFormatUtil.formatMethod(psiMethod, PsiSubstitutor.EMPTY, options, PsiFormatUtilBase.SHOW_TYPE);
+ description = wrapOrSkip(description, inCode);
+
+ return "[Java] " + description;
+ }
+
+ @Override
+ public Collection findConflicts(PsiElement element, PsiElement[] allElementsToDelete) {
+ if (element instanceof JetNamedFunction) {
+ JetClass jetClass = PsiTreeUtil.getParentOfType(element, JetClass.class);
+ if (jetClass == null || jetClass.getBody() != element.getParent()) return null;
+
+ JetModifierList modifierList = jetClass.getModifierList();
+ if (modifierList != null && modifierList.hasModifier(JetTokens.ABSTRACT_KEYWORD)) return null;
+
+ BindingContext bindingContext =
+ AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext();
+
+ DeclarationDescriptor declarationDescriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
+ if (!(declarationDescriptor instanceof FunctionDescriptor)) return null;
+
+ List messages = new ArrayList();
+ FunctionDescriptor functionDescriptor = (FunctionDescriptor) declarationDescriptor;
+ for (FunctionDescriptor overridenDescriptor : functionDescriptor.getOverriddenDescriptors()) {
+ if (overridenDescriptor.getModality() == Modality.ABSTRACT) {
+ String message = JetBundle.message(
+ "x.implements.y",
+ formatFunction(functionDescriptor, bindingContext, true),
+ formatClass(functionDescriptor.getContainingDeclaration(), bindingContext, true),
+ formatFunction(overridenDescriptor, bindingContext, true),
+ formatClass(overridenDescriptor.getContainingDeclaration(), bindingContext, true)
+ );
+ messages.add(message);
+ }
+ }
+
+ if (!messages.isEmpty()) return messages;
+ }
+ return super.findConflicts(element, allElementsToDelete);
+ }
+
+ /*
+ * Mostly copied from JavaSafeDeleteProcessor.preprocessUsages
+ * Revision: d4fc033
+ * (replaced original dialog)
+ */
@Nullable
@Override
public UsageInfo[] preprocessUsages(Project project, UsageInfo[] usages) {
- return usages;
+ ArrayList result = new ArrayList();
+ ArrayList overridingMethodUsages = new ArrayList();
+
+ for (UsageInfo usage : usages) {
+ if (usage instanceof KotlinSafeDeleteOverridingMethodUsageInfo) {
+ overridingMethodUsages.add(usage);
+ }
+ else {
+ result.add(usage);
+ }
+ }
+
+ if (!overridingMethodUsages.isEmpty()) {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ result.addAll(overridingMethodUsages);
+ }
+ else {
+ KotlinOverridingMethodsDialog dialog = new KotlinOverridingMethodsDialog(project, overridingMethodUsages);
+ dialog.show();
+ if (!dialog.isOK()) return null;
+ result.addAll(dialog.getSelected());
+ }
+ }
+
+ return result.toArray(new UsageInfo[result.size()]);
+ }
+
+ private static void removeOverrideModifier(@NotNull PsiElement element) {
+ if (element instanceof JetNamedFunction) {
+ JetModifierList modifierList = ((JetNamedFunction) element).getModifierList();
+ if (modifierList == null) return;
+
+ PsiElement overrideModifier = modifierList.getModifier(JetTokens.OVERRIDE_KEYWORD);
+ if (overrideModifier != null) {
+ overrideModifier.delete();
+ }
+ }
+ else if (element instanceof PsiMethod) {
+ PsiMethod method = (PsiMethod) element;
+
+ PsiAnnotation overrideAnnotation = null;
+ for (PsiAnnotation annotation : method.getModifierList().getAnnotations()) {
+ if ("java.lang.Override".equals(annotation.getQualifiedName())) {
+ overrideAnnotation = annotation;
+ break;
+ }
+ }
+
+ if (overrideAnnotation != null) {
+ overrideAnnotation.delete();
+ }
+ }
+ }
+
+ @Override
+ public void prepareForDeletion(PsiElement element) throws IncorrectOperationException {
+ if (element instanceof PsiMethod) {
+ cleanUpOverrides((PsiMethod) element);
+ }
+ else if (element instanceof JetNamedFunction) {
+ PsiMethod lightMethod = LightClassUtil.getLightClassMethod((JetNamedFunction) element);
+ if (lightMethod == null) {
+ return;
+ }
+
+ cleanUpOverrides(lightMethod);
+ }
+ }
+
+ private static void cleanUpOverrides(PsiMethod method) {
+ Collection superMethods =
+ SuperMethodsSearch.search(method, null, true, false).findAll();
+ Collection overridingMethods = OverridingMethodsSearch.search(method, false).findAll();
+ overrideLoop: for (PsiMethod overridingMethod : overridingMethods) {
+ PsiElement overridingElement = overridingMethod instanceof JetClsMethod
+ ? ((JetClsMethod) overridingMethod).getOrigin()
+ : overridingMethod;
+ Collection currentSuperMethods =
+ SuperMethodsSearch.search(overridingMethod, null, true, false).findAll();
+ currentSuperMethods.addAll(superMethods);
+ for (MethodSignatureBackedByPsiMethod superMethod: currentSuperMethods) {
+ if (superMethod.getMethod() != method) continue overrideLoop;
+ }
+
+ removeOverrideModifier(overridingElement);
+ }
+ }
+
+ @Nullable
+ private static Collection extends PsiElement> checkSuperMethods(
+ @NotNull JetNamedFunction function, @Nullable Collection ignore
+ ) {
+ final BindingContext bindingContext =
+ AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) function.getContainingFile()).getBindingContext();
+
+ DeclarationDescriptor declarationDescriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function);
+ if (!(declarationDescriptor instanceof FunctionDescriptor)) return null;
+
+ FunctionDescriptor functionDescriptor = (FunctionDescriptor) declarationDescriptor;
+ Set extends FunctionDescriptor> overridenDescriptors = functionDescriptor.getOverriddenDescriptors();
+
+ Collection extends PsiElement> superMethods = ContainerUtil.map(
+ overridenDescriptors,
+ new Function() {
+ @Override
+ public PsiElement fun(FunctionDescriptor descriptor) {
+ return BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor);
+ }
+ }
+ );
+ if (ignore != null) {
+ superMethods.removeAll(ignore);
+ }
+
+ if (superMethods.isEmpty()) return Collections.singletonList(function);
+
+ List superClasses = ContainerUtil.map(
+ superMethods,
+ new Function() {
+ @Override
+ public String fun(PsiElement element) {
+ String description;
+
+ if (element instanceof JetNamedFunction) {
+ DeclarationDescriptor descriptor =
+ bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
+ assert descriptor != null;
+
+ DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration();
+ assert containingDescriptor != null;
+
+ description = formatClassDescriptor(containingDescriptor);
+ }
+ else {
+ assert element instanceof PsiMethod;
+
+ PsiClass psiClass = ((PsiMethod) element).getContainingClass();
+ assert psiClass != null;
+
+ description = formatPsiClass(psiClass, true, false);
+ }
+
+ return " " + description + "\n";
+ }
+ }
+ );
+
+ String superClassesStr = "\n" + StringUtil.join(superClasses, "");
+ String message = JetBundle.message(
+ "x.overrides.y.in.class.list",
+ DescriptorRenderer.COMPACT.render(functionDescriptor),
+ DescriptorRenderer.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(functionDescriptor.getContainingDeclaration()),
+ superClassesStr
+ );
+
+ int exitCode = Messages.showYesNoCancelDialog(
+ function.getProject(), message, IdeBundle.message("title.warning"), Messages.getQuestionIcon()
+ );
+ switch (exitCode) {
+ case Messages.YES:
+ return superMethods;
+ case Messages.NO:
+ return Collections.singletonList(function);
+ default:
+ return Collections.emptyList();
+ }
+ }
+
+ @Nullable
+ @Override
+ public Collection extends PsiElement> getElementsToSearch(
+ PsiElement element, @Nullable Module module, Collection allElementsToDelete
+ ) {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return Collections.singletonList(element);
+ }
+
+ if (element instanceof JetNamedFunction) {
+ return checkSuperMethods((JetNamedFunction) element, allElementsToDelete);
+ }
+ return super.getElementsToSearch(element, module, allElementsToDelete);
}
@Override
public Collection getAdditionalElementsToDelete(
PsiElement element, Collection allElementsToDelete, boolean askUser
) {
- if (element instanceof JetObjectDeclarationName && element.getParent() instanceof JetObjectDeclaration) {
- return Arrays.asList(element.getParent());
+ if (element instanceof JetObjectDeclarationName) {
+ return Arrays.asList(getObjectDeclarationOrFail(element));
}
return super.getAdditionalElementsToDelete(element, allElementsToDelete, askUser);
}
diff --git a/idea/testData/safeDelete/deleteFunction/fun1.kt b/idea/testData/safeDelete/deleteFunction/fun1.kt
new file mode 100644
index 00000000000..bd48ff9ec5a
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/fun1.kt
@@ -0,0 +1,11 @@
+package test
+
+import test.foo
+
+fun foo() {
+
+}
+
+class B {
+
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/fun1.kt.after b/idea/testData/safeDelete/deleteFunction/fun1.kt.after
new file mode 100644
index 00000000000..191deddb2f8
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/fun1.kt.after
@@ -0,0 +1,5 @@
+package test
+
+class B {
+
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/fun2.kt b/idea/testData/safeDelete/deleteFunction/fun2.kt
new file mode 100644
index 00000000000..8bd92224aa2
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/fun2.kt
@@ -0,0 +1,11 @@
+package test
+
+import test.foo
+
+fun foo() {
+
+}
+
+class B {
+ val ref = ::foo
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/fun2.kt.messages b/idea/testData/safeDelete/deleteFunction/fun2.kt.messages
new file mode 100644
index 00000000000..7d36bd30109
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/fun2.kt.messages
@@ -0,0 +1,2 @@
+Function foo has 1 usage that is not safe to delete.
+Of those 0 usages are in strings, comments, or non-code files.
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/implement1.kt b/idea/testData/safeDelete/deleteFunction/implement1.kt
new file mode 100644
index 00000000000..d682b537116
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/implement1.kt
@@ -0,0 +1,9 @@
+trait A {
+ fun foo()
+}
+
+class B: A {
+ override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/implement1.kt.messages b/idea/testData/safeDelete/deleteFunction/implement1.kt.messages
new file mode 100644
index 00000000000..faf8f3ae2c0
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/implement1.kt.messages
@@ -0,0 +1 @@
+fun foo(): jet.Unit in final class B : A implements fun foo(): jet.Unit in trait A.
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/implement2.kt b/idea/testData/safeDelete/deleteFunction/implement2.kt
new file mode 100644
index 00000000000..d45ab2c6785
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/implement2.kt
@@ -0,0 +1,13 @@
+trait A {
+ fun foo()
+}
+
+trait Z {
+ fun foo()
+}
+
+class B: A, Z {
+ override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/implement2.kt.messages b/idea/testData/safeDelete/deleteFunction/implement2.kt.messages
new file mode 100644
index 00000000000..f1e89f82803
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/implement2.kt.messages
@@ -0,0 +1,2 @@
+fun foo(): jet.Unit in final class B : A, Z implements fun foo(): jet.Unit in trait A.
+fun foo(): jet.Unit in final class B : A, Z implements fun foo(): jet.Unit in trait Z.
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/noUsages.kt b/idea/testData/safeDelete/deleteFunction/noUsages.kt
new file mode 100644
index 00000000000..b05a392db1d
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/noUsages.kt
@@ -0,0 +1,7 @@
+class C {
+ fun foo {
+ }
+}
+
+class B {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/noUsages.kt.after b/idea/testData/safeDelete/deleteFunction/noUsages.kt.after
new file mode 100644
index 00000000000..3f11a5b1fd3
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/noUsages.kt.after
@@ -0,0 +1,5 @@
+class C {
+}
+
+class B {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/override1.kt b/idea/testData/safeDelete/deleteFunction/override1.kt
new file mode 100644
index 00000000000..73f238168cb
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/override1.kt
@@ -0,0 +1,11 @@
+open class A {
+ open fun foo() {
+
+ }
+}
+
+class B: A {
+ override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/override1.kt.after b/idea/testData/safeDelete/deleteFunction/override1.kt.after
new file mode 100644
index 00000000000..15c2912677e
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/override1.kt.after
@@ -0,0 +1,8 @@
+open class A {
+ open fun foo() {
+
+ }
+}
+
+class B: A {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/override2.kt b/idea/testData/safeDelete/deleteFunction/override2.kt
new file mode 100644
index 00000000000..afdf4b91e97
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/override2.kt
@@ -0,0 +1,11 @@
+open class A {
+ open fun foo() {
+
+ }
+}
+
+class B: A {
+ override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/override2.kt.after b/idea/testData/safeDelete/deleteFunction/override2.kt.after
new file mode 100644
index 00000000000..4d87308c243
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/override2.kt.after
@@ -0,0 +1,5 @@
+open class A {
+}
+
+class B: A {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/overrideAndImplement1.kt b/idea/testData/safeDelete/deleteFunction/overrideAndImplement1.kt
new file mode 100644
index 00000000000..b11689d9a37
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/overrideAndImplement1.kt
@@ -0,0 +1,15 @@
+open class A {
+ open fun foo() {
+
+ }
+}
+
+trait Z {
+ fun foo()
+}
+
+class B: A, Z {
+ override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/overrideAndImplement1.kt.messages b/idea/testData/safeDelete/deleteFunction/overrideAndImplement1.kt.messages
new file mode 100644
index 00000000000..b1f9c85864b
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/overrideAndImplement1.kt.messages
@@ -0,0 +1 @@
+fun foo(): jet.Unit in final class B : A, Z implements fun foo(): jet.Unit in trait Z.
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/overrideAndImplement2.kt b/idea/testData/safeDelete/deleteFunction/overrideAndImplement2.kt
new file mode 100644
index 00000000000..053782794d8
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/overrideAndImplement2.kt
@@ -0,0 +1,15 @@
+open class A {
+ open fun foo() {
+
+ }
+}
+
+trait Z {
+ fun foo()
+}
+
+class B: A, Z {
+ override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/overrideAndImplement2.kt.after b/idea/testData/safeDelete/deleteFunction/overrideAndImplement2.kt.after
new file mode 100644
index 00000000000..92575432fe6
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/overrideAndImplement2.kt.after
@@ -0,0 +1,12 @@
+open class A {
+}
+
+trait Z {
+ fun foo()
+}
+
+class B: A, Z {
+ override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/overrideWithUsages.kt b/idea/testData/safeDelete/deleteFunction/overrideWithUsages.kt
new file mode 100644
index 00000000000..50de3d0bfdb
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/overrideWithUsages.kt
@@ -0,0 +1,15 @@
+open class A {
+ open fun foo() {
+
+ }
+}
+
+class B: A {
+ fun bar() {
+ foo()
+ }
+
+ override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunction/overrideWithUsages.kt.after b/idea/testData/safeDelete/deleteFunction/overrideWithUsages.kt.after
new file mode 100644
index 00000000000..7d6de4fbd2f
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunction/overrideWithUsages.kt.after
@@ -0,0 +1,12 @@
+open class A {
+}
+
+class B: A {
+ fun bar() {
+ foo()
+ }
+
+ fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement1.java b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement1.java
new file mode 100644
index 00000000000..9d1504fe3b6
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement1.java
@@ -0,0 +1,3 @@
+interface A {
+ void foo();
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement1.kt b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement1.kt
new file mode 100644
index 00000000000..a76fc973489
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement1.kt
@@ -0,0 +1,5 @@
+class B: A {
+ override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement1.kt.messages b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement1.kt.messages
new file mode 100644
index 00000000000..370cf8c5f42
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement1.kt.messages
@@ -0,0 +1 @@
+fun foo(): jet.Unit in final class B : A implements [Java] void foo() in interface A.
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.java b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.java
new file mode 100644
index 00000000000..922ff12cbb2
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.java
@@ -0,0 +1,5 @@
+class B implements A {
+ public void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.java.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.java.after
new file mode 100644
index 00000000000..96506701c7c
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.java.after
@@ -0,0 +1,2 @@
+class B implements A {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.kt b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.kt
new file mode 100644
index 00000000000..f3a5a12bc65
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.kt
@@ -0,0 +1,3 @@
+trait A {
+ fun foo()
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.kt.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.kt.after
new file mode 100644
index 00000000000..670cabdb241
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.kt.after
@@ -0,0 +1,2 @@
+trait A {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement3.java b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement3.java
new file mode 100644
index 00000000000..f39830b8fc1
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement3.java
@@ -0,0 +1,7 @@
+interface A {
+ void foo();
+}
+
+interface Z {
+ void foo();
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement3.kt b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement3.kt
new file mode 100644
index 00000000000..c6b6e0d3ee9
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement3.kt
@@ -0,0 +1,5 @@
+class B: A, Z {
+ public override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement3.kt.messages b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement3.kt.messages
new file mode 100644
index 00000000000..f6a48a49969
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement3.kt.messages
@@ -0,0 +1,2 @@
+fun foo(): jet.Unit in final class B : A, Z implements [Java] void foo() in interface A.
+fun foo(): jet.Unit in final class B : A, Z implements [Java] void foo() in interface Z.
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.java b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.java
new file mode 100644
index 00000000000..f8cba162620
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.java
@@ -0,0 +1,5 @@
+class B implements A, Z {
+ public void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.java.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.java.after
new file mode 100644
index 00000000000..f8cba162620
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.java.after
@@ -0,0 +1,5 @@
+class B implements A, Z {
+ public void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.kt b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.kt
new file mode 100644
index 00000000000..3470b144414
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.kt
@@ -0,0 +1,7 @@
+trait A {
+ fun foo()
+}
+
+trait Z {
+ fun foo()
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.kt.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.kt.after
new file mode 100644
index 00000000000..4b0d260090e
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.kt.after
@@ -0,0 +1,6 @@
+trait A {
+}
+
+trait Z {
+ fun foo()
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.java b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.java
new file mode 100644
index 00000000000..59ea3d006c7
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.java
@@ -0,0 +1,5 @@
+class A {
+ void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.java.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.java.after
new file mode 100644
index 00000000000..59ea3d006c7
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.java.after
@@ -0,0 +1,5 @@
+class A {
+ void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.kt b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.kt
new file mode 100644
index 00000000000..a76fc973489
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.kt
@@ -0,0 +1,5 @@
+class B: A {
+ override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.kt.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.kt.after
new file mode 100644
index 00000000000..994dc58cf69
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.kt.after
@@ -0,0 +1,2 @@
+class B: A {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.java b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.java
new file mode 100644
index 00000000000..315ae36bf5a
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.java
@@ -0,0 +1,6 @@
+class B extends A {
+ @Override
+ void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.java.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.java.after
new file mode 100644
index 00000000000..99b93165680
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.java.after
@@ -0,0 +1,2 @@
+class B extends A {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.kt b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.kt
new file mode 100644
index 00000000000..f3d6421d400
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.kt
@@ -0,0 +1,5 @@
+open class A {
+ open fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.kt.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.kt.after
new file mode 100644
index 00000000000..d80ee90d357
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.kt.after
@@ -0,0 +1,2 @@
+open class A {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.java b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.java
new file mode 100644
index 00000000000..6647e4afa78
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.java
@@ -0,0 +1,10 @@
+class B extends A {
+ void bar() {
+ foo();
+ }
+
+ @Override
+ public void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.java.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.java.after
new file mode 100644
index 00000000000..774a6b0c204
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.java.after
@@ -0,0 +1,9 @@
+class B extends A {
+ void bar() {
+ foo();
+ }
+
+ public void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.kt b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.kt
new file mode 100644
index 00000000000..f3d6421d400
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.kt
@@ -0,0 +1,5 @@
+open class A {
+ open fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.kt.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.kt.after
new file mode 100644
index 00000000000..d80ee90d357
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.kt.after
@@ -0,0 +1,2 @@
+open class A {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement1.java b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement1.java
new file mode 100644
index 00000000000..780d713081d
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement1.java
@@ -0,0 +1,9 @@
+class A {
+ public void foo() {
+
+ }
+}
+
+interface Z {
+ void foo();
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement1.kt b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement1.kt
new file mode 100644
index 00000000000..c6b6e0d3ee9
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement1.kt
@@ -0,0 +1,5 @@
+class B: A, Z {
+ public override fun foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement1.kt.messages b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement1.kt.messages
new file mode 100644
index 00000000000..1f17b7e2ff5
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement1.kt.messages
@@ -0,0 +1 @@
+fun foo(): jet.Unit in final class B : A, Z implements [Java] void foo() in interface Z.
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.java b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.java
new file mode 100644
index 00000000000..8e3a7341f25
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.java
@@ -0,0 +1,5 @@
+class B extends A implements Z {
+ public void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.java.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.java.after
new file mode 100644
index 00000000000..8e3a7341f25
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.java.after
@@ -0,0 +1,5 @@
+class B extends A implements Z {
+ public void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.kt b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.kt
new file mode 100644
index 00000000000..46b50ab2ac9
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.kt
@@ -0,0 +1,9 @@
+class A {
+ public fun foo() {
+
+ }
+}
+
+trait Z {
+ fun foo()
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.kt.after b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.kt.after
new file mode 100644
index 00000000000..ce7ab3cebd7
--- /dev/null
+++ b/idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.kt.after
@@ -0,0 +1,6 @@
+class A {
+}
+
+trait Z {
+ fun foo()
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.java b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.java
new file mode 100644
index 00000000000..7ee62ca2e46
--- /dev/null
+++ b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.java
@@ -0,0 +1,10 @@
+interface A {
+ public void foo();
+}
+
+abstract class C implements B {
+ @Override
+ public void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.java.after b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.java.after
new file mode 100644
index 00000000000..e91065bc7be
--- /dev/null
+++ b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.java.after
@@ -0,0 +1,5 @@
+interface A {
+}
+
+abstract class C implements B {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.kt b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.kt
new file mode 100644
index 00000000000..4bde1062335
--- /dev/null
+++ b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.kt
@@ -0,0 +1,11 @@
+trait B: A {
+ public override fun foo() {
+ throw UnsupportedOperationException()
+ }
+}
+
+class D: C() {
+ public override fun foo() {
+ throw UnsupportedOperationException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.kt.after b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.kt.after
new file mode 100644
index 00000000000..d1fffa7e4b8
--- /dev/null
+++ b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.kt.after
@@ -0,0 +1,5 @@
+trait B: A {
+}
+
+class D: C() {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.java b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.java
new file mode 100644
index 00000000000..88d0495e439
--- /dev/null
+++ b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.java
@@ -0,0 +1,10 @@
+interface A {
+ public void foo();
+}
+
+abstract class C implements B {
+ @Override
+ public void foo() {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.java.after b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.java.after
new file mode 100644
index 00000000000..525267c9118
--- /dev/null
+++ b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.java.after
@@ -0,0 +1,6 @@
+interface A {
+ public void foo();
+}
+
+abstract class C implements B {
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.kt b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.kt
new file mode 100644
index 00000000000..4bde1062335
--- /dev/null
+++ b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.kt
@@ -0,0 +1,11 @@
+trait B: A {
+ public override fun foo() {
+ throw UnsupportedOperationException()
+ }
+}
+
+class D: C() {
+ public override fun foo() {
+ throw UnsupportedOperationException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.kt.after b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.kt.after
new file mode 100644
index 00000000000..d208f64b7a7
--- /dev/null
+++ b/idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.kt.after
@@ -0,0 +1,8 @@
+trait B: A {
+ public override fun foo() {
+ throw UnsupportedOperationException()
+ }
+}
+
+class D: C() {
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/jet/safeDelete/AbstractJetSafeDeleteTest.java b/idea/tests/org/jetbrains/jet/safeDelete/AbstractJetSafeDeleteTest.java
index 0473afb8389..0ef86248851 100644
--- a/idea/tests/org/jetbrains/jet/safeDelete/AbstractJetSafeDeleteTest.java
+++ b/idea/tests/org/jetbrains/jet/safeDelete/AbstractJetSafeDeleteTest.java
@@ -16,44 +16,99 @@
package org.jetbrains.jet.safeDelete;
-import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.codeInsight.TargetElementUtilBase;
+import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.safeDelete.SafeDeleteHandler;
-import com.intellij.testFramework.LightCodeInsightTestCase;
+import com.intellij.testFramework.LightProjectDescriptor;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetClass;
-import org.jetbrains.jet.lang.psi.JetElement;
+import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.psi.JetObjectDeclarationName;
+import org.jetbrains.jet.plugin.JetLightProjectDescriptor;
+import org.jetbrains.jet.plugin.PluginTestCaseBase;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-public abstract class AbstractJetSafeDeleteTest extends LightCodeInsightTestCase {
+public abstract class AbstractJetSafeDeleteTest extends LightCodeInsightFixtureTestCase {
+ @NotNull
+ @Override
+ protected LightProjectDescriptor getProjectDescriptor() {
+ return JetLightProjectDescriptor.INSTANCE;
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ String pathBase = PluginTestCaseBase.getTestDataPathBase();
+ myFixture.setTestDataPath(pathBase.substring(0, pathBase.lastIndexOf("/idea/testData")));
+ }
+
public void doClassTest(@NotNull String path) throws Exception {
- doTest(path, JetClass.class);
+ doTest(path, JetClass.class, false);
}
public void doObjectTest(@NotNull String path) throws Exception {
- doTest(path, JetObjectDeclarationName.class);
+ doTest(path, JetObjectDeclarationName.class, false);
}
- private void doTest(@NotNull String path, @NotNull Class elementClass) throws Exception {
- configureByFile(path);
+ public void doFunctionTest(@NotNull String path) throws Exception {
+ doTest(path, JetNamedFunction.class, false);
+ }
- DataContext dataContext = getCurrentEditorDataContext();
- PsiElement element = PsiTreeUtil.getParentOfType(LangDataKeys.PSI_ELEMENT.getData(dataContext), elementClass, false);
+ public void doFunctionTestWithJava(@NotNull String path) throws Exception {
+ doTest(path, JetNamedFunction.class, true);
+ }
+
+ public void doJavaMethodTest(@NotNull String path) throws Exception {
+ doTest(path, PsiMethod.class, true);
+ }
+
+ private void doTest(
+ @NotNull String path, @NotNull Class elementClass, boolean withJava) throws Exception {
+ String[] filePaths;
+ if (withJava) {
+ filePaths = new String[]{path, path.endsWith(".java") ? path.replace(".java", ".kt") : path.replace(".kt", ".java")};
+ }
+ else {
+ filePaths = new String[]{path};
+ }
+
+ Editor[] editors = new Editor[filePaths.length];
+ int i = 0;
+ for (String filePath : filePaths) {
+ myFixture.configureByFile(filePath);
+ editors[i++] = myFixture.getEditor();
+ }
+
+ PsiElement elementAtCaret = null;
+ for (Editor editor : editors) {
+ elementAtCaret = TargetElementUtilBase.findTargetElement(
+ editor, TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED | TargetElementUtilBase.ELEMENT_NAME_ACCEPTED
+ );
+ if (elementAtCaret != null) break;
+ }
+
+ assertNotNull("Couldn't find element at caret position", elementAtCaret);
+
+ T element = PsiTreeUtil.getParentOfType(elementAtCaret, elementClass, false);
try {
- new SafeDeleteHandler().invoke(getProject(), new PsiElement[] {element}, dataContext);
- checkResultByFile(path + ".after");
+ SafeDeleteHandler.invoke(getProject(), new PsiElement[] {element}, null, true, null);
+ for (int j = 0; j < filePaths.length; j++) {
+ String expectedText = FileUtil.loadFile(new File(filePaths[j] + ".after"));
+ assertEquals(StringUtil.convertLineSeparators(expectedText), editors[j].getDocument().getText());
+ }
}
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
List messages = new ArrayList(e.getMessages());
@@ -64,10 +119,4 @@ public abstract class AbstractJetSafeDeleteTest extends LightCodeInsightTestCase
assertEquals(expectedMessage, StringUtil.join(messages, "\n"));
}
}
-
- @NotNull
- @Override
- protected String getTestDataPath() {
- return "";
- }
}
diff --git a/idea/tests/org/jetbrains/jet/safeDelete/JetSafeDeleteTestGenerated.java b/idea/tests/org/jetbrains/jet/safeDelete/JetSafeDeleteTestGenerated.java
index 6440a519560..78d2ffdb3e1 100644
--- a/idea/tests/org/jetbrains/jet/safeDelete/JetSafeDeleteTestGenerated.java
+++ b/idea/tests/org/jetbrains/jet/safeDelete/JetSafeDeleteTestGenerated.java
@@ -30,7 +30,7 @@ import org.jetbrains.jet.safeDelete.AbstractJetSafeDeleteTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
-@InnerTestClasses({JetSafeDeleteTestGenerated.DeleteClass.class, JetSafeDeleteTestGenerated.DeleteObject.class})
+@InnerTestClasses({JetSafeDeleteTestGenerated.DeleteClass.class, JetSafeDeleteTestGenerated.DeleteObject.class, JetSafeDeleteTestGenerated.DeleteFunction.class, JetSafeDeleteTestGenerated.DeleteFunctionWithJavaUsages.class, JetSafeDeleteTestGenerated.DeleteJavaMethod.class})
public class JetSafeDeleteTestGenerated extends AbstractJetSafeDeleteTest {
@TestMetadata("idea/testData/safeDelete/deleteClass")
public static class DeleteClass extends AbstractJetSafeDeleteTest {
@@ -118,10 +118,142 @@ public class JetSafeDeleteTestGenerated extends AbstractJetSafeDeleteTest {
}
+ @TestMetadata("idea/testData/safeDelete/deleteFunction")
+ public static class DeleteFunction extends AbstractJetSafeDeleteTest {
+ public void testAllFilesPresentInDeleteFunction() throws Exception {
+ JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/safeDelete/deleteFunction"), Pattern.compile("^(.+)\\.kt$"), true);
+ }
+
+ @TestMetadata("fun1.kt")
+ public void testFun1() throws Exception {
+ doFunctionTest("idea/testData/safeDelete/deleteFunction/fun1.kt");
+ }
+
+ @TestMetadata("fun2.kt")
+ public void testFun2() throws Exception {
+ doFunctionTest("idea/testData/safeDelete/deleteFunction/fun2.kt");
+ }
+
+ @TestMetadata("implement1.kt")
+ public void testImplement1() throws Exception {
+ doFunctionTest("idea/testData/safeDelete/deleteFunction/implement1.kt");
+ }
+
+ @TestMetadata("implement2.kt")
+ public void testImplement2() throws Exception {
+ doFunctionTest("idea/testData/safeDelete/deleteFunction/implement2.kt");
+ }
+
+ @TestMetadata("noUsages.kt")
+ public void testNoUsages() throws Exception {
+ doFunctionTest("idea/testData/safeDelete/deleteFunction/noUsages.kt");
+ }
+
+ @TestMetadata("override1.kt")
+ public void testOverride1() throws Exception {
+ doFunctionTest("idea/testData/safeDelete/deleteFunction/override1.kt");
+ }
+
+ @TestMetadata("override2.kt")
+ public void testOverride2() throws Exception {
+ doFunctionTest("idea/testData/safeDelete/deleteFunction/override2.kt");
+ }
+
+ @TestMetadata("overrideAndImplement1.kt")
+ public void testOverrideAndImplement1() throws Exception {
+ doFunctionTest("idea/testData/safeDelete/deleteFunction/overrideAndImplement1.kt");
+ }
+
+ @TestMetadata("overrideAndImplement2.kt")
+ public void testOverrideAndImplement2() throws Exception {
+ doFunctionTest("idea/testData/safeDelete/deleteFunction/overrideAndImplement2.kt");
+ }
+
+ @TestMetadata("overrideWithUsages.kt")
+ public void testOverrideWithUsages() throws Exception {
+ doFunctionTest("idea/testData/safeDelete/deleteFunction/overrideWithUsages.kt");
+ }
+
+ }
+
+ @TestMetadata("idea/testData/safeDelete/deleteFunctionWithJavaUsages")
+ public static class DeleteFunctionWithJavaUsages extends AbstractJetSafeDeleteTest {
+ public void testAllFilesPresentInDeleteFunctionWithJavaUsages() throws Exception {
+ JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/safeDelete/deleteFunctionWithJavaUsages"), Pattern.compile("^(.+)\\.kt$"), true);
+ }
+
+ @TestMetadata("implement1.kt")
+ public void testImplement1() throws Exception {
+ doFunctionTestWithJava("idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement1.kt");
+ }
+
+ @TestMetadata("implement2.kt")
+ public void testImplement2() throws Exception {
+ doFunctionTestWithJava("idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement2.kt");
+ }
+
+ @TestMetadata("implement3.kt")
+ public void testImplement3() throws Exception {
+ doFunctionTestWithJava("idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement3.kt");
+ }
+
+ @TestMetadata("implement4.kt")
+ public void testImplement4() throws Exception {
+ doFunctionTestWithJava("idea/testData/safeDelete/deleteFunctionWithJavaUsages/implement4.kt");
+ }
+
+ @TestMetadata("override1.kt")
+ public void testOverride1() throws Exception {
+ doFunctionTestWithJava("idea/testData/safeDelete/deleteFunctionWithJavaUsages/override1.kt");
+ }
+
+ @TestMetadata("override2.kt")
+ public void testOverride2() throws Exception {
+ doFunctionTestWithJava("idea/testData/safeDelete/deleteFunctionWithJavaUsages/override2.kt");
+ }
+
+ @TestMetadata("override3.kt")
+ public void testOverride3() throws Exception {
+ doFunctionTestWithJava("idea/testData/safeDelete/deleteFunctionWithJavaUsages/override3.kt");
+ }
+
+ @TestMetadata("overrideAndImplement1.kt")
+ public void testOverrideAndImplement1() throws Exception {
+ doFunctionTestWithJava("idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement1.kt");
+ }
+
+ @TestMetadata("overrideAndImplement2.kt")
+ public void testOverrideAndImplement2() throws Exception {
+ doFunctionTestWithJava("idea/testData/safeDelete/deleteFunctionWithJavaUsages/overrideAndImplement2.kt");
+ }
+
+ }
+
+ @TestMetadata("idea/testData/safeDelete/deleteJavaMethod")
+ public static class DeleteJavaMethod extends AbstractJetSafeDeleteTest {
+ public void testAllFilesPresentInDeleteJavaMethod() throws Exception {
+ JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/safeDelete/deleteJavaMethod"), Pattern.compile("^(.+)\\.kt$"), true);
+ }
+
+ @TestMetadata("mixedHierarchy1.kt")
+ public void testMixedHierarchy1() throws Exception {
+ doJavaMethodTest("idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy1.kt");
+ }
+
+ @TestMetadata("mixedHierarchy2.kt")
+ public void testMixedHierarchy2() throws Exception {
+ doJavaMethodTest("idea/testData/safeDelete/deleteJavaMethod/mixedHierarchy2.kt");
+ }
+
+ }
+
public static Test suite() {
TestSuite suite = new TestSuite("JetSafeDeleteTestGenerated");
suite.addTestSuite(DeleteClass.class);
suite.addTestSuite(DeleteObject.class);
+ suite.addTestSuite(DeleteFunction.class);
+ suite.addTestSuite(DeleteFunctionWithJavaUsages.class);
+ suite.addTestSuite(DeleteJavaMethod.class);
return suite;
}
}