diff --git a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt index 071f9f7e59d..a828bbb6b2a 100644 --- a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt @@ -413,6 +413,7 @@ fun main(args: Array) { model("intentions/makeTypeExplicitInLambda", testMethod = "doTestMakeTypeExplicitInLambda") model("intentions/makeTypeImplicitInLambda", testMethod = "doTestMakeTypeImplicitInLambda") model("intentions/invertIfCondition", testMethod = "doTestInvertIfCondition") + model("intentions/operatorToFunction", testMethod = "doTestOperatorToFunction") model("intentions/convertToForEachLoop", testMethod = "doTestConvertToForEachLoop") model("intentions/convertToForEachFunctionCall", testMethod = "doTestConvertToForEachFunctionCall") } diff --git a/idea/resources/intentionDescriptions/OperatorToFunctionIntention/after.kt.template b/idea/resources/intentionDescriptions/OperatorToFunctionIntention/after.kt.template new file mode 100644 index 00000000000..cb267b1d4c1 --- /dev/null +++ b/idea/resources/intentionDescriptions/OperatorToFunctionIntention/after.kt.template @@ -0,0 +1,11 @@ +a.not() + +b.dec() + +a.times(b) + +a.get(i_1, ..., i_n) + +a.set(i_1, ..., i_n, b) + +a.invoke(i_1, ..., i_n) diff --git a/idea/resources/intentionDescriptions/OperatorToFunctionIntention/before.kt.template b/idea/resources/intentionDescriptions/OperatorToFunctionIntention/before.kt.template new file mode 100644 index 00000000000..1348b690a7b --- /dev/null +++ b/idea/resources/intentionDescriptions/OperatorToFunctionIntention/before.kt.template @@ -0,0 +1,11 @@ +!a + +--b + +a * b + +a[i_1, ..., i_n] + +a[i_1, ..., i_n] = b + +a(i_1, ..., i_n) diff --git a/idea/resources/intentionDescriptions/OperatorToFunctionIntention/description.html b/idea/resources/intentionDescriptions/OperatorToFunctionIntention/description.html new file mode 100644 index 00000000000..5520f90ab0d --- /dev/null +++ b/idea/resources/intentionDescriptions/OperatorToFunctionIntention/description.html @@ -0,0 +1,5 @@ + + +Replace overloaded operator with function call + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 23549465e2b..cc9d29f9d27 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -706,7 +706,12 @@ Kotlin - + org.jetbrains.jet.plugin.intentions.OperatorToFunctionIntention + Kotlin + + + + diff --git a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties index c9161beb75a..61074b9d57c 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties +++ b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties @@ -314,6 +314,8 @@ make.type.implicit.in.lambda=Make types implicit in lambda (may break code) make.type.implicit.in.lambda.family=Make Types Implicit In Lambda (May Break Code) invert.if.condition=Invert If Condition invert.if.condition.family=Invert If Condition +operator.to.function=Replace overloaded operator with function call +operator.to.function.family=Replace Overloaded Operator With Function Call convert.to.for.each.loop.intention=Replace with a for each loop convert.to.for.each.loop.intention.family=Replace with a For Each Loop convert.to.for.each.function.call.intention=Replace with a forEach function call diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/OperatorToFunctionIntention.kt b/idea/src/org/jetbrains/jet/plugin/intentions/OperatorToFunctionIntention.kt new file mode 100644 index 00000000000..f7370581269 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/intentions/OperatorToFunctionIntention.kt @@ -0,0 +1,208 @@ +/* +* 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.intentions + +import org.jetbrains.jet.lang.psi.JetExpression +import com.intellij.openapi.editor.Editor +import org.jetbrains.jet.lang.psi.JetPrefixExpression +import org.jetbrains.jet.lang.psi.JetPostfixExpression +import org.jetbrains.jet.lang.psi.JetBinaryExpression +import org.jetbrains.jet.lang.psi.JetArrayAccessExpression +import org.jetbrains.jet.lang.psi.JetCallExpression +import org.jetbrains.jet.lexer.JetTokens +import org.jetbrains.jet.lang.psi.JetPsiFactory +import org.jetbrains.jet.lang.psi.JetElement +import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression +import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache +import org.jetbrains.jet.lang.resolve.BindingContext +import org.jetbrains.jet.lang.psi.psiUtil.getQualifiedElementSelector +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns +import org.jetbrains.jet.lang.psi.JetFile +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor +import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression + +public class OperatorToFunctionIntention : JetSelfTargetingIntention("operator.to.function", javaClass()) { + fun isApplicablePrefix(element: JetPrefixExpression): Boolean { + return when (element.getOperationReference().getReferencedNameElementType()) { + JetTokens.PLUS, JetTokens.MINUS, JetTokens.PLUSPLUS, JetTokens.MINUSMINUS, JetTokens.EXCL -> true + else -> false + } + } + + fun isApplicablePostfix(element: JetPostfixExpression): Boolean { + return when (element.getOperationReference().getReferencedNameElementType()) { + JetTokens.PLUSPLUS, JetTokens.MINUSMINUS -> true + else -> false + } + } + + fun isApplicableBinary(element: JetBinaryExpression): Boolean { + return when (element.getOperationReference().getReferencedNameElementType()) { + JetTokens.PLUS, JetTokens.MINUS, JetTokens.MUL, JetTokens.DIV, JetTokens.PERC, JetTokens.RANGE, JetTokens.IN_KEYWORD, JetTokens.NOT_IN, JetTokens.PLUSEQ, JetTokens.MINUSEQ, JetTokens.MULTEQ, JetTokens.DIVEQ, JetTokens.PERCEQ, JetTokens.EQEQ, JetTokens.EXCLEQ, JetTokens.GT, JetTokens.LT, JetTokens.GTEQ, JetTokens.LTEQ -> true + JetTokens.EQ -> element.getLeft() is JetArrayAccessExpression + else -> false + } + } + + fun isApplicableCall(element: JetCallExpression): Boolean { + val resolvedCall = AnalyzerFacadeWithCache.getContextForElement(element)[BindingContext.RESOLVED_CALL, element.getCalleeExpression()] + val descriptor = resolvedCall?.getResultingDescriptor() + if (descriptor is FunctionDescriptor && descriptor.getName().asString() == "invoke") { + val parent = element.getParent() + if (parent is JetDotQualifiedExpression && element.getCalleeExpression()?.getText() == "invoke") return false + return !(element.getValueArgumentList() == null && element.getFunctionLiteralArguments().isEmpty()) + } + return false + } + + override fun isApplicableTo(element: JetExpression): Boolean { + return when (element) { + is JetPrefixExpression -> isApplicablePrefix(element) + is JetPostfixExpression -> isApplicablePostfix(element) + is JetBinaryExpression -> isApplicableBinary(element) + is JetArrayAccessExpression -> true + is JetCallExpression -> isApplicableCall(element) + else -> false + } + } + + fun convertPrefix(element: JetPrefixExpression) { + val op = element.getOperationReference().getReferencedNameElementType() + val base = element.getBaseExpression()!!.getText() + + val call = when (op) { + JetTokens.PLUS -> "plus()" + JetTokens.MINUS -> "minus()" + JetTokens.PLUSPLUS -> "inc()" + JetTokens.MINUSMINUS -> "dec()" + JetTokens.EXCL -> "not()" + else -> return + } + + val transformation = "$base.$call" + val transformed = JetPsiFactory.createExpression(element.getProject(), transformation) + element.replace(transformed) + } + + fun convertPostFix(element: JetPostfixExpression) { + val op = element.getOperationReference().getReferencedNameElementType() + val base = element.getBaseExpression().getText() + + val call = when (op) { + JetTokens.PLUSPLUS -> "inc()" + JetTokens.MINUSMINUS -> "dec()" + else -> return + } + + val transformation = "$base.$call" + val transformed = JetPsiFactory.createExpression(element.getProject(), transformation) + element.replace(transformed) + } + + fun convertBinary(element: JetBinaryExpression) { + val op = element.getOperationReference().getReferencedNameElementType() + val left = element.getLeft()!! + val right = element.getRight()!! + val leftText = left.getText() + val rightText = right.getText() + + if (op == JetTokens.EQ) { + if (left is JetArrayAccessExpression) { + convertArrayAccess(left as JetArrayAccessExpression) + } + return + } + + val context = AnalyzerFacadeWithCache.getContextForElement(element) + val functionCandidate = context[BindingContext.RESOLVED_CALL, element.getOperationReference()] + val functionName = functionCandidate?.getCandidateDescriptor()?.getName().toString() + val elemType = context[BindingContext.EXPRESSION_TYPE, left] + + val transformation = when (op) { + JetTokens.PLUS -> "$leftText.plus($rightText)" + JetTokens.MINUS -> "$leftText.minus($rightText)" + JetTokens.MUL -> "$leftText.times($rightText)" + JetTokens.DIV -> "$leftText.div($rightText)" + JetTokens.PERC -> "$leftText.mod($rightText)" + JetTokens.RANGE -> "$leftText.rangeTo($rightText)" + JetTokens.IN_KEYWORD -> "$rightText.contains($leftText)" + JetTokens.NOT_IN -> "!$rightText.contains($leftText)" + JetTokens.PLUSEQ -> if (functionName == "plusAssign") "$leftText.plusAssign($rightText)" else "$leftText = $leftText.plus($rightText)" + JetTokens.MINUSEQ -> if (functionName == "minusAssign") "$leftText.minusAssign($rightText)" else "$leftText = $leftText.minus($rightText)" + JetTokens.MULTEQ -> if (functionName == "multAssign") "$leftText.multAssign($rightText)" else "$leftText = $leftText.mult($rightText)" + JetTokens.DIVEQ -> if (functionName == "divAssign") "$leftText.divAssign($rightText)" else "$leftText = $leftText.div($rightText)" + JetTokens.PERCEQ -> if (functionName == "modAssign") "$leftText.modAssign($rightText)" else "$leftText = $leftText.mod($rightText)" + JetTokens.EQEQ -> if (elemType?.isNullable() ?: true) "$leftText?.equals($rightText) ?: $rightText.identityEquals(null)" else "$leftText.equals($rightText)" + JetTokens.EXCLEQ -> if (elemType?.isNullable() ?: true) "!($leftText?.equals($rightText) ?: $rightText.identityEquals(null))" else "!$leftText.equals($rightText)" + JetTokens.GT -> "$leftText.compareTo($rightText) > 0" + JetTokens.LT -> "$leftText.compareTo($rightText) < 0" + JetTokens.GTEQ -> "$leftText.compareTo($rightText) >= 0" + JetTokens.LTEQ -> "$leftText.compareTo($rightText) <= 0" + else -> return + } + + val transformed = JetPsiFactory.createExpression(element.getProject(), transformation) + + element.replace(transformed) + } + + fun convertArrayAccess(element: JetArrayAccessExpression) { + val parent = element.getParent() + val array = element.getArrayExpression()!!.getText() + val indices = element.getIndicesNode() + val indicesText = indices.getText().trim("[","]") + + val transformation : String + val replaced : JetElement + if (parent is JetBinaryExpression && parent.getOperationReference().getReferencedNameElementType() == JetTokens.EQ) { + // part of an assignment + val right = parent.getRight()!!.getText() + transformation = "$array.set($indicesText, $right)" + replaced = parent + } + else { + transformation = "$array.get($indicesText)" + replaced = element + } + + val transformed = JetPsiFactory.createExpression(element.getProject(), transformation) + replaced.replace(transformed) + } + + fun convertCall(element: JetCallExpression) { + val callee = element.getCalleeExpression()!! + val arguments = element.getValueArgumentList() + val argumentString = arguments?.getText()?.trim("(", ")") + val funcLitArgs = element.getFunctionLiteralArguments() + val calleeText = callee.getText() + val transformation = if (argumentString == null) "$calleeText.invoke" else "$calleeText.invoke($argumentString)" + val transformed = JetPsiFactory.createExpression(element.getProject(), transformation) + funcLitArgs.forEach { transformed.add(it) } + callee.getParent()!!.replace(transformed) + } + + override fun applyTo(element: JetExpression, editor: Editor) { + return when (element) { + is JetPrefixExpression -> convertPrefix(element) + is JetPostfixExpression -> convertPostFix(element) + is JetBinaryExpression -> convertBinary(element) + is JetArrayAccessExpression -> convertArrayAccess(element) + is JetCallExpression -> convertCall(element) + } + } +} diff --git a/idea/testData/intentions/operatorToFunction/arrayAccessMultipleIndex.kt b/idea/testData/intentions/operatorToFunction/arrayAccessMultipleIndex.kt new file mode 100644 index 00000000000..88a8ec79617 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/arrayAccessMultipleIndex.kt @@ -0,0 +1,7 @@ +class Bar { + fun get(vararg args: Int) {} +} + +fun foo(a: Bar, i: Int) { + a[i, 1] +} diff --git a/idea/testData/intentions/operatorToFunction/arrayAccessMultipleIndex.kt.after b/idea/testData/intentions/operatorToFunction/arrayAccessMultipleIndex.kt.after new file mode 100644 index 00000000000..66376895418 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/arrayAccessMultipleIndex.kt.after @@ -0,0 +1,7 @@ +class Bar { + fun get(vararg args: Int) {} +} + +fun foo(a: Bar, i: Int) { + a.get(i, 1) +} diff --git a/idea/testData/intentions/operatorToFunction/arrayAccessOneIndex.kt b/idea/testData/intentions/operatorToFunction/arrayAccessOneIndex.kt new file mode 100644 index 00000000000..d6419b830d9 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/arrayAccessOneIndex.kt @@ -0,0 +1,3 @@ +fun foo(a: Array, i: Int) { + a[i] +} diff --git a/idea/testData/intentions/operatorToFunction/arrayAccessOneIndex.kt.after b/idea/testData/intentions/operatorToFunction/arrayAccessOneIndex.kt.after new file mode 100644 index 00000000000..c661a9d0a4f --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/arrayAccessOneIndex.kt.after @@ -0,0 +1,3 @@ +fun foo(a: Array, i: Int) { + a.get(i) +} diff --git a/idea/testData/intentions/operatorToFunction/arrayAssignmentMultipleIndex.kt b/idea/testData/intentions/operatorToFunction/arrayAssignmentMultipleIndex.kt new file mode 100644 index 00000000000..7ccac0aad78 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/arrayAssignmentMultipleIndex.kt @@ -0,0 +1,5 @@ +// ERROR: Unresolved reference: array +fun foo(b: Int) { + var a = array(1, 2, 3, 4, 5) + a[2, 3] = b +} diff --git a/idea/testData/intentions/operatorToFunction/arrayAssignmentMultipleIndex.kt.after b/idea/testData/intentions/operatorToFunction/arrayAssignmentMultipleIndex.kt.after new file mode 100644 index 00000000000..6a25cff5a42 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/arrayAssignmentMultipleIndex.kt.after @@ -0,0 +1,5 @@ +// ERROR: Unresolved reference: array +fun foo(b: Int) { + var a = array(1, 2, 3, 4, 5) + a.set(2, 3, b) +} diff --git a/idea/testData/intentions/operatorToFunction/binaryEqualsEqualsNullableOperands.kt b/idea/testData/intentions/operatorToFunction/binaryEqualsEqualsNullableOperands.kt new file mode 100644 index 00000000000..91285e78490 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryEqualsEqualsNullableOperands.kt @@ -0,0 +1,3 @@ +fun foo(a: String?, b: String?) { + a == b +} diff --git a/idea/testData/intentions/operatorToFunction/binaryEqualsEqualsNullableOperands.kt.after b/idea/testData/intentions/operatorToFunction/binaryEqualsEqualsNullableOperands.kt.after new file mode 100644 index 00000000000..a53857d785c --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryEqualsEqualsNullableOperands.kt.after @@ -0,0 +1,3 @@ +fun foo(a: String?, b: String?) { + a?.equals(b) ?: b.identityEquals(null) +} diff --git a/idea/testData/intentions/operatorToFunction/binaryNotEqualsNonNullableOperands.kt b/idea/testData/intentions/operatorToFunction/binaryNotEqualsNonNullableOperands.kt new file mode 100644 index 00000000000..11e53ee439f --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryNotEqualsNonNullableOperands.kt @@ -0,0 +1,3 @@ +fun foo(a: Int, b: Int) { + a != b +} diff --git a/idea/testData/intentions/operatorToFunction/binaryNotEqualsNonNullableOperands.kt.after b/idea/testData/intentions/operatorToFunction/binaryNotEqualsNonNullableOperands.kt.after new file mode 100644 index 00000000000..ae1993b145c --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryNotEqualsNonNullableOperands.kt.after @@ -0,0 +1,3 @@ +fun foo(a: Int, b: Int) { + !a.equals(b) +} diff --git a/idea/testData/intentions/operatorToFunction/binaryNotIn.kt b/idea/testData/intentions/operatorToFunction/binaryNotIn.kt new file mode 100644 index 00000000000..060c1db8eb3 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryNotIn.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun foo(a: Int, b: Array) { + a !in b +} diff --git a/idea/testData/intentions/operatorToFunction/binaryNotIn.kt.after b/idea/testData/intentions/operatorToFunction/binaryNotIn.kt.after new file mode 100644 index 00000000000..d417392efff --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryNotIn.kt.after @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun foo(a: Int, b: Array) { + !b.contains(a) +} diff --git a/idea/testData/intentions/operatorToFunction/binaryPlus.kt b/idea/testData/intentions/operatorToFunction/binaryPlus.kt new file mode 100644 index 00000000000..f9d2bcfd3f1 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryPlus.kt @@ -0,0 +1,3 @@ +fun foo(a: Int, b: Int) { + a + b +} diff --git a/idea/testData/intentions/operatorToFunction/binaryPlus.kt.after b/idea/testData/intentions/operatorToFunction/binaryPlus.kt.after new file mode 100644 index 00000000000..8f2467e0410 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryPlus.kt.after @@ -0,0 +1,3 @@ +fun foo(a: Int, b: Int) { + a.plus(b) +} diff --git a/idea/testData/intentions/operatorToFunction/binaryPlusEquals.kt b/idea/testData/intentions/operatorToFunction/binaryPlusEquals.kt new file mode 100644 index 00000000000..6b4fd1a30e9 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryPlusEquals.kt @@ -0,0 +1,8 @@ +class Bar { + fun plusAssign(arg: Bar) {} +} + +fun foo(b: Bar) { + var a = Bar() + a += b +} diff --git a/idea/testData/intentions/operatorToFunction/binaryPlusEquals.kt.after b/idea/testData/intentions/operatorToFunction/binaryPlusEquals.kt.after new file mode 100644 index 00000000000..e20ec04548f --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryPlusEquals.kt.after @@ -0,0 +1,8 @@ +class Bar { + fun plusAssign(arg: Bar) {} +} + +fun foo(b: Bar) { + var a = Bar() + a.plusAssign(b) +} diff --git a/idea/testData/intentions/operatorToFunction/binaryPlusEqualsNotResolved.kt b/idea/testData/intentions/operatorToFunction/binaryPlusEqualsNotResolved.kt new file mode 100644 index 00000000000..9dbf831a73f --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryPlusEqualsNotResolved.kt @@ -0,0 +1,4 @@ +fun foo(b: Int) { + var a = 0 + a += b +} diff --git a/idea/testData/intentions/operatorToFunction/binaryPlusEqualsNotResolved.kt.after b/idea/testData/intentions/operatorToFunction/binaryPlusEqualsNotResolved.kt.after new file mode 100644 index 00000000000..60d9559b06f --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryPlusEqualsNotResolved.kt.after @@ -0,0 +1,4 @@ +fun foo(b: Int) { + var a = 0 + a = a.plus(b) +} diff --git a/idea/testData/intentions/operatorToFunction/binaryRangeTo.kt b/idea/testData/intentions/operatorToFunction/binaryRangeTo.kt new file mode 100644 index 00000000000..98a96cd3a41 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryRangeTo.kt @@ -0,0 +1,3 @@ +fun foo(a: Int, b: Int) { + a..b +} diff --git a/idea/testData/intentions/operatorToFunction/binaryRangeTo.kt.after b/idea/testData/intentions/operatorToFunction/binaryRangeTo.kt.after new file mode 100644 index 00000000000..a4417309e15 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/binaryRangeTo.kt.after @@ -0,0 +1,3 @@ +fun foo(a: Int, b: Int) { + a.rangeTo(b) +} diff --git a/idea/testData/intentions/operatorToFunction/functionCallMultipleArgument.kt b/idea/testData/intentions/operatorToFunction/functionCallMultipleArgument.kt new file mode 100644 index 00000000000..92710592dde --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/functionCallMultipleArgument.kt @@ -0,0 +1,9 @@ +class bar() { + fun invoke(i: Any?, j: Any?) : Boolean { + return true + } +} + +fun foo(i: Any?, j: Any?) { + val test = bar()(i, j) +} \ No newline at end of file diff --git a/idea/testData/intentions/operatorToFunction/functionCallMultipleArgument.kt.after b/idea/testData/intentions/operatorToFunction/functionCallMultipleArgument.kt.after new file mode 100644 index 00000000000..2b8528e8fd4 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/functionCallMultipleArgument.kt.after @@ -0,0 +1,9 @@ +class bar() { + fun invoke(i: Any?, j: Any?) : Boolean { + return true + } +} + +fun foo(i: Any?, j: Any?) { + val test = bar().invoke(i, j) +} \ No newline at end of file diff --git a/idea/testData/intentions/operatorToFunction/functionCallWithMultipleArgumentsAndSingleLambda.kt b/idea/testData/intentions/operatorToFunction/functionCallWithMultipleArgumentsAndSingleLambda.kt new file mode 100644 index 00000000000..69d990f549a --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/functionCallWithMultipleArgumentsAndSingleLambda.kt @@ -0,0 +1,8 @@ +class Mocha() { + fun invoke(x: Int, y: String, f: (Int) -> String) { + } +} +fun main() { + val mocha = Mocha() + val testing = mocha(1, "fire"){ (x: Int) -> "hello world" } +} \ No newline at end of file diff --git a/idea/testData/intentions/operatorToFunction/functionCallWithMultipleArgumentsAndSingleLambda.kt.after b/idea/testData/intentions/operatorToFunction/functionCallWithMultipleArgumentsAndSingleLambda.kt.after new file mode 100644 index 00000000000..d3d9b88baf5 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/functionCallWithMultipleArgumentsAndSingleLambda.kt.after @@ -0,0 +1,8 @@ +class Mocha() { + fun invoke(x: Int, y: String, f: (Int) -> String) { + } +} +fun main() { + val mocha = Mocha() + val testing = mocha.invoke(1, "fire"){ (x: Int) -> "hello world" } +} \ No newline at end of file diff --git a/idea/testData/intentions/operatorToFunction/functionCallWithSingleLambda.kt b/idea/testData/intentions/operatorToFunction/functionCallWithSingleLambda.kt new file mode 100644 index 00000000000..8fdf204f8e1 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/functionCallWithSingleLambda.kt @@ -0,0 +1,7 @@ +class Mocha() { + fun invoke(f: (Int) -> String) {} +} +fun main() { + val mocha = Mocha() + val testing = mocha{ (x: Int) -> "hello world" } +} \ No newline at end of file diff --git a/idea/testData/intentions/operatorToFunction/functionCallWithSingleLambda.kt.after b/idea/testData/intentions/operatorToFunction/functionCallWithSingleLambda.kt.after new file mode 100644 index 00000000000..54273f150b9 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/functionCallWithSingleLambda.kt.after @@ -0,0 +1,7 @@ +class Mocha() { + fun invoke(f: (Int) -> String) {} +} +fun main() { + val mocha = Mocha() + val testing = mocha.invoke{ (x: Int) -> "hello world" } +} \ No newline at end of file diff --git a/idea/testData/intentions/operatorToFunction/notApplicableAssignment.kt b/idea/testData/intentions/operatorToFunction/notApplicableAssignment.kt new file mode 100644 index 00000000000..bf1f852a694 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/notApplicableAssignment.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +fun foo(b: Any) { + var a: Any + a = b +} diff --git a/idea/testData/intentions/operatorToFunction/notApplicableCallWithInvokeMethod.kt b/idea/testData/intentions/operatorToFunction/notApplicableCallWithInvokeMethod.kt new file mode 100644 index 00000000000..5c4330d2a4d --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/notApplicableCallWithInvokeMethod.kt @@ -0,0 +1,8 @@ +// IS_APPLICABLE: false +class coffee() { + fun invoke() { + } +} +fun main() { + val f = coffee().invoke() +} \ No newline at end of file diff --git a/idea/testData/intentions/operatorToFunction/notApplicableFunctionCallWithCallableReference.kt b/idea/testData/intentions/operatorToFunction/notApplicableFunctionCallWithCallableReference.kt new file mode 100644 index 00000000000..919dffa23c3 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/notApplicableFunctionCallWithCallableReference.kt @@ -0,0 +1,6 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false +fun foo() { + val bar = { (x: Int) -> x + 1 } + val incremented = listOf(1, 2, 3).map(bar) +} \ No newline at end of file diff --git a/idea/testData/intentions/operatorToFunction/notApplicableNewClassObject.kt b/idea/testData/intentions/operatorToFunction/notApplicableNewClassObject.kt new file mode 100644 index 00000000000..56b1fd31adf --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/notApplicableNewClassObject.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +class bar() {} +fun main() { + val x = bar() +} \ No newline at end of file diff --git a/idea/testData/intentions/operatorToFunction/postfixMinusMinus.kt b/idea/testData/intentions/operatorToFunction/postfixMinusMinus.kt new file mode 100644 index 00000000000..1e4cfb32427 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/postfixMinusMinus.kt @@ -0,0 +1,4 @@ +fun foo() { + var a = 5 + a-- +} diff --git a/idea/testData/intentions/operatorToFunction/postfixMinusMinus.kt.after b/idea/testData/intentions/operatorToFunction/postfixMinusMinus.kt.after new file mode 100644 index 00000000000..56091438b24 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/postfixMinusMinus.kt.after @@ -0,0 +1,4 @@ +fun foo() { + var a = 5 + a.dec() +} diff --git a/idea/testData/intentions/operatorToFunction/prefixPlus.kt b/idea/testData/intentions/operatorToFunction/prefixPlus.kt new file mode 100644 index 00000000000..59243c4dfb5 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/prefixPlus.kt @@ -0,0 +1,3 @@ +fun foo(a: Int) { + +a +} diff --git a/idea/testData/intentions/operatorToFunction/prefixPlus.kt.after b/idea/testData/intentions/operatorToFunction/prefixPlus.kt.after new file mode 100644 index 00000000000..01f36b6001c --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/prefixPlus.kt.after @@ -0,0 +1,3 @@ +fun foo(a: Int) { + a.plus() +} diff --git a/idea/testData/intentions/operatorToFunction/prefixPlusPlus.kt b/idea/testData/intentions/operatorToFunction/prefixPlusPlus.kt new file mode 100644 index 00000000000..9462ec5a2d0 --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/prefixPlusPlus.kt @@ -0,0 +1,4 @@ +fun foo() { + var a = 0 + ++a +} diff --git a/idea/testData/intentions/operatorToFunction/prefixPlusPlus.kt.after b/idea/testData/intentions/operatorToFunction/prefixPlusPlus.kt.after new file mode 100644 index 00000000000..c4aa68e20ce --- /dev/null +++ b/idea/testData/intentions/operatorToFunction/prefixPlusPlus.kt.after @@ -0,0 +1,4 @@ +fun foo() { + var a = 0 + a.inc() +} diff --git a/idea/testData/quickfix/autoImports/noImportInSafeQualifiedExpressionNotFirst.after.kt b/idea/testData/quickfix/autoImports/noImportInSafeQualifiedExpressionNotFirst.after.kt index 12443030b22..98b12da810d 100644 --- a/idea/testData/quickfix/autoImports/noImportInSafeQualifiedExpressionNotFirst.after.kt +++ b/idea/testData/quickfix/autoImports/noImportInSafeQualifiedExpressionNotFirst.after.kt @@ -1,5 +1,8 @@ // "class com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFixBase" "false" // ERROR: Unresolved reference: SomeTest +// ACTION: Edit intention settings +// ACTION: Replace safe access expression with 'if' expression +// ACTION: Disable 'Replace Safe Access Expression with 'if' Expression' package testing diff --git a/idea/tests/org/jetbrains/jet/plugin/intentions/AbstractCodeTransformationTest.java b/idea/tests/org/jetbrains/jet/plugin/intentions/AbstractCodeTransformationTest.java index dce19dd0184..628df8d30c9 100644 --- a/idea/tests/org/jetbrains/jet/plugin/intentions/AbstractCodeTransformationTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/intentions/AbstractCodeTransformationTest.java @@ -270,6 +270,10 @@ public abstract class AbstractCodeTransformationTest extends LightCodeInsightTes doTestIntention(path, new MakeTypeImplicitInLambdaIntention()); } + public void doTestOperatorToFunction(@NotNull String path) throws Exception { + doTestIntention(path, new OperatorToFunctionIntention()); + } + public void doTestConvertToForEachLoop(@NotNull String path) throws Exception { doTestIntention(path, new ConvertToForEachLoopIntention()); }