diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java index 05c5e2abb76..177dfe44f7d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java @@ -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 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, " "); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java index 5a3d535a954..e5d069264d7 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java @@ -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; diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java b/idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java index fe2d845d336..99a445d356f 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java @@ -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() { + @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 inlinedExpressions) { + JetFile containingFile = (JetFile) inlinedExpressions.get(0).getContainingFile(); + List 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 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 inlinedExpressions) { JetFile containingFile = (JetFile) inlinedExpressions.get(0).getContainingFile(); List callsToAddArguments = Lists.newArrayList(); diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/EnoughDontExplicate.kt b/idea/testData/refactoring/inline/explicateParameterTypes/EnoughDontExplicate.kt new file mode 100644 index 00000000000..b73042844c8 --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/EnoughDontExplicate.kt @@ -0,0 +1,4 @@ +fun foo() { + val f: (Int) -> Int = { x -> x } + val ff: (Int) -> Int = f +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/EnoughDontExplicate.kt.after b/idea/testData/refactoring/inline/explicateParameterTypes/EnoughDontExplicate.kt.after new file mode 100644 index 00000000000..566d7fcbb96 --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/EnoughDontExplicate.kt.after @@ -0,0 +1,3 @@ +fun foo() { + val ff: (Int) -> Int = { x -> x } +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/ErrorTypes.kt b/idea/testData/refactoring/inline/explicateParameterTypes/ErrorTypes.kt new file mode 100644 index 00000000000..0e3abab9827 --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/ErrorTypes.kt @@ -0,0 +1,4 @@ +fun foo() { + val f = { x -> x } + val ff = f +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/ErrorTypes.kt.after b/idea/testData/refactoring/inline/explicateParameterTypes/ErrorTypes.kt.after new file mode 100644 index 00000000000..9d0e4257c65 --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/ErrorTypes.kt.after @@ -0,0 +1,3 @@ +fun foo() { + val ff = { x -> x } +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/It.kt b/idea/testData/refactoring/inline/explicateParameterTypes/It.kt new file mode 100644 index 00000000000..d7369401d35 --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/It.kt @@ -0,0 +1,4 @@ +fun foo() { + val f: (Int) -> Int = { it } + val ff = f +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/It.kt.after b/idea/testData/refactoring/inline/explicateParameterTypes/It.kt.after new file mode 100644 index 00000000000..12320844a7a --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/It.kt.after @@ -0,0 +1,3 @@ +fun foo() { + val ff = {(it: Int) -> it } +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/ItMultiLine.kt b/idea/testData/refactoring/inline/explicateParameterTypes/ItMultiLine.kt new file mode 100644 index 00000000000..7c21a5ec73f --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/ItMultiLine.kt @@ -0,0 +1,6 @@ +fun foo() { + val f: (Int) -> Int = { + it + } + val ff = f +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/ItMultiLine.kt.after b/idea/testData/refactoring/inline/explicateParameterTypes/ItMultiLine.kt.after new file mode 100644 index 00000000000..9789c6abf2c --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/ItMultiLine.kt.after @@ -0,0 +1,6 @@ +fun foo() { + val ff = { + (it: Int) -> + it + } +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/Parenthesized.kt b/idea/testData/refactoring/inline/explicateParameterTypes/Parenthesized.kt new file mode 100644 index 00000000000..14d1545a305 --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/Parenthesized.kt @@ -0,0 +1,4 @@ +fun foo() { + val f: (Int) -> Int = (({ x -> x })) + val ff = f +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/Parenthesized.kt.after b/idea/testData/refactoring/inline/explicateParameterTypes/Parenthesized.kt.after new file mode 100644 index 00000000000..da62790d86b --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/Parenthesized.kt.after @@ -0,0 +1,3 @@ +fun foo() { + val ff = (({(x: Int) -> x })) +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/Simplest.kt b/idea/testData/refactoring/inline/explicateParameterTypes/Simplest.kt new file mode 100644 index 00000000000..e86b00614c0 --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/Simplest.kt @@ -0,0 +1,4 @@ +fun foo() { + val f: (Int) -> Int = { x -> x } + val ff = f +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/Simplest.kt.after b/idea/testData/refactoring/inline/explicateParameterTypes/Simplest.kt.after new file mode 100644 index 00000000000..1f2ba71b0ad --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/Simplest.kt.after @@ -0,0 +1,3 @@ +fun foo() { + val ff = {(x: Int) -> x } +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/TrivialDontExplicate.kt b/idea/testData/refactoring/inline/explicateParameterTypes/TrivialDontExplicate.kt new file mode 100644 index 00000000000..f7f2ef84da7 --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/TrivialDontExplicate.kt @@ -0,0 +1,4 @@ +fun foo() { + val f = { 1 } + val ff = f +} diff --git a/idea/testData/refactoring/inline/explicateParameterTypes/TrivialDontExplicate.kt.after b/idea/testData/refactoring/inline/explicateParameterTypes/TrivialDontExplicate.kt.after new file mode 100644 index 00000000000..8601d37d7d8 --- /dev/null +++ b/idea/testData/refactoring/inline/explicateParameterTypes/TrivialDontExplicate.kt.after @@ -0,0 +1,3 @@ +fun foo() { + val ff = { 1 } +} diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java index 6d3b3b96159..f11a2c1e592 100644 --- a/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java @@ -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; }