"Make types explicit in lambda" intention: not available on right brace + adapted to new lambda syntax + refactored
This commit is contained in:
@@ -320,8 +320,6 @@ remove.explicit.type.arguments=Remove explicit type arguments
|
||||
remove.explicit.type.arguments.family=Remove Explicit Type Arguments
|
||||
convert.if.with.throw.to.assert=Replace 'if' with 'assert' statement
|
||||
convert.if.with.throw.to.assert.family=Replace 'if' with 'assert' Statement
|
||||
make.type.explicit.in.lambda=Make types explicit in lambda
|
||||
make.type.explicit.in.lambda.family=Make Types Explicit In Lambda
|
||||
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)
|
||||
|
||||
|
||||
+19
-55
@@ -26,47 +26,39 @@ import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
|
||||
public class MakeTypeExplicitInLambdaIntention : JetSelfTargetingIntention<JetFunctionLiteralExpression>(
|
||||
"make.type.explicit.in.lambda", javaClass()) {
|
||||
public class MakeTypeExplicitInLambdaIntention : JetSelfTargetingIntention<JetFunctionLiteralExpression>(javaClass(), "Make types explicit in lambda") {
|
||||
|
||||
override fun isApplicableTo(element: JetFunctionLiteralExpression, caretOffset: Int): Boolean {
|
||||
val openBraceOffset = element.getLeftCurlyBrace().getStartOffset()
|
||||
val closeBraceOffset = element.getRightCurlyBrace()?.getStartOffset()
|
||||
val arrow = element.getFunctionLiteral().getArrowNode()
|
||||
if (arrow != null && !(openBraceOffset < caretOffset && caretOffset < arrow.getStartOffset() + 2) &&
|
||||
caretOffset != closeBraceOffset) return false
|
||||
else if (arrow == null && caretOffset != openBraceOffset + 1 && caretOffset != closeBraceOffset) return false
|
||||
if (arrow != null) {
|
||||
if (caretOffset > arrow.getTextRange().getEndOffset()) return false
|
||||
if (element.getValueParameters().all { it.getTypeReference() != null }) return false
|
||||
}
|
||||
else {
|
||||
if (!element.getLeftCurlyBrace().getTextRange().containsOffset(caretOffset)) return false
|
||||
}
|
||||
|
||||
val context = element.analyze()
|
||||
val func = context[BindingContext.FUNCTION, element.getFunctionLiteral()]
|
||||
if (func == null || ErrorUtils.containsErrorType(func)) return false
|
||||
|
||||
if (hasImplicitReturnType(element) && func.getReturnType() != null) return true
|
||||
if (hasImplicitReceiverType(element) && func.getExtensionReceiverParameter()?.getType() != null) return true
|
||||
|
||||
val params = element.getValueParameters()
|
||||
return params.any { it.getTypeReference() == null }
|
||||
val functionDescriptor = element.analyze()[BindingContext.FUNCTION, element.getFunctionLiteral()] ?: return false
|
||||
return functionDescriptor.getValueParameters().none { it.getType().isError() }
|
||||
}
|
||||
|
||||
override fun applyTo(element: JetFunctionLiteralExpression, editor: Editor) {
|
||||
val functionLiteral = element.getFunctionLiteral()
|
||||
val context = element.analyze()
|
||||
val func = context[BindingContext.FUNCTION, functionLiteral]!!
|
||||
|
||||
// Step 1: make the parameters types explicit
|
||||
val valueParameters = func.getValueParameters()
|
||||
val parameterString = valueParameters.map({descriptor -> "" + descriptor.getName() +
|
||||
": " + IdeDescriptorRenderers.SOURCE_CODE.renderType(descriptor.getType())
|
||||
}).makeString(", ", "(", ")")
|
||||
val psiFactory = JetPsiFactory(element)
|
||||
val newParameterList = psiFactory.createParameterList(parameterString)
|
||||
val functionLiteral = element.getFunctionLiteral()
|
||||
val functionDescriptor = element.analyze()[BindingContext.FUNCTION, functionLiteral]!!
|
||||
|
||||
val parameterString = functionDescriptor.getValueParameters()
|
||||
.map { "${it.getName()}: ${IdeDescriptorRenderers.SOURCE_CODE.renderType(it.getType())}" }
|
||||
.joinToString(", ")
|
||||
|
||||
val newParameterList = psiFactory.createFunctionLiteralParameterList(parameterString)
|
||||
val oldParameterList = functionLiteral.getValueParameterList()
|
||||
if (oldParameterList != null) {
|
||||
oldParameterList.replace(newParameterList)
|
||||
}
|
||||
else {
|
||||
val openBraceElement = functionLiteral.getLBrace()
|
||||
val nextSibling = openBraceElement?.getNextSibling()
|
||||
val nextSibling = openBraceElement.getNextSibling()
|
||||
val addNewline = nextSibling is PsiWhiteSpace && nextSibling.getText()?.contains("\n") ?: false
|
||||
val (whitespace, arrow) = psiFactory.createWhitespaceAndArrow()
|
||||
functionLiteral.addRangeAfter(whitespace, arrow, openBraceElement)
|
||||
@@ -76,33 +68,5 @@ public class MakeTypeExplicitInLambdaIntention : JetSelfTargetingIntention<JetFu
|
||||
}
|
||||
}
|
||||
ShortenReferences.DEFAULT.process(element.getValueParameters())
|
||||
|
||||
// Step 2: make the return type explicit
|
||||
val expectedReturnType = func.getReturnType()
|
||||
if (hasImplicitReturnType(element) && expectedReturnType != null) {
|
||||
val paramList = functionLiteral.getValueParameterList()
|
||||
val returnTypeColon = psiFactory.createColon()
|
||||
val returnTypeExpr = psiFactory.createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(expectedReturnType))
|
||||
functionLiteral.addAfter(returnTypeExpr, paramList)
|
||||
functionLiteral.addAfter(returnTypeColon, paramList)
|
||||
ShortenReferences.DEFAULT.process(functionLiteral.getTypeReference()!!)
|
||||
}
|
||||
|
||||
// Step 3: make the receiver type explicit
|
||||
val expectedReceiverType = func.getExtensionReceiverParameter()?.getType()
|
||||
if (hasImplicitReceiverType(element) && expectedReceiverType != null) {
|
||||
val receiverTypeString = IdeDescriptorRenderers.SOURCE_CODE.renderType(expectedReceiverType)
|
||||
val dot = functionLiteral.addBefore(psiFactory.createDot(), functionLiteral.getValueParameterList())
|
||||
functionLiteral.addBefore(psiFactory.createType(receiverTypeString), dot)
|
||||
ShortenReferences.DEFAULT.process(functionLiteral.getReceiverTypeReference()!!)
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasImplicitReturnType(element: JetFunctionLiteralExpression): Boolean {
|
||||
return !element.hasDeclaredReturnType()
|
||||
}
|
||||
|
||||
private fun hasImplicitReceiverType(element: JetFunctionLiteralExpression): Boolean {
|
||||
return element.getFunctionLiteral().getReceiverTypeReference() == null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ public class TestingUse {
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val coercion = TestingUse().test5({(x: Int): Unit -> x + 2}, 20)
|
||||
val coercion = TestingUse().test5({ x: Int -> x + 2}, 20)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun main() {
|
||||
val x = {<caret>(): Unit -> }
|
||||
val x = {<caret> -> }
|
||||
}
|
||||
@@ -5,5 +5,5 @@ class TestingUse {
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val num = TestingUse().test3({(it: Int): Int -> it * 2}, 20)
|
||||
val num = TestingUse().test3({ it: Int -> it * 2}, 20)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
fun main() {
|
||||
val oom: (Int)->Int = {
|
||||
(it: Int): Int ->
|
||||
it: Int ->
|
||||
it * 2
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun main() {
|
||||
val oom = {<caret> -> 42}
|
||||
}
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
fun main() {
|
||||
val oom = {(): Int -> 42}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun main() {
|
||||
val sum : (Int, Int) -> Int = { (x, y) -> <caret>x + y }
|
||||
val sum : (Int, Int) -> Int = { x, y -> <caret>x + y }
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@ public class TestingUse {
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val funcInfunc = TestingUse().test6({(f: (Int) -> Int): Boolean -> f(5) > 20}, {x -> x + 2})
|
||||
val funcInfunc = TestingUse().test6({ f: (Int) -> Int -> f(5) > 20}, {x -> x + 2})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
val foo: (Long) -> String = {
|
||||
(it: Long): String ->
|
||||
it: Long ->
|
||||
|
||||
|
||||
it.toString()
|
||||
|
||||
@@ -6,5 +6,5 @@ class TestingUse {
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val num = TestingUse().test4({(<caret>x, str) -> }, 5)
|
||||
val num = TestingUse().test4({ <caret>x, str -> }, 5)
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@ class TestingUse {
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val num = TestingUse().test4({(x: Int, str: String): Unit -> }, 5)
|
||||
val num = TestingUse().test4({ x: Int, str: String -> }, 5)
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
fun main() {
|
||||
val foo: (Int) -> String = {(x: Int) -> "This number is " + x<caret>}
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
fun main() {
|
||||
val foo: (Int) -> String = {(x: Int): String -> "This number is " + x}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fun main() {
|
||||
val bar: (Array<String>) -> Int = {<caret>(arr): Int -> arr.size()}
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
fun main() {
|
||||
val bar: (Array<String>) -> Int = {(arr: Array<String>): Int -> arr.size()}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fun main() {
|
||||
val randomFunction: (x: kotlin.support.AbstractIterator<Int>, y: kotlin.String) -> kotlin.String = {(<caret>x, str) -> str}
|
||||
val randomFunction: (x: kotlin.support.AbstractIterator<Int>, y: kotlin.String) -> kotlin.String = { <caret>x, str -> str}
|
||||
}
|
||||
|
||||
// WITH_RUNTIME
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import kotlin.support.AbstractIterator
|
||||
|
||||
fun main() {
|
||||
val randomFunction: (x: kotlin.support.AbstractIterator<Int>, y: kotlin.String) -> kotlin.String = {(x: AbstractIterator<Int>, str: String): String -> str}
|
||||
val randomFunction: (x: kotlin.support.AbstractIterator<Int>, y: kotlin.String) -> kotlin.String = { x: AbstractIterator<Int>, str: String -> str}
|
||||
}
|
||||
|
||||
// WITH_RUNTIME
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
fun main() {
|
||||
val randomFunction: kotlin.support.AbstractIterator<kotlin.Int>.(x: Int) -> Boolean = {<caret>y -> if (this.next() < y) true else false}
|
||||
}
|
||||
|
||||
// WITH_RUNTIME
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
import kotlin.support.AbstractIterator
|
||||
|
||||
fun main() {
|
||||
val randomFunction: kotlin.support.AbstractIterator<kotlin.Int>.(x: Int) -> Boolean = { AbstractIterator<Int>.(y: Int): Boolean -> if (this.next() < y) true else false}
|
||||
}
|
||||
|
||||
// WITH_RUNTIME
|
||||
@@ -1,5 +0,0 @@
|
||||
fun main(c: kotlin.support.AbstractIterator<Int>) {
|
||||
val f = { <caret>(x: Int) -> c}
|
||||
}
|
||||
|
||||
// WITH_RUNTIME
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
import kotlin.support.AbstractIterator
|
||||
|
||||
fun main(c: kotlin.support.AbstractIterator<Int>) {
|
||||
val f = {(x: Int): AbstractIterator<Int> -> c}
|
||||
}
|
||||
|
||||
// WITH_RUNTIME
|
||||
@@ -5,5 +5,5 @@ class TestingUse {
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val num = TestingUse().test3({(x: Int): Int -> 2*x}, 20)
|
||||
val num = TestingUse().test3({ x: Int -> 2*x}, 20)
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
class TestingUse {
|
||||
fun test(sum: Int.(a: Int) -> Int, b: Int): Int {
|
||||
return b.sum(b)
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val num = TestingUse().test({ <caret>x -> x + 2 }, 20)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
class TestingUse {
|
||||
fun test(sum: Int.(a: Int) -> Int, b: Int): Int {
|
||||
return b.sum(b)
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val num = TestingUse().test({ Int.(x: Int): Int -> x + 2 }, 20)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun main() {
|
||||
val sum = { Int.(<caret>x: Int, y: Int) : Int -> x + y }
|
||||
val sum = { <caret>x: Int, y: Int -> x + y }
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
// ERROR: Cannot infer a type for this parameter. Please specify it explicitly.
|
||||
// ERROR: Cannot infer a type for this parameter. Please specify it explicitly.
|
||||
fun main() {
|
||||
val sum = { (x, <caret>y) -> x + y // Type of x and y cannot be inferred, so intention can't be used
|
||||
val sum = { x, <caret>y -> x + y // Type of x and y cannot be inferred, so intention can't be used
|
||||
}
|
||||
|
||||
@@ -4411,48 +4411,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("paramDeclaredReturnNotDeclared.kt")
|
||||
public void testParamDeclaredReturnNotDeclared() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/makeTypeExplicitInLambda/paramDeclaredReturnNotDeclared.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("returnDeclaredParamNotDeclared.kt")
|
||||
public void testReturnDeclaredParamNotDeclared() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/makeTypeExplicitInLambda/returnDeclaredParamNotDeclared.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("shortenReferencesForParams.kt")
|
||||
public void testShortenReferencesForParams() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/makeTypeExplicitInLambda/shortenReferencesForParams.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("shortenReferencesForReceiver.kt")
|
||||
public void testShortenReferencesForReceiver() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/makeTypeExplicitInLambda/shortenReferencesForReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("shortenReferencesForReturnType.kt")
|
||||
public void testShortenReferencesForReturnType() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/makeTypeExplicitInLambda/shortenReferencesForReturnType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("singleParam.kt")
|
||||
public void testSingleParam() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/makeTypeExplicitInLambda/singleParam.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("singleParamWithReceiver.kt")
|
||||
public void testSingleParamWithReceiver() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/makeTypeExplicitInLambda/singleParamWithReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("typesAlreadyExplicit.kt")
|
||||
public void testTypesAlreadyExplicit() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/makeTypeExplicitInLambda/typesAlreadyExplicit.kt");
|
||||
|
||||
Reference in New Issue
Block a user