Simplify message expression from lambda argument when converting assert to if.

This commit is contained in:
Ilya Gorbunov
2015-11-09 23:13:27 +03:00
parent 3639afafce
commit a793a63b73
21 changed files with 88 additions and 24 deletions
@@ -23,8 +23,12 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.util.ShortenReferences import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.check
public class ConvertAssertToIfWithThrowIntention : SelfTargetingIntention<KtCallExpression>(javaClass(), "Replace 'assert' with 'if' statement"), LowPriorityAction { public class ConvertAssertToIfWithThrowIntention : SelfTargetingIntention<KtCallExpression>(javaClass(), "Replace 'assert' with 'if' statement"), LowPriorityAction {
override fun isApplicableTo(element: KtCallExpression, caretOffset: Int): Boolean { override fun isApplicableTo(element: KtCallExpression, caretOffset: Int): Boolean {
@@ -44,17 +48,21 @@ public class ConvertAssertToIfWithThrowIntention : SelfTargetingIntention<KtCall
override fun applyTo(element: KtCallExpression, editor: Editor) { override fun applyTo(element: KtCallExpression, editor: Editor) {
val args = element.getValueArguments() val args = element.getValueArguments()
val conditionText = args[0]?.getArgumentExpression()?.getText() ?: return val conditionText = args[0]?.getArgumentExpression()?.getText() ?: return
val functionLiteral = element.getFunctionLiteralArguments().singleOrNull() val functionLiteralArgument = element.functionLiteralArguments.singleOrNull()
val messageIsFunction = messageIsFunction(element) val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
val psiFactory = KtPsiFactory(element) val psiFactory = KtPsiFactory(element)
val messageExpr = when { val messageFunctionExpr = when {
args.size() == 2 -> args[1]?.getArgumentExpression() ?: return args.size == 2 -> args[1]?.getArgumentExpression() ?: return
functionLiteral != null -> functionLiteral!! functionLiteralArgument != null -> functionLiteralArgument.getFunctionLiteral()
else -> psiFactory.createExpression("\"Assertion failed\"") else -> null
} }
val extractedMessageSingleExpr = (messageFunctionExpr as? KtFunctionLiteralExpression)?.let { extractMessageSingleExpression(it, bindingContext) }
val messageIsFunction = extractedMessageSingleExpr == null && messageIsFunction(element, bindingContext)
val messageExpr = extractedMessageSingleExpr ?: messageFunctionExpr ?: psiFactory.createExpression("\"Assertion failed\"")
val ifExpression = replaceWithIfThenThrowExpression(element) val ifExpression = replaceWithIfThenThrowExpression(element)
// shorten java.lang.AssertionError // shorten java.lang.AssertionError
@@ -85,8 +93,14 @@ public class ConvertAssertToIfWithThrowIntention : SelfTargetingIntention<KtCall
simplifyConditionIfPossible(ifExpression) simplifyConditionIfPossible(ifExpression)
} }
private fun messageIsFunction(callExpr: KtCallExpression): Boolean { private fun extractMessageSingleExpression(functionLiteral: KtFunctionLiteralExpression, bindingContext: BindingContext): KtExpression? {
val resolvedCall = callExpr.getResolvedCall(callExpr.analyze()) ?: return false return functionLiteral.bodyExpression?.statements?.singleOrNull()?.let { singleStatement ->
singleStatement.check { it.isUsedAsExpression(bindingContext) }
}
}
private fun messageIsFunction(callExpr: KtCallExpression, bindingContext: BindingContext): Boolean {
val resolvedCall = callExpr.getResolvedCall(bindingContext) ?: return false
val valParameters = resolvedCall.getResultingDescriptor().getValueParameters() val valParameters = resolvedCall.getResultingDescriptor().getValueParameters()
return valParameters.size() > 1 && !KotlinBuiltIns.isAny(valParameters[1].type) return valParameters.size() > 1 && !KotlinBuiltIns.isAny(valParameters[1].type)
} }
@@ -1,7 +1,7 @@
package a package a
fun foo() { fun foo() {
<caret>assert(true, "text") <caret>assert(true, { "text" })
} }
class AssertionError class AssertionError
@@ -1,4 +1,4 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
<caret>assert(true && false, "text") <caret>assert(true && false, { "text" })
} }
@@ -1,4 +1,4 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
<caret>assert(1 > 0, "text") <caret>assert(1 > 0) { "text" }
} }
@@ -1,4 +1,4 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
<caret>assert(0 != 1, "text") <caret>assert(0 != 1) { "text" }
} }
@@ -2,5 +2,5 @@
fun foo() { fun foo() {
val x = true val x = true
val y = false val y = false
<caret>assert(x || y, "text") <caret>assert(x || y) { "text" }
} }
@@ -1,5 +1,5 @@
fun main(args: Array<String>) { fun main(args: Array<String>) {
asse<caret>rt(false, "mess" as kotlin.String) asse<caret>rt(false) { "mess" as kotlin.String }
} }
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,4 +1,4 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
kotlin.<caret>assert(true, "text") kotlin.<caret>assert(true) {"text"}
} }
@@ -1,6 +1,6 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
<caret>assert(bar(), "text") <caret>assert(bar()) { "text" }
} }
fun bar(): Boolean = true fun bar(): Boolean = true
@@ -1,4 +1,6 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
<caret>assert(true, { "text" }) <caret>assert(true, {
if (true) "text" else return
})
} }
@@ -1,6 +1,6 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
if (!true) { if (!true) {
throw AssertionError({ "text" }()) throw AssertionError(if (true) "text" else return)
} }
} }
@@ -1,4 +1,6 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
<caret>assert(true) { "text" } <caret>assert(true) {
return
}
} }
@@ -1,6 +1,8 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
if (!true) { if (!true) {
throw AssertionError({ "text" }()) throw AssertionError({
return
}())
} }
} }
@@ -0,0 +1,7 @@
// WITH_RUNTIME
fun foo() {
<caret>assert(true, {
val value = 1
"text and $value"
})
}
@@ -0,0 +1,9 @@
// WITH_RUNTIME
fun foo() {
if (!true) {
throw AssertionError({
val value = 1
"text and $value"
}())
}
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
fun foo() {
<caret>assert(true) {
if (false) return
"text"
}
}
@@ -0,0 +1,9 @@
// WITH_RUNTIME
fun foo() {
if (!true) {
throw AssertionError({
if (false) return
"text"
}())
}
}
@@ -1,4 +1,4 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
<caret>assert((true && false), "text") <caret>assert((true && false)) { "text" }
} }
@@ -1,4 +1,4 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
<caret>assert(true, "text") <caret>assert(true) { "text" }
} }
@@ -1,5 +1,5 @@
// WITH_RUNTIME // WITH_RUNTIME
fun foo() { fun foo() {
val f = "text" val f = "text"
<caret>assert(true, f) <caret>assert(true) { f }
} }
@@ -3088,6 +3088,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("lambdaMultiStatementMessageInsideParentheses.kt")
public void testLambdaMultiStatementMessageInsideParentheses() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertAssertToIf/lambdaMultiStatementMessageInsideParentheses.kt");
doTest(fileName);
}
@TestMetadata("lambdaMultiStatementMessageOutsideParentheses.kt")
public void testLambdaMultiStatementMessageOutsideParentheses() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertAssertToIf/lambdaMultiStatementMessageOutsideParentheses.kt");
doTest(fileName);
}
@TestMetadata("lambdaVariable.kt") @TestMetadata("lambdaVariable.kt")
public void testLambdaVariable() throws Exception { public void testLambdaVariable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertAssertToIf/lambdaVariable.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertAssertToIf/lambdaVariable.kt");