Change Signature: Support Kotlin functions overriding Java methods
#KT-5856 Fixed
This commit is contained in:
+6
-28
@@ -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
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -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("");
|
||||
}
|
||||
}
|
||||
+20
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user