Adding type arguments if necessary.

#KT-2637 in progress
This commit is contained in:
Evgeny Gerashchenko
2013-07-11 15:46:23 +04:00
parent 99bfd6d477
commit 0367f1836d
22 changed files with 284 additions and 11 deletions
@@ -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();
@@ -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;
}
}