Change Signature: Process Kotlin-based usages when changing signature of the Java function

This commit is contained in:
Alexey Sedunov
2014-11-20 18:18:46 +03:00
parent 18c10c9079
commit 591c409854
14 changed files with 250 additions and 13 deletions
@@ -22,6 +22,13 @@ import org.jetbrains.jet.lang.resolve.java.JvmResolverForModule
import com.intellij.openapi.project.Project
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver
import org.jetbrains.jet.lang.resolve.BindingContext
import com.intellij.psi.PsiMethod
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.asJava.KotlinLightMethod
import org.jetbrains.jet.lang.resolve.java.resolveMethod
import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaMethodImpl
import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaConstructorImpl
import org.jetbrains.jet.lang.resolve.java.resolveConstructor
public object JavaResolveExtension : CacheExtension<(PsiElement) -> Pair<JavaDescriptorResolver, BindingContext>> {
override val platform: TargetPlatform = TargetPlatform.JVM
@@ -38,4 +45,17 @@ public object JavaResolveExtension : CacheExtension<(PsiElement) -> Pair<JavaDes
public fun getContext(project: Project, element: PsiElement): BindingContext =
KotlinCacheService.getInstance(project)[this](element).second
}
fun PsiMethod.getJavaMethodDescriptor(): FunctionDescriptor {
if (this is KotlinLightMethod) throw AssertionError("Light methods are not allowed here: ${origin?.getText()}")
val resolver = JavaResolveExtension.getResolver(getProject(), this)
val methodDescriptor = when {
this.isConstructor() -> resolver.resolveConstructor(JavaConstructorImpl(this))
else -> resolver.resolveMethod(JavaMethodImpl(this))
}
assert(methodDescriptor != null) { "No descriptor found for " + getText() }
return methodDescriptor!!
}
@@ -22,14 +22,12 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiType;
import com.intellij.refactoring.changeSignature.ChangeInfo;
import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor;
import com.intellij.refactoring.changeSignature.JavaChangeInfo;
import com.intellij.refactoring.changeSignature.ParameterInfoImpl;
import com.intellij.refactoring.changeSignature.*;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.Function;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.ContainerUtil;
import kotlin.Function1;
import kotlin.KotlinPackage;
import kotlin.Pair;
import org.jetbrains.annotations.NotNull;
@@ -44,6 +42,7 @@ import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.plugin.JetLanguage;
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
import java.util.Collection;
import java.util.HashMap;
@@ -339,4 +338,45 @@ public class JetChangeInfo implements ChangeInfo {
primaryMethodUpdated = true;
javaChangeInfo = null;
}
@NotNull
public static JetChangeInfo fromJavaChangeInfo(
@NotNull ChangeInfo javaChangeInfo,
@NotNull JetMethodDescriptor originalChangeSignatureDescriptor
) {
PsiMethod method = (PsiMethod) javaChangeInfo.getMethod();
FunctionDescriptor functionDescriptor = ResolvePackage.getJavaMethodDescriptor(method);
final List<ValueParameterDescriptor> parameterDescriptors = functionDescriptor.getValueParameters();
List<JetParameterInfo> newParameters = KotlinPackage.map(
KotlinPackage.withIndices(javaChangeInfo.getNewParameters()),
new Function1<Pair<? extends Integer, ? extends ParameterInfo>, JetParameterInfo>() {
@Override
public JetParameterInfo invoke(Pair<? extends Integer, ? extends ParameterInfo> pair) {
ParameterInfo info = pair.getSecond();
JetParameterInfo jetParameterInfo = new JetParameterInfo(
info.getOldIndex(),
info.getName(),
parameterDescriptors.get(pair.getFirst()).getType(),
null,
null
);
jetParameterInfo.setDefaultValueText(info.getDefaultValue());
return jetParameterInfo;
}
}
);
return new JetChangeInfo(
originalChangeSignatureDescriptor,
javaChangeInfo.getNewName(),
functionDescriptor.getReturnType(),
"",
functionDescriptor.getVisibility(),
newParameters,
method,
new JetGeneratedInfo()
);
}
}
@@ -62,14 +62,21 @@ public final class JetChangeSignatureData implements JetMethodDescriptor {
this.descriptorsForSignatureChange = descriptorsForSignatureChange;
final List<JetParameter> valueParameters = this.baseDeclaration instanceof JetFunction
? ((JetFunction) this.baseDeclaration).getValueParameters()
: ((JetClass) this.baseDeclaration).getPrimaryConstructorParameters();
: this.baseDeclaration instanceof JetClass
? ((JetClass) this.baseDeclaration).getPrimaryConstructorParameters()
: null;
this.parameters = new ArrayList<JetParameterInfo>(
ContainerUtil.map(this.baseDescriptor.getValueParameters(), new Function<ValueParameterDescriptor, JetParameterInfo>() {
@Override
public JetParameterInfo fun(ValueParameterDescriptor param) {
JetParameter parameter = valueParameters.get(param.getIndex());
return new JetParameterInfo(param.getIndex(), param.getName().asString(), param.getType(),
parameter.getDefaultValue(), parameter.getValOrVarNode());
JetParameter parameter = valueParameters != null ? valueParameters.get(param.getIndex()) : null;
return new JetParameterInfo(
param.getIndex(),
param.getName().asString(),
param.getType(),
parameter != null ? parameter.getDefaultValue() : null,
parameter != null ? parameter.getValOrVarNode() : null
);
}
}));
}
@@ -19,10 +19,13 @@ package org.jetbrains.jet.plugin.refactoring.changeSignature;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.changeSignature.*;
import com.intellij.refactoring.rename.ResolveSnapshotProvider;
import com.intellij.refactoring.util.MoveRenameUsageInfo;
import com.intellij.refactoring.util.TextOccurrencesUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.Function;
@@ -31,6 +34,7 @@ import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.KotlinLightMethod;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.impl.FunctionDescriptorImpl;
import org.jetbrains.jet.lang.psi.*;
@@ -41,8 +45,10 @@ import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
import org.jetbrains.jet.plugin.refactoring.changeSignature.usages.*;
import org.jetbrains.jet.plugin.references.JetSimpleNameReference;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -238,11 +244,31 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
});
}
private JetMethodDescriptor originalJavaMethodDescriptor;
private static boolean isJavaMethodUsage(UsageInfo usageInfo) {
if (!(usageInfo instanceof MoveRenameUsageInfo)) return false;
PsiElement referencedElement = ((MoveRenameUsageInfo) usageInfo).getReferencedElement();
return referencedElement instanceof PsiMethod && !(referencedElement instanceof KotlinLightMethod);
}
@Nullable
private static UsageInfo createFunctionCallUsage(
UsageInfo originalUsageInfo,
JetChangeInfo javaMethodChangeInfo
) {
JetCallElement callElement = PsiTreeUtil.getParentOfType(originalUsageInfo.getElement(), JetCallElement.class);
return callElement != null ? new JavaMethodKotlinCallUsage(callElement, javaMethodChangeInfo) : null;
}
@Override
public boolean processUsage(ChangeInfo changeInfo, UsageInfo usageInfo, boolean beforeMethodChange, UsageInfo[] usages) {
PsiElement method = changeInfo.getMethod();
boolean isJavaMethodUsage = isJavaMethodUsage(usageInfo);
if (usageInfo instanceof KotlinWrapperForJavaUsageInfos) {
JavaChangeInfo javaChangeInfo = ((JetChangeInfo) changeInfo).getOrCreateJavaChangeInfo();
assert javaChangeInfo != null : "JavaChangeInfo not found: " + changeInfo.getMethod().getText();
assert javaChangeInfo != null : "JavaChangeInfo not found: " + method.getText();
UsageInfo[] javaUsageInfos = ((KotlinWrapperForJavaUsageInfos) usageInfo).getJavaUsageInfos();
ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions();
@@ -257,13 +283,56 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
}
}
if (beforeMethodChange) return true;
if (beforeMethodChange) {
if (isJavaMethodUsage) {
FunctionDescriptor methodDescriptor = ResolvePackage.getJavaMethodDescriptor((PsiMethod) method);
JetChangeSignatureData changeSignatureData =
new JetChangeSignatureData(methodDescriptor, method, Collections.singletonList(methodDescriptor));
if (changeSignatureData != originalJavaMethodDescriptor) {
originalJavaMethodDescriptor = changeSignatureData;
}
}
return true;
}
PsiElement element = usageInfo.getElement();
if (element != null)
return usageInfo instanceof JetUsageInfo ? ((JetUsageInfo) usageInfo).processUsage((JetChangeInfo) changeInfo, element) : true;
else
if (element == null) return false;
if (isJavaMethodUsage && originalJavaMethodDescriptor != null) {
JetChangeInfo javaMethodChangeInfo = JetChangeInfo.fromJavaChangeInfo(changeInfo, originalJavaMethodDescriptor);
originalJavaMethodDescriptor = null;
for (int i = 0; i < usages.length; i++) {
UsageInfo oldUsageInfo = usages[i];
if (!isJavaMethodUsage(oldUsageInfo)) continue;
UsageInfo newUsageInfo = createFunctionCallUsage(oldUsageInfo, javaMethodChangeInfo);
if (newUsageInfo != null) {
usages[i] = newUsageInfo;
if (oldUsageInfo == usageInfo) {
usageInfo = newUsageInfo;
}
}
}
}
if (usageInfo instanceof JavaMethodKotlinCallUsage) {
return ((JavaMethodKotlinCallUsage) usageInfo).processUsage();
}
if (usageInfo instanceof MoveRenameUsageInfo && isJavaMethodUsage) {
JetSimpleNameExpression callee = PsiTreeUtil.getParentOfType(usageInfo.getElement(), JetSimpleNameExpression.class, false);
PsiReference ref = callee != null ? callee.getReference() : null;
if (ref instanceof JetSimpleNameReference) {
((JetSimpleNameReference) ref).handleElementRename(((PsiMethod)method).getName());
return true;
}
return false;
}
return usageInfo instanceof JetUsageInfo ? ((JetUsageInfo) usageInfo).processUsage((JetChangeInfo) changeInfo, element) : true;
}
@Override
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2014 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.changeSignature.usages
import org.jetbrains.jet.lang.psi.JetCallElement
import com.intellij.psi.PsiElement
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetChangeInfo
import com.intellij.usageView.UsageInfo
public class JavaMethodKotlinCallUsage(
val callElement: JetCallElement,
val javaMethodChangeInfo: JetChangeInfo): UsageInfo(javaMethodChangeInfo.getMethod()) {
private val delegateUsage = JetFunctionCallUsage(callElement, javaMethodChangeInfo.getMethod(), false)
fun processUsage(): Boolean = delegateUsage.processUsage(javaMethodChangeInfo, callElement)
}
@@ -0,0 +1,4 @@
fun test() {
J("1")
J("3")
}
@@ -0,0 +1,5 @@
class J {
J(String s) {
}
}
@@ -0,0 +1,4 @@
fun test() {
J("1", 2)
J("3", 4)
}
@@ -0,0 +1,5 @@
class J {
<caret>J(String s, int n) {
}
}
@@ -0,0 +1,6 @@
import J.bar
fun test() {
J.bar("1")
J.bar("3")
}
@@ -0,0 +1,5 @@
class J {
static void bar(String s) {
}
}
@@ -0,0 +1,6 @@
import J.foo
fun test() {
J.foo("1", 2)
J.foo("3", 4)
}
@@ -0,0 +1,5 @@
class J {
static void <caret>foo(String s, int n) {
}
}
@@ -28,6 +28,7 @@ import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor;
import com.intellij.refactoring.changeSignature.ParameterInfoImpl;
import com.intellij.refactoring.changeSignature.ThrownExceptionInfo;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.VisibilityUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -328,6 +329,36 @@ public class JetChangeSignatureTest extends KotlinCodeInsightTestCase {
doTest(changeInfo);
}
public void testJavaMethodKotlinUsages() throws Exception {
doJavaTest(
new JavaRefactoringProvider() {
@NotNull
@Override
String getNewName(@NotNull PsiMethod method) {
return "bar";
}
@NotNull
@Override
ParameterInfoImpl[] getNewParameters(@NotNull PsiMethod method) {
return ArrayUtil.remove(super.getNewParameters(method), 1);
}
}
);
}
public void testJavaConstructorKotlinUsages() throws Exception {
doJavaTest(
new JavaRefactoringProvider() {
@NotNull
@Override
ParameterInfoImpl[] getNewParameters(@NotNull PsiMethod method) {
return ArrayUtil.remove(super.getNewParameters(method), 1);
}
}
);
}
public void testFunctionRenameJavaUsages() throws Exception {
JetChangeInfo changeInfo = getChangeInfo();
changeInfo.setNewName("bar");