Implement "Safe Delete" refactoring for type parameters

Conflicts:
	idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteProcessor.java

Conflicts:
	generators/org/jetbrains/jet/generators/tests/GenerateTests.java

Conflicts:
	generators/org/jetbrains/jet/generators/tests/GenerateTests.java
This commit is contained in:
Alexey Sedunov
2013-07-29 17:30:07 +04:00
parent 62a1a9b8d1
commit 203e80debe
102 changed files with 722 additions and 122 deletions
@@ -911,6 +911,35 @@ public class JetPsiUtil {
return (names.size() > partIndex) ? names.get(partIndex) : header.getLastPartExpression();
}
// Delete given element and all the elements separating it from the neighboring elements of the same class
public static void deleteElementWithDelimiters(@NotNull PsiElement element) {
PsiElement paramBefore = PsiTreeUtil.getPrevSiblingOfType(element, element.getClass());
PsiElement from;
PsiElement to;
if (paramBefore != null) {
from = paramBefore.getNextSibling();
to = element;
}
else {
PsiElement paramAfter = PsiTreeUtil.getNextSiblingOfType(element, element.getClass());
from = element;
to = paramAfter != null ? paramAfter.getPrevSibling() : element;
}
PsiElement parent = element.getParent();
parent.deleteChildRange(from, to);
}
// Delete element if it doesn't contain children of a given type
public static <T extends PsiElement> void deleteChildlessElement(PsiElement element, Class<T> childClass) {
if (PsiTreeUtil.getChildrenOfType(element, childClass) == null) {
element.delete();
}
}
public static PsiElement ascendIfPropertyAccessor(PsiElement element) {
if (element instanceof JetPropertyAccessor) {
return element.getParent();
@@ -36,13 +36,13 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
public void testAllFilesPresentInBoxWithStdlib() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/boxWithStdlib"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/annotations")
public static class Annotations extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInAnnotations() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/boxWithStdlib/annotations"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("defaultParameterValues.kt")
public void testDefaultParameterValues() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/annotations/defaultParameterValues.kt");
@@ -29,9 +29,9 @@ public class JetElementDescriptionProvider implements ElementDescriptionProvider
@Override
public String getElementDescription(@NotNull PsiElement element, @NotNull ElementDescriptionLocation location) {
if (location instanceof UsageViewLongNameLocation) {
if (element instanceof PsiNamedElement && element instanceof JetElement) {
return ((PsiNamedElement)element).getName();
}
if (element instanceof PsiNamedElement && element instanceof JetElement) {
return ((PsiNamedElement) element).getName();
}
}
else if (location instanceof RefactoringDescriptionLocation) {
if (element instanceof JetClass) {
@@ -39,16 +39,16 @@ public class JetElementDescriptionProvider implements ElementDescriptionProvider
return (jetClass.isTrait() ? "Trait " : "Class ") + jetClass.getName();
}
if (element instanceof JetObjectDeclaration || element instanceof JetObjectDeclarationName) {
return "Object " + ((PsiNamedElement)element).getName();
return "Object " + ((PsiNamedElement) element).getName();
}
if (element instanceof JetNamedFunction) {
return "Function " + ((PsiNamedElement)element).getName();
return "Function " + ((PsiNamedElement) element).getName();
}
if (element instanceof JetProperty) {
return "Property " + ((PsiNamedElement)element).getName();
return "Property " + ((PsiNamedElement) element).getName();
}
if (element instanceof JetTypeParameter) {
return "Type parameter " + ((PsiNamedElement)element).getName();
return "Type parameter " + ((PsiNamedElement) element).getName();
}
}
return null;
@@ -31,6 +31,7 @@ import com.intellij.refactoring.safeDelete.JavaSafeDeleteProcessor;
import com.intellij.refactoring.safeDelete.NonCodeUsageSearchInfo;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteOverrideAnnotation;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteOverridingMethodUsageInfo;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceJavaDeleteUsageInfo;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceSimpleDeleteUsageInfo;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.*;
@@ -56,7 +57,8 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor {
|| element instanceof JetObjectDeclarationName
|| element instanceof JetNamedFunction
|| element instanceof PsiMethod
|| element instanceof JetProperty;
|| element instanceof JetProperty
|| element instanceof JetTypeParameter;
}
@Override
@@ -108,6 +110,9 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor {
}
return findPropertyUsages(property, allElementsToDelete, result);
}
if (element instanceof JetTypeParameter) {
return findTypeParameterUsages((JetTypeParameter) element, allElementsToDelete, result);
}
return getSearchInfo(element, allElementsToDelete);
}
@@ -309,6 +314,101 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor {
return getSearchInfo(property, allElementsToDelete);
}
protected static NonCodeUsageSearchInfo findTypeParameterUsages(
final JetTypeParameter parameter,
final PsiElement[] allElementsToDelete,
final List<UsageInfo> result
) {
NonCodeUsageSearchInfo searchInfo = getSearchInfo(parameter, allElementsToDelete);
ReferencesSearch.search(parameter).forEach(new Processor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
PsiElement element = reference.getElement();
if (!isInside(element, allElementsToDelete)) {
result.add(new SafeDeleteReferenceSimpleDeleteUsageInfo(element, parameter, false));
}
return true;
}
});
JetTypeParameterListOwner owner = PsiTreeUtil.getParentOfType(parameter, JetTypeParameterListOwner.class);
if (owner == null) return searchInfo;
List<JetTypeParameter> parameterList = owner.getTypeParameters();
final int parameterCount = parameterList.size();
final int parameterIndex = parameterList.indexOf(parameter);
ReferencesSearch.search(owner).forEach(
new Processor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
if (reference instanceof PsiJavaCodeReferenceElement) {
processJavaTypeArgumentListCandidate(
(PsiJavaCodeReferenceElement) reference, parameterIndex, parameterCount, result, parameter
);
}
else {
processKotlinTypeArgumentListCandidate(reference, parameterIndex, result, parameter);
}
return true;
}
}
);
return searchInfo;
}
private static void processJavaTypeArgumentListCandidate(
PsiJavaCodeReferenceElement reference,
int parameterIndex,
int parameterCount,
List<UsageInfo> result,
PsiElement parameter
) {
PsiReferenceParameterList parameterList = reference.getParameterList();
if (parameterList != null) {
PsiTypeElement[] typeArgs = parameterList.getTypeParameterElements();
if (typeArgs.length > parameterIndex) {
if (typeArgs.length == 1 && parameterCount > 1
&& typeArgs[0].getType() instanceof PsiDiamondType) {
return;
}
result.add(new SafeDeleteReferenceJavaDeleteUsageInfo(typeArgs[parameterIndex], parameter, true));
}
}
}
private static void processKotlinTypeArgumentListCandidate(
PsiReference reference,
int parameterIndex,
List<UsageInfo> result,
JetTypeParameter parameter
) {
PsiElement referencedElement = reference.getElement();
JetTypeArgumentList argList = null;
JetUserType type = PsiTreeUtil.getParentOfType(referencedElement, JetUserType.class);
if (type != null) {
argList = type.getTypeArgumentList();
}
else {
JetCallExpression callExpression = PsiTreeUtil.getParentOfType(referencedElement, JetCallExpression.class);
if (callExpression != null) {
argList = callExpression.getTypeArgumentList();
}
}
if (argList != null) {
List<JetTypeProjection> projections = argList.getArguments();
if (parameterIndex < projections.size()) {
result.add(new SafeDeleteTypeArgumentListUsageInfo(projections.get(parameterIndex), parameter));
}
}
}
/*
* Mostly copied from JavaSafeDeleteProcessor.validateOverridingMethods
* Revision: d4fc033
@@ -561,6 +661,15 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor {
cleanUpOverrides(setter);
}
}
else if (element instanceof JetTypeParameter) {
deleteElementAndCleanParent(element);
}
}
public static void deleteElementAndCleanParent(PsiElement element) {
PsiElement parent = element.getParent();
JetPsiUtil.deleteElementWithDelimiters(element);
JetPsiUtil.deleteChildlessElement(parent, element.getClass());
}
private static boolean checkPsiMethodEquality(PsiMethod method1, PsiMethod method2) {
@@ -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.plugin.refactoring.safeDelete;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceSimpleDeleteUsageInfo;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.*;
public class SafeDeleteTypeArgumentListUsageInfo extends SafeDeleteReferenceSimpleDeleteUsageInfo {
public SafeDeleteTypeArgumentListUsageInfo(@NotNull JetTypeProjection typeProjection, @NotNull JetTypeParameter parameter) {
super(typeProjection, parameter, true);
}
@Override
public void deleteElement() throws IncorrectOperationException {
PsiElement element = getElement();
if (element != null) {
KotlinSafeDeleteProcessor.deleteElementAndCleanParent(element);
}
}
}
@@ -0,0 +1,7 @@
class A<<caret>X, Y, Z> {
fun foo(x: X) {
}
}
val a = A<Int, String, Any>()
@@ -0,0 +1,2 @@
Type parameter X has 1 usage that is not safe to delete.
Of those 0 usages are in strings, comments, or non-code files.
@@ -0,0 +1,9 @@
class A<X> {
fun foo<<caret>Y>(x: X, y: Y) {
}
}
fun bar(a: A<String>) {
a.foo<Int>("123", 123)
}
@@ -0,0 +1,2 @@
Type parameter Y has 1 usage that is not safe to delete.
Of those 0 usages are in strings, comments, or non-code files.
@@ -0,0 +1,7 @@
class A<<caret>X, Y: X, Z> {
fun foo(x: X) {
}
}
val a = A<Int, String, Any>()
@@ -0,0 +1,2 @@
Type parameter X has 2 usages that are not safe to delete.
Of those 0 usages are in strings, comments, or non-code files.
@@ -0,0 +1,4 @@
class A<<caret>X, Y, Z> where Y: X {
}
val a = A<Int, String, Any>()
@@ -0,0 +1,2 @@
Type parameter X has 1 usage that is not safe to delete.
Of those 0 usages are in strings, comments, or non-code files.
@@ -0,0 +1,9 @@
class A<<caret>X> {
fun foo<Y, Z>() where Y: X {
}
}
fun bar(a: A<Number>) {
a.foo<Int, String>()
}
@@ -0,0 +1,2 @@
Type parameter X has 1 usage that is not safe to delete.
Of those 0 usages are in strings, comments, or non-code files.
@@ -0,0 +1,3 @@
class A<out <caret>X: Comparable<X>, in Y: Number, Z>
val a = A<String, Int, Any>()
@@ -0,0 +1,3 @@
class A<in Y: Number, Z>
val a = A<Int, Any>()
@@ -0,0 +1,3 @@
class A<out X: Comparable<X>, in <caret>Y: Number, Z>
val a = A<String, Int, Any>()
@@ -0,0 +1,3 @@
class A<out X: Comparable<X>, Z>
val a = A<String, Any>()
@@ -0,0 +1,3 @@
open class A<<caret>X, Y, Z>
class B<T, U>: A<T, String, U>
@@ -0,0 +1,3 @@
open class A<Y, Z>
class B<T, U>: A<String, U>
@@ -0,0 +1,3 @@
open class A<<caret>X, Y, Z>
class B<T, U>: A<String, T, U>
@@ -0,0 +1,3 @@
open class A<Y, Z>
class B<T, U>: A<T, U>
@@ -0,0 +1,3 @@
class A<<caret>X, Y, Z>
val a = A<Int, String, Any>()
@@ -0,0 +1,3 @@
class A<Y, Z>
val a = A<String, Any>()
@@ -0,0 +1,3 @@
class A<X, <caret>Y, Z>
val a = A<Int, String, Any>()
@@ -0,0 +1,3 @@
class A<X, Z>
val a = A<Int, Any>()
@@ -0,0 +1,3 @@
class A<X, Y, <caret>Z>
val a = A<Int, String, Any>()
@@ -0,0 +1,3 @@
class A<X, Y>
val a = A<Int, String>()
@@ -0,0 +1,9 @@
class A<X> {
fun foo<<caret>Y, Z>() {
}
}
fun bar(a: A<String>) {
a.foo<Int, Any>()
}
@@ -0,0 +1,9 @@
class A<X> {
fun foo<Z>() {
}
}
fun bar(a: A<String>) {
a.foo<Any>()
}
@@ -0,0 +1,9 @@
class A<X> {
fun foo<Y, <caret>Z>() {
}
}
fun bar(a: A<String>) {
a.foo<Int, Any>()
}
@@ -0,0 +1,9 @@
class A<X> {
fun foo<Y>() {
}
}
fun bar(a: A<String>) {
a.foo<Int>()
}
@@ -0,0 +1,3 @@
class A<<caret>X>
val a = A<Int>()
@@ -0,0 +1,3 @@
class A
val a = A()
@@ -0,0 +1,9 @@
class A<X> {
fun foo<<caret>Y>() {
}
}
fun bar(a: A<String>) {
a.foo<Int>()
}
@@ -0,0 +1,9 @@
class A<X> {
fun foo() {
}
}
fun bar(a: A<String>) {
a.foo()
}
@@ -0,0 +1,3 @@
class B {
A<Int, String, Object> a = new A<Int, String, Object>()
}
@@ -0,0 +1,5 @@
class A<<caret>X, Y, Z> {
fun foo(x: X) {
}
}
@@ -0,0 +1,2 @@
Type parameter X has 1 usage that is not safe to delete.
Of those 0 usages are in strings, comments, or non-code files.
@@ -0,0 +1,5 @@
class B {
void bar(A<String> a) {
a.<Int>foo("123", 123)
}
}
@@ -0,0 +1,5 @@
class A<X> {
fun foo<<caret>Y>(x: X, y: Y) {
}
}
@@ -0,0 +1,2 @@
Type parameter Y has 1 usage that is not safe to delete.
Of those 0 usages are in strings, comments, or non-code files.
@@ -0,0 +1,3 @@
class B {
A<Int, String, Object> a = new A<Int, String, Object>()
}
@@ -0,0 +1,5 @@
class A<<caret>X, Y: X, Z> {
fun foo(x: X) {
}
}
@@ -0,0 +1,2 @@
Type parameter X has 2 usages that are not safe to delete.
Of those 0 usages are in strings, comments, or non-code files.
@@ -0,0 +1,3 @@
class B {
A<Int, String, Object> a = new A<Int, String, Object>()
}
@@ -0,0 +1,2 @@
class A<<caret>X, Y, Z> where Y: X {
}
@@ -0,0 +1,2 @@
Type parameter X has 1 usage that is not safe to delete.
Of those 0 usages are in strings, comments, or non-code files.
@@ -0,0 +1,5 @@
class B {
void bar(A<Number> a) {
a.<Int, String>foo()
}
}
@@ -0,0 +1,5 @@
class A<<caret>X> {
fun foo<Y, Z>() where Y: X {
}
}
@@ -0,0 +1,2 @@
Type parameter X has 1 usage that is not safe to delete.
Of those 0 usages are in strings, comments, or non-code files.
@@ -0,0 +1,3 @@
class B {
A a = new A()
}
@@ -0,0 +1,3 @@
class B {
A a = new A()
}
@@ -0,0 +1 @@
class A<<caret>X, Y, Z>
@@ -0,0 +1,3 @@
class B {
A<String, Int, Object> a = new A<String, Int, Object>()
}
@@ -0,0 +1,3 @@
class B {
A<Int, Object> a = new A<Int, Object>()
}
@@ -0,0 +1 @@
class A<out <caret>X: Comparable<X>, in Y: Number, Z>
@@ -0,0 +1,3 @@
class B {
A<String, Int, Object> a = new A<String, Int, Object>()
}
@@ -0,0 +1,3 @@
class B {
A<String, Object> a = new A<String, Object>()
}
@@ -0,0 +1 @@
class A<out X: Comparable<X>, in <caret>Y: Number, Z>
@@ -0,0 +1 @@
class A<out X: Comparable<X>, Z>
@@ -0,0 +1,3 @@
class B<T, U> extends A<T, String, U> {
}
@@ -0,0 +1,3 @@
class B<T, U> extends A<String, U> {
}
@@ -0,0 +1 @@
open class A<<caret>X, Y, Z>
@@ -0,0 +1,3 @@
class B<T, U> extends A<String, T, U> {
}
@@ -0,0 +1,3 @@
class B<T, U> extends A<T, U> {
}
@@ -0,0 +1 @@
open class A<<caret>X, Y, Z>
@@ -0,0 +1,3 @@
class B {
A<Int, String, Object> a = new A<Int, String, Object>()
}
@@ -0,0 +1,3 @@
class B {
A<String, Object> a = new A<String, Object>()
}
@@ -0,0 +1 @@
class A<<caret>X, Y, Z>
@@ -0,0 +1,3 @@
class B {
A<Int, String, Object> a = new A<Int, String, Object>()
}
@@ -0,0 +1,3 @@
class B {
A<Int, Object> a = new A<Int, Object>()
}
@@ -0,0 +1 @@
class A<X, <caret>Y, Z>
@@ -0,0 +1,3 @@
class B {
A<Int, String, Object> a = new A<Int, String, Object>()
}
@@ -0,0 +1,3 @@
class B {
A<Int, String> a = new A<Int, String>()
}
@@ -0,0 +1 @@
class A<X, Y, <caret>Z>
@@ -0,0 +1,5 @@
class B {
void bar(A<String> a) {
a.<Int, Object>foo()
}
}
@@ -0,0 +1,5 @@
class B {
void bar(A<String> a) {
a.<Object>foo()
}
}
@@ -0,0 +1,5 @@
class A<X> {
fun foo<<caret>Y, Z>() {
}
}
@@ -0,0 +1,5 @@
class A<X> {
fun foo<Z>() {
}
}
@@ -0,0 +1,5 @@
class B {
void bar(A<String> a) {
a.<Int, Object>foo()
}
}
@@ -0,0 +1,5 @@
class B {
void bar(A<String> a) {
a.<Int>foo()
}
}
@@ -0,0 +1,5 @@
class A<X> {
fun foo<Y, <caret>Z>() {
}
}
@@ -0,0 +1,5 @@
class A<X> {
fun foo<Y>() {
}
}
@@ -0,0 +1,3 @@
class B {
A<Int> a = new A<Int>()
}
@@ -0,0 +1,3 @@
class B {
A a = new A()
}
@@ -0,0 +1 @@
class A<<caret>X>
@@ -0,0 +1,5 @@
class B {
void bar(A<String> a) {
a.<Int>foo()
}
}
@@ -0,0 +1,5 @@
class B {
void bar(A<String> a) {
a.foo()
}
}
@@ -0,0 +1,5 @@
class A<X> {
fun foo<<caret>Y>() {
}
}
@@ -0,0 +1,5 @@
class A<X> {
fun foo() {
}
}

Some files were not shown because too many files have changed in this diff Show More