Adding parameter types for functions if necessary.

#KT-2637 in progress
This commit is contained in:
Evgeny Gerashchenko
2013-07-19 18:03:07 +04:00
parent 2cfe11cb6b
commit 5aa4672d32
18 changed files with 217 additions and 1 deletions
@@ -112,6 +112,14 @@ public class JetPsiFactory {
return semicolon;
}
//the pair contains the first and the last elements of a range
@NotNull
public static Pair<PsiElement, PsiElement> createWhitespaceAndArrow(Project project) {
JetFunctionType functionType = (JetFunctionType) createType(project, "() -> Int").getTypeElement();
assert functionType != null;
return Pair.create(functionType.findElementAt(2), functionType.findElementAt(3));
}
public static PsiElement createWhiteSpace(Project project) {
return createWhiteSpace(project, " ");
}
@@ -49,6 +49,10 @@ public class ErrorUtils {
if (containsErrorType(function.getReturnType())) {
return true;
}
ReceiverParameterDescriptor receiverParameter = function.getReceiverParameter();
if (receiverParameter != null && containsErrorType(receiverParameter.getType())) {
return true;
}
for (ValueParameterDescriptor parameter : function.getValueParameters()) {
if (containsErrorType(parameter.getType())) {
return true;
@@ -12,13 +12,16 @@ 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.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
@@ -27,7 +30,10 @@ 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.SimpleFunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.diagnostics.AbstractDiagnosticFactory;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
@@ -35,6 +41,7 @@ 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.ErrorUtils;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.plugin.JetLanguage;
@@ -99,6 +106,7 @@ public class KotlinInlineLocalHandler extends InlineActionHandler {
}
final String typeArgumentsForCall = getTypeArgumentsStringForCall(initializer);
final String parametersForFunctionLiteral = getParametersForFunctionLiteral(initializer);
PsiReference[] referencesArray = references.toArray(references.toArray(new PsiReference[references.size()]));
@@ -150,12 +158,110 @@ public class KotlinInlineLocalHandler extends InlineActionHandler {
if (typeArgumentsForCall != null) {
addTypeArguments(typeArgumentsForCall, inlinedExpressions);
}
if (parametersForFunctionLiteral != null) {
addFunctionLiteralParameterTypes(parametersForFunctionLiteral, inlinedExpressions);
}
}
});
}
}, RefactoringBundle.message("inline.command", name), null);
}
@Nullable
private static String getParametersForFunctionLiteral(JetExpression initializer) {
JetFunctionLiteralExpression functionLiteralExpression = getFunctionLiteralExpression(initializer);
if (functionLiteralExpression == null) {
return null;
}
ResolveSession resolveSession = AnalyzerFacadeWithCache.getLazyResolveSession((JetFile) initializer.getContainingFile());
BindingContext context = ResolveSessionUtils.resolveToExpression(resolveSession, initializer);
SimpleFunctionDescriptor fun = context.get(BindingContext.FUNCTION, functionLiteralExpression.getFunctionLiteral());
if (fun == null || ErrorUtils.containsErrorType(fun)) {
return null;
}
return StringUtil.join(fun.getValueParameters(), new Function<ValueParameterDescriptor, String>() {
@Override
public String fun(ValueParameterDescriptor descriptor) {
return descriptor.getName() + ": " + DescriptorRenderer.TEXT.renderType(descriptor.getType());
}
}, ", ");
}
@Nullable
private static JetFunctionLiteralExpression getFunctionLiteralExpression(@NotNull JetExpression expression) {
if (expression instanceof JetParenthesizedExpression) {
JetExpression inner = ((JetParenthesizedExpression) expression).getExpression();
return inner == null ? null : getFunctionLiteralExpression(inner);
}
if (expression instanceof JetFunctionLiteralExpression) {
return (JetFunctionLiteralExpression) expression;
}
return null;
}
private static void addFunctionLiteralParameterTypes(@NotNull String parameters, @NotNull List<JetExpression> inlinedExpressions) {
JetFile containingFile = (JetFile) inlinedExpressions.get(0).getContainingFile();
List<JetFunctionLiteralExpression> functionsToAddParameters = Lists.newArrayList();
ResolveSession resolveSession = AnalyzerFacadeWithCache.getLazyResolveSession(containingFile);
for (JetExpression inlinedExpression : inlinedExpressions) {
JetFunctionLiteralExpression functionLiteralExpression = getFunctionLiteralExpression(inlinedExpression);
assert functionLiteralExpression != null : "can't find function literal expression for " + inlinedExpression.getText();
if (needToAddParameterTypes(functionLiteralExpression, resolveSession)) {
functionsToAddParameters.add(functionLiteralExpression);
}
}
for (JetFunctionLiteralExpression functionLiteralExpression : functionsToAddParameters) {
JetFunctionLiteral functionLiteral = functionLiteralExpression.getFunctionLiteral();
JetParameterList currentParameterList = functionLiteral.getValueParameterList();
JetParameterList newParameterList = JetPsiFactory.createParameterList(containingFile.getProject(), "(" + parameters + ")");
if (currentParameterList != null) {
currentParameterList.replace(newParameterList);
}
else {
PsiElement openBraceElement = functionLiteral.getOpenBraceNode().getPsi();
PsiElement nextSibling = openBraceElement.getNextSibling();
PsiElement whitespaceToAdd = nextSibling instanceof PsiWhiteSpace && nextSibling.getText().contains("\n")
? nextSibling.copy() : null;
Pair<PsiElement, PsiElement> whitespaceAndArrow = JetPsiFactory.createWhitespaceAndArrow(containingFile.getProject());
functionLiteral.addRangeAfter(whitespaceAndArrow.first, whitespaceAndArrow.second, openBraceElement);
functionLiteral.addAfter(newParameterList, openBraceElement);
if (whitespaceToAdd != null) {
functionLiteral.addAfter(whitespaceToAdd, openBraceElement);
}
}
ReferenceToClassesShortening.compactReferenceToClasses(functionLiteralExpression.getValueParameters());
}
}
private static boolean needToAddParameterTypes(
@NotNull JetFunctionLiteralExpression functionLiteralExpression,
@NotNull ResolveSession resolveSession
) {
JetFunctionLiteral functionLiteral = functionLiteralExpression.getFunctionLiteral();
BindingContext context = ResolveSessionUtils.resolveToExpression(resolveSession, functionLiteralExpression);
for (Diagnostic diagnostic : context.getDiagnostics()) {
AbstractDiagnosticFactory factory = diagnostic.getFactory();
PsiElement element = diagnostic.getPsiElement();
boolean hasCantInferParameter = factory == Errors.CANNOT_INFER_PARAMETER_TYPE && element.getParent().getParent() == functionLiteral;
boolean hasUnresolvedItOrThis = factory == Errors.UNRESOLVED_REFERENCE && element.getText().equals("it") &&
PsiTreeUtil.getParentOfType(element, JetFunctionLiteral.class) == functionLiteral;
if (hasCantInferParameter || hasUnresolvedItOrThis) {
return true;
}
}
return false;
}
private static void addTypeArguments(@NotNull String typeArguments, @NotNull List<JetExpression> inlinedExpressions) {
JetFile containingFile = (JetFile) inlinedExpressions.get(0).getContainingFile();
List<JetCallExpression> callsToAddArguments = Lists.newArrayList();
@@ -0,0 +1,4 @@
fun foo() {
val f: (Int) -> Int = { x -> x }
val ff: (Int) -> Int = <caret>f
}
@@ -0,0 +1,3 @@
fun foo() {
val ff: (Int) -> Int = { x -> x }
}
@@ -0,0 +1,4 @@
fun foo() {
val f = { x -> x }
val ff = <caret>f
}
@@ -0,0 +1,3 @@
fun foo() {
val ff = { x -> x }
}
@@ -0,0 +1,4 @@
fun foo() {
val f: (Int) -> Int = { it }
val ff = <caret>f
}
@@ -0,0 +1,3 @@
fun foo() {
val ff = {(it: Int) -> it }
}
@@ -0,0 +1,6 @@
fun foo() {
val f: (Int) -> Int = {
it
}
val ff = <caret>f
}
@@ -0,0 +1,6 @@
fun foo() {
val ff = {
(it: Int) ->
it
}
}
@@ -0,0 +1,4 @@
fun foo() {
val f: (Int) -> Int = (({ x -> x }))
val ff = <caret>f
}
@@ -0,0 +1,3 @@
fun foo() {
val ff = (({(x: Int) -> x }))
}
@@ -0,0 +1,4 @@
fun foo() {
val f: (Int) -> Int = { x -> x }
val ff = <caret>f
}
@@ -0,0 +1,3 @@
fun foo() {
val ff = {(x: Int) -> x }
}
@@ -0,0 +1,4 @@
fun foo() {
val f = { 1 }
val ff = <caret>f
}
@@ -0,0 +1,3 @@
fun foo() {
val ff = { 1 }
}
@@ -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, InlineTestGenerated.ExplicateTypeArgument.class})
@InnerTestClasses({InlineTestGenerated.AddParenthesis.class, InlineTestGenerated.ExplicateParameterTypes.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,6 +255,49 @@ public class InlineTestGenerated extends AbstractInlineTest {
}
@TestMetadata("idea/testData/refactoring/inline/explicateParameterTypes")
public static class ExplicateParameterTypes extends AbstractInlineTest {
public void testAllFilesPresentInExplicateParameterTypes() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/refactoring/inline/explicateParameterTypes"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("EnoughDontExplicate.kt")
public void testEnoughDontExplicate() throws Exception {
doTest("idea/testData/refactoring/inline/explicateParameterTypes/EnoughDontExplicate.kt");
}
@TestMetadata("ErrorTypes.kt")
public void testErrorTypes() throws Exception {
doTest("idea/testData/refactoring/inline/explicateParameterTypes/ErrorTypes.kt");
}
@TestMetadata("It.kt")
public void testIt() throws Exception {
doTest("idea/testData/refactoring/inline/explicateParameterTypes/It.kt");
}
@TestMetadata("ItMultiLine.kt")
public void testItMultiLine() throws Exception {
doTest("idea/testData/refactoring/inline/explicateParameterTypes/ItMultiLine.kt");
}
@TestMetadata("Parenthesized.kt")
public void testParenthesized() throws Exception {
doTest("idea/testData/refactoring/inline/explicateParameterTypes/Parenthesized.kt");
}
@TestMetadata("Simplest.kt")
public void testSimplest() throws Exception {
doTest("idea/testData/refactoring/inline/explicateParameterTypes/Simplest.kt");
}
@TestMetadata("TrivialDontExplicate.kt")
public void testTrivialDontExplicate() throws Exception {
doTest("idea/testData/refactoring/inline/explicateParameterTypes/TrivialDontExplicate.kt");
}
}
@TestMetadata("idea/testData/refactoring/inline/explicateTypeArgument")
public static class ExplicateTypeArgument extends AbstractInlineTest {
public void testAllFilesPresentInExplicateTypeArgument() throws Exception {
@@ -312,6 +355,7 @@ public class InlineTestGenerated extends AbstractInlineTest {
TestSuite suite = new TestSuite("InlineTestGenerated");
suite.addTestSuite(InlineTestGenerated.class);
suite.addTestSuite(AddParenthesis.class);
suite.addTestSuite(ExplicateParameterTypes.class);
suite.addTestSuite(ExplicateTypeArgument.class);
return suite;
}