Adding type arguments if necessary.
#KT-2637 in progress
This commit is contained in:
@@ -61,6 +61,13 @@ public class JetPsiFactory {
|
||||
return callExpression.getValueArgumentList();
|
||||
}
|
||||
|
||||
public static JetTypeArgumentList createTypeArguments(Project project, String text) {
|
||||
JetProperty property = createProperty(project, "val x = foo" + text + "()");
|
||||
JetExpression initializer = property.getInitializer();
|
||||
JetCallExpression callExpression = (JetCallExpression) initializer;
|
||||
return callExpression.getTypeArgumentList();
|
||||
}
|
||||
|
||||
public static JetTypeReference createType(Project project, String type) {
|
||||
JetProperty property = createProperty(project, "val x : " + type);
|
||||
return property.getTypeRef();
|
||||
|
||||
+125
-10
@@ -1,5 +1,6 @@
|
||||
package org.jetbrains.jet.plugin.refactoring.inline;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.codeInsight.highlighting.HighlightManager;
|
||||
import com.intellij.lang.Language;
|
||||
@@ -11,6 +12,7 @@ import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.wm.StatusBar;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
@@ -21,12 +23,26 @@ import com.intellij.refactoring.HelpID;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.refactoring.util.RefactoringMessageDialog;
|
||||
import com.intellij.util.Function;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
|
||||
import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.lazy.ResolveSession;
|
||||
import org.jetbrains.jet.lang.resolve.lazy.ResolveSessionUtils;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
import org.jetbrains.jet.plugin.JetLanguage;
|
||||
import org.jetbrains.jet.plugin.codeInsight.ReferenceToClassesShortening;
|
||||
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
public class KotlinInlineLocalHandler extends InlineActionHandler {
|
||||
@Override
|
||||
@@ -44,7 +60,7 @@ public class KotlinInlineLocalHandler extends InlineActionHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inlineElement(final Project project, Editor editor, PsiElement element) {
|
||||
public void inlineElement(Project project, Editor editor, PsiElement element) {
|
||||
final JetProperty val = (JetProperty) element;
|
||||
String name = val.getName();
|
||||
|
||||
@@ -82,6 +98,8 @@ public class KotlinInlineLocalHandler extends InlineActionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
final String typeArgumentsForCall = getTypeArgumentsStringForCall(initializer);
|
||||
|
||||
PsiReference[] referencesArray = references.toArray(references.toArray(new PsiReference[references.size()]));
|
||||
|
||||
if (editor != null && !ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
@@ -107,7 +125,9 @@ public class KotlinInlineLocalHandler extends InlineActionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
final List<JetExpression> inlinedExpressions = Lists.newArrayList();
|
||||
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
@@ -118,13 +138,7 @@ public class KotlinInlineLocalHandler extends InlineActionHandler {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (referenceElement.getParent() instanceof JetSimpleNameStringTemplateEntry &&
|
||||
!(initializer instanceof JetSimpleNameExpression)) {
|
||||
referenceElement.getParent().replace(JetPsiFactory.createBlockStringTemplateEntry(project, initializer));
|
||||
}
|
||||
else {
|
||||
referenceElement.replace(initializer.copy());
|
||||
}
|
||||
inlinedExpressions.add(replaceExpression(referenceElement, initializer));
|
||||
}
|
||||
|
||||
for (PsiElement assignment : assignments) {
|
||||
@@ -132,9 +146,110 @@ public class KotlinInlineLocalHandler extends InlineActionHandler {
|
||||
}
|
||||
|
||||
val.delete();
|
||||
|
||||
if (typeArgumentsForCall != null) {
|
||||
addTypeArguments(typeArgumentsForCall, inlinedExpressions);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, RefactoringBundle.message("inline.command", name), null);
|
||||
}
|
||||
|
||||
private static void addTypeArguments(@NotNull String typeArguments, @NotNull List<JetExpression> inlinedExpressions) {
|
||||
JetFile containingFile = (JetFile) inlinedExpressions.get(0).getContainingFile();
|
||||
List<JetCallExpression> callsToAddArguments = Lists.newArrayList();
|
||||
|
||||
ResolveSession resolveSession = AnalyzerFacadeWithCache.getLazyResolveSession(containingFile);
|
||||
for (JetExpression inlinedExpression : inlinedExpressions) {
|
||||
JetCallExpression callExpression = getCallExpression(inlinedExpression);
|
||||
assert callExpression != null : "can't find call expression for " + inlinedExpression.getText();
|
||||
|
||||
if (hasIncompleteTypeInferenceDiagnostic(callExpression, resolveSession) && callExpression.getTypeArgumentList() == null) {
|
||||
callsToAddArguments.add(callExpression);
|
||||
}
|
||||
}
|
||||
|
||||
for (JetCallExpression call : callsToAddArguments) {
|
||||
call.addAfter(JetPsiFactory.createTypeArguments(containingFile.getProject(), "<" + typeArguments + ">"),
|
||||
call.getCalleeExpression());
|
||||
ReferenceToClassesShortening.compactReferenceToClasses(Arrays.asList(call.getTypeArgumentList()));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getTypeArgumentsStringForCall(@NotNull JetExpression initializer) {
|
||||
JetCallExpression callExpression = getCallExpression(initializer);
|
||||
if (callExpression == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JetExpression callee = callExpression.getCalleeExpression();
|
||||
ResolveSession resolveSession = AnalyzerFacadeWithCache.getLazyResolveSession((JetFile) initializer.getContainingFile());
|
||||
BindingContext context = ResolveSessionUtils.resolveToExpression(resolveSession, initializer);
|
||||
ResolvedCall<? extends CallableDescriptor> call = context.get(BindingContext.RESOLVED_CALL, callee);
|
||||
if (call == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<JetType> typeArguments = Lists.newArrayList();
|
||||
Map<TypeParameterDescriptor, JetType> typeArgumentMap = call.getTypeArguments();
|
||||
for (TypeParameterDescriptor typeParameter : call.getCandidateDescriptor().getTypeParameters()) {
|
||||
typeArguments.add(typeArgumentMap.get(typeParameter));
|
||||
}
|
||||
|
||||
return StringUtil.join(typeArguments, new Function<JetType, String>() {
|
||||
@Override
|
||||
public String fun(JetType type) {
|
||||
return DescriptorRenderer.TEXT.renderType(type);
|
||||
}
|
||||
}, ", ");
|
||||
}
|
||||
|
||||
private static boolean hasIncompleteTypeInferenceDiagnostic(
|
||||
@NotNull JetCallExpression callExpression,
|
||||
@NotNull ResolveSession resolveSession
|
||||
) {
|
||||
JetExpression callee = callExpression.getCalleeExpression();
|
||||
BindingContext context = ResolveSessionUtils.resolveToExpression(resolveSession, callExpression);
|
||||
for (Diagnostic diagnostic : context.getDiagnostics()) {
|
||||
if (diagnostic.getFactory() == Errors.TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER && diagnostic.getPsiElement() == callee) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JetExpression replaceExpression(
|
||||
@NotNull PsiElement referenceElement,
|
||||
@NotNull JetExpression newExpression
|
||||
) {
|
||||
if (referenceElement.getParent() instanceof JetSimpleNameStringTemplateEntry &&
|
||||
!(newExpression instanceof JetSimpleNameExpression)) {
|
||||
JetBlockStringTemplateEntry templateEntry =
|
||||
(JetBlockStringTemplateEntry) referenceElement.getParent().replace(
|
||||
JetPsiFactory.createBlockStringTemplateEntry(referenceElement.getProject(), newExpression));
|
||||
JetExpression expression = templateEntry.getExpression();
|
||||
assert expression != null;
|
||||
return expression;
|
||||
}
|
||||
return (JetExpression) referenceElement.replace(newExpression.copy());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JetCallExpression getCallExpression(@NotNull JetExpression expression) {
|
||||
if (expression instanceof JetParenthesizedExpression) {
|
||||
JetExpression inner = ((JetParenthesizedExpression) expression).getExpression();
|
||||
return inner == null ? null : getCallExpression(inner);
|
||||
}
|
||||
if (expression instanceof JetCallExpression) {
|
||||
return (JetCallExpression) expression;
|
||||
}
|
||||
if (expression instanceof JetQualifiedExpression) {
|
||||
JetExpression selector = ((JetQualifiedExpression) expression).getSelectorExpression();
|
||||
return selector == null ? null : getCallExpression(selector);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
val v : List<Int> = ArrayList(listOf())
|
||||
ArrayList(<caret>v)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
ArrayList(ArrayList<Int>(listOf()))
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
val v : List<Int> = listOf()
|
||||
val copy: List<Int> = ArrayList(<caret>v)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
val copy: List<Int> = ArrayList(listOf())
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
val v : List<Int> = listOf()
|
||||
val copy1: List<Int> = ArrayList(<caret>v)
|
||||
val copy2: = ArrayList(v)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
val copy1: List<Int> = ArrayList(listOf())
|
||||
val copy2: = ArrayList(listOf<Int>())
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
fun f() {
|
||||
val v : List<Int> = listOf()
|
||||
"$<caret>v"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun f() {
|
||||
"${listOf<Int>()}"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
val v : List<Int> = listOf()
|
||||
ArrayList(<caret>v)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
ArrayList(listOf<Int>())
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
fun f() {
|
||||
val v : List<Int> = (listOf())
|
||||
val vv = <caret>v
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun f() {
|
||||
val vv = (listOf<Int>())
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun <T> String.ext(): List<T> = listOf()
|
||||
|
||||
fun f() {
|
||||
val v : List<Int> = ("".ext())
|
||||
val vv = <caret>v
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun <T> String.ext(): List<T> = listOf()
|
||||
|
||||
fun f() {
|
||||
val vv = ("".ext<Int>())
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
fun f() {
|
||||
val v : List<Int> = listOf()
|
||||
val vv = <caret>v
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun f() {
|
||||
val vv = listOf<Int>()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
val v = listOf(1, 2, 3)
|
||||
ArrayList(<caret>v)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
ArrayList(listOf(1, 2, 3))
|
||||
}
|
||||
@@ -5,9 +5,11 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.InTextDirectivesUtils;
|
||||
import org.jetbrains.jet.plugin.JetWithJdkAndRuntimeLightProjectDescriptor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -51,4 +53,10 @@ public abstract class AbstractInlineTest extends LightCodeInsightFixtureTestCase
|
||||
assertFalse(afterFileExists);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ import org.jetbrains.jet.plugin.refactoring.inline.AbstractInlineTest;
|
||||
/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/refactoring/inline")
|
||||
@InnerTestClasses({InlineTestGenerated.AddParenthesis.class})
|
||||
@InnerTestClasses({InlineTestGenerated.AddParenthesis.class, InlineTestGenerated.ExplicateTypeArgument.class})
|
||||
public class InlineTestGenerated extends AbstractInlineTest {
|
||||
public void testAllFilesPresentInInline() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/refactoring/inline"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
@@ -255,10 +255,64 @@ public class InlineTestGenerated extends AbstractInlineTest {
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/inline/explicateTypeArgument")
|
||||
public static class ExplicateTypeArgument extends AbstractInlineTest {
|
||||
public void testAllFilesPresentInExplicateTypeArgument() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/refactoring/inline/explicateTypeArgument"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("DeeperNestedCall.kt")
|
||||
public void testDeeperNestedCall() throws Exception {
|
||||
doTest("idea/testData/refactoring/inline/explicateTypeArgument/DeeperNestedCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("EnoughDontExplicate.kt")
|
||||
public void testEnoughDontExplicate() throws Exception {
|
||||
doTest("idea/testData/refactoring/inline/explicateTypeArgument/EnoughDontExplicate.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ExplicateForSome.kt")
|
||||
public void testExplicateForSome() throws Exception {
|
||||
doTest("idea/testData/refactoring/inline/explicateTypeArgument/ExplicateForSome.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InStringTemplate.kt")
|
||||
public void testInStringTemplate() throws Exception {
|
||||
doTest("idea/testData/refactoring/inline/explicateTypeArgument/InStringTemplate.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NestedCall.kt")
|
||||
public void testNestedCall() throws Exception {
|
||||
doTest("idea/testData/refactoring/inline/explicateTypeArgument/NestedCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Parenthesized.kt")
|
||||
public void testParenthesized() throws Exception {
|
||||
doTest("idea/testData/refactoring/inline/explicateTypeArgument/Parenthesized.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Qualified.kt")
|
||||
public void testQualified() throws Exception {
|
||||
doTest("idea/testData/refactoring/inline/explicateTypeArgument/Qualified.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Simplest.kt")
|
||||
public void testSimplest() throws Exception {
|
||||
doTest("idea/testData/refactoring/inline/explicateTypeArgument/Simplest.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TrivialDontExplicate.kt")
|
||||
public void testTrivialDontExplicate() throws Exception {
|
||||
doTest("idea/testData/refactoring/inline/explicateTypeArgument/TrivialDontExplicate.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
TestSuite suite = new TestSuite("InlineTestGenerated");
|
||||
suite.addTestSuite(InlineTestGenerated.class);
|
||||
suite.addTestSuite(AddParenthesis.class);
|
||||
suite.addTestSuite(ExplicateTypeArgument.class);
|
||||
return suite;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user