Change Signature: Support Kotlin functions overriding Java methods

#KT-5856 Fixed
This commit is contained in:
Alexey Sedunov
2015-06-24 13:03:03 +03:00
parent 38982fe641
commit 4eeff03525
8 changed files with 156 additions and 39 deletions
@@ -60,37 +60,15 @@ public class JetChangeSignatureProcessor extends ChangeSignatureProcessorBase {
@NotNull
@Override
protected UsageInfo[] findUsages() {
UsageInfo[] kotlinUsages = super.findUsages();
List<UsageInfo> allUsages = new ArrayList<UsageInfo>();
JavaChangeInfo javaChangeInfo = getChangeInfo().getOrCreateJavaChangeInfo();
if (javaChangeInfo == null) return kotlinUsages;
if (javaChangeInfo != null) {
UsageInfo[] javaUsages = new JavaChangeSignatureUsageProcessor().findUsages(javaChangeInfo);
allUsages.add(new KotlinWrapperForJavaUsageInfos(javaUsages, getChangeInfo().getMethod()));
}
KotlinPackage.filterIsInstanceTo(super.findUsages(), allUsages, JetUsageInfo.class);
List<UsageInfo> javaUsages = new ArrayList<UsageInfo>();
KotlinPackage.filterNotTo(
new JavaChangeSignatureUsageProcessor().findUsages(javaChangeInfo),
javaUsages,
new Function1<UsageInfo, Boolean>() {
@Override
public Boolean invoke(UsageInfo info) {
// Filter overriding declarations since they are already found by our usage processor
return info instanceof OverriderUsageInfo;
}
}
);
Pair<List<? extends UsageInfo>, List<? extends UsageInfo>> usagesByKotlinProcessor = KotlinPackage.partition(
kotlinUsages,
new Function1<UsageInfo, Boolean>() {
@Override
public Boolean invoke(UsageInfo info) {
return info instanceof JetUsageInfo;
}
}
);
javaUsages.addAll(usagesByKotlinProcessor.getSecond());
List<UsageInfo> allUsages = new ArrayList<UsageInfo>();
allUsages.add(new KotlinWrapperForJavaUsageInfos(javaUsages.toArray(new UsageInfo[javaUsages.size()]), getChangeInfo().getMethod()));
allUsages.addAll(usagesByKotlinProcessor.getFirst());
return allUsages.toArray(new UsageInfo[allUsages.size()]);
}
@@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
@@ -45,6 +46,8 @@ import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.analyzer.AnalyzerPackage;
import org.jetbrains.kotlin.asJava.AsJavaPackage;
import org.jetbrains.kotlin.asJava.KotlinLightMethod;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.idea.JetFileType;
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
@@ -91,6 +94,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
else {
findSAMUsages(info, result);
findConstructorDelegationUsages(info, result);
findKotlinOverrides(info, result);
}
return result.toArray(new UsageInfo[result.size()]);
@@ -334,7 +338,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
JetType samCallType = context.getType(callExpression);
if (samCallType == null) continue;
result.add(new DeferredSAMUsage(functionLiteral, functionDescriptor, samCallType));
result.add(new DeferredJavaMethodOverrideOrSAMUsage(functionLiteral, functionDescriptor, samCallType));
}
}
@@ -358,6 +362,21 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
);
}
private static void findKotlinOverrides(ChangeInfo changeInfo, Set<UsageInfo> result) {
PsiElement method = changeInfo.getMethod();
if (!RefactoringPackage.isTrueJavaMethod(method)) return;
for (PsiMethod overridingMethod : OverridingMethodsSearch.search((PsiMethod) method)) {
PsiElement unwrappedElement = AsJavaPackage.getNamedUnwrappedElement(overridingMethod);
if (!(unwrappedElement instanceof JetNamedFunction)) continue;
JetNamedFunction function = (JetNamedFunction) unwrappedElement;
FunctionDescriptor functionDescriptor = (FunctionDescriptor) ResolvePackage.resolveToDescriptor(function);
result.add(new DeferredJavaMethodOverrideOrSAMUsage(function, functionDescriptor, null));
}
}
@Override
public MultiMap<PsiElement, String> findConflicts(ChangeInfo info, Ref<UsageInfo[]> refUsages) {
MultiMap<PsiElement, String> result = new MultiMap<PsiElement, String>();
@@ -692,7 +711,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
}
if (usage instanceof OverriderUsageInfo) {
PsiMethod overridingMethod = ((OverriderUsageInfo)usage).getOverridingMethod();
if (overridingMethod != null) {
if (overridingMethod != null && !(overridingMethod instanceof KotlinLightMethod)) {
nullabilityPropagator.processMethod(overridingMethod);
}
}
@@ -22,24 +22,24 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo
import org.jetbrains.kotlin.psi.JetConstructorDelegationCall
import org.jetbrains.kotlin.psi.JetFunction
import org.jetbrains.kotlin.psi.JetFunctionLiteral
import org.jetbrains.kotlin.types.JetType
public abstract class JavaMethodDeferredKotlinUsage<T: PsiElement>(element: T): UsageInfo(element) {
abstract fun resolve(javaMethodChangeInfo: JetChangeInfo): JavaMethodKotlinUsageWithDelegate<T>
}
public class DeferredSAMUsage(
val functionLiteral: JetFunctionLiteral,
public class DeferredJavaMethodOverrideOrSAMUsage(
val function: JetFunction,
val functionDescriptor: FunctionDescriptor,
val samCallType: JetType
): JavaMethodDeferredKotlinUsage<JetFunctionLiteral>(functionLiteral) {
override fun resolve(javaMethodChangeInfo: JetChangeInfo): JavaMethodKotlinUsageWithDelegate<JetFunctionLiteral> {
return object : JavaMethodKotlinUsageWithDelegate<JetFunctionLiteral>(functionLiteral, javaMethodChangeInfo) {
val samCallType: JetType?
): JavaMethodDeferredKotlinUsage<JetFunction>(function) {
override fun resolve(javaMethodChangeInfo: JetChangeInfo): JavaMethodKotlinUsageWithDelegate<JetFunction> {
return object : JavaMethodKotlinUsageWithDelegate<JetFunction>(function, javaMethodChangeInfo) {
override val delegateUsage = JetFunctionDefinitionUsage(
functionLiteral,
function,
functionDescriptor,
javaMethodChangeInfo.methodDescriptor.originalPrimaryFunction, samCallType
javaMethodChangeInfo.methodDescriptor.originalPrimaryFunction,
samCallType
)
}
}
@@ -0,0 +1,25 @@
open class X: A() {
fun foo(x: Int): String? {
return super.foo(1)
}
}
open class Y: B() {
fun foo(x: Int): String? {
return 0
}
}
open class Z: X() {
fun foo(x: Int): String? {
return 0
}
}
fun test() {
A().foo(1)
B().foo(1)
X().foo(1)
Y().foo(1)
Z().foo(1)
}
@@ -0,0 +1,25 @@
import java.lang.Override;
import java.lang.String;
class A {
String foo(int x) {
return 0;
}
}
class B extends A {
@Override
String foo(int x) {
return super.foo(x);
}
}
class Test {
void test() {
new A().foo(1);
new B().foo(1);
new X().foo(1);
new Y().foo(1);
new Z().foo(1);
}
}
@@ -0,0 +1,25 @@
open class X: A() {
fun foo(s: String): Int {
return super.foo(s)
}
}
open class Y: B() {
fun foo(s: String): Int {
return 0
}
}
open class Z: X() {
fun foo(s: String): Int {
return 0
}
}
fun test() {
A().foo("")
B().foo("")
X().foo("")
Y().foo("")
Z().foo("")
}
@@ -0,0 +1,25 @@
import java.lang.Override;
import java.lang.String;
class A {
int <caret>foo(String s) {
return 0;
}
}
class B extends A {
@Override
int foo(String s) {
return super.foo(s);
}
}
class Test {
void test() {
new A().foo("");
new B().foo("");
new X().foo("");
new Y().foo("");
new Z().foo("");
}
}
@@ -975,6 +975,26 @@ public class JetChangeSignatureTest extends KotlinCodeInsightTestCase {
doTest(changeInfo);
}
public void testJavaMethodOverrides() throws Exception {
doJavaTest(
new JavaRefactoringProvider() {
@Nullable
@Override
PsiType getNewReturnType(@NotNull PsiMethod method) {
return PsiType.getJavaLangString(getPsiManager(), GlobalSearchScope.allScope(getProject()));
}
@NotNull
@Override
ParameterInfoImpl[] getNewParameters(@NotNull PsiMethod method) {
ParameterInfoImpl[] newParameters = super.getNewParameters(method);
newParameters[0] = new ParameterInfoImpl(-1, "x", PsiType.INT, "1");
return newParameters;
}
}
);
}
@NotNull
@Override
protected String getTestDataPath() {