mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[kotlin k2] Support K2 the "Invert 'if' condition" intention
^KTIJ-27469 GitOrigin-RevId: c8f7bcc4eb5264e29a4e05201fadc26dd9561998
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1154f1a6ca
commit
d5d5b86b21
+7
@@ -235,5 +235,12 @@
|
||||
<bundleName>messages.KotlinBundle</bundleName>
|
||||
<categoryKey>group.names.kotlin</categoryKey>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<language>kotlin</language>
|
||||
<className>org.jetbrains.kotlin.idea.k2.codeinsight.intentions.InvertIfConditionIntention</className>
|
||||
<bundleName>messages.KotlinBundle</bundleName>
|
||||
<categoryKey>group.names.kotlin</categoryKey>
|
||||
</intentionAction>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
+4
-2
@@ -23,12 +23,14 @@ internal class ConvertBinaryExpressionWithDemorgansLawIntention :
|
||||
override fun apply(element: KtBinaryExpression, context: AnalysisActionContext<DemorgansLawContext>, updater: ModPsiUpdater) {
|
||||
val expr = element.topmostBinaryExpression()
|
||||
if (splitBooleanSequence(expr) == null) return
|
||||
applyDemorgansLaw(element, context.analyzeContext)
|
||||
applyDemorgansLaw(expr, context.analyzeContext)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
override fun prepareContext(element: KtBinaryExpression): DemorgansLawContext? {
|
||||
return prepareDemorgansLawContext(element)
|
||||
val operands = element.topmostBinaryExpression().let(::splitBooleanSequence) ?: return null
|
||||
if (operands.any { !it.isBoolean }) return null
|
||||
return prepareDemorgansLawContext(operands)
|
||||
}
|
||||
|
||||
override fun getActionName(element: KtBinaryExpression, context: DemorgansLawContext): String {
|
||||
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.k2.codeinsight.intentions
|
||||
|
||||
import com.intellij.modcommand.ModPsiUpdater
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
import com.intellij.refactoring.suggested.createSmartPointer
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.base.psi.getLineNumber
|
||||
import org.jetbrains.kotlin.idea.base.psi.replaced
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.intentions.AbstractKotlinModCommandWithContext
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.intentions.AnalysisActionContext
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicators.KotlinApplicabilityRange
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicators.applicabilityTarget
|
||||
import org.jetbrains.kotlin.idea.codeinsight.utils.*
|
||||
import org.jetbrains.kotlin.idea.util.CommentSaver
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
|
||||
|
||||
internal class InvertIfConditionIntention : AbstractKotlinModCommandWithContext<KtIfExpression, Context>(KtIfExpression::class) {
|
||||
override fun getFamilyName(): String = KotlinBundle.message("invert.if.condition")
|
||||
override fun getActionName(element: KtIfExpression, context: Context): String = familyName
|
||||
|
||||
context(KtAnalysisSession)
|
||||
override fun prepareContext(element: KtIfExpression): Context {
|
||||
val rBrace = parentBlockRBrace(element)
|
||||
val commentSavingRange = if (rBrace != null)
|
||||
PsiChildRange(element, rBrace)
|
||||
else
|
||||
PsiChildRange.singleElement(element)
|
||||
|
||||
val commentSaver = CommentSaver(commentSavingRange)
|
||||
|
||||
val condition = element.condition!!
|
||||
val areAllOperandsBoolean =
|
||||
(condition is KtBinaryExpression && splitBooleanSequence(condition)?.all { it.isBoolean } == true) || condition.isBoolean
|
||||
val newCondition = (condition as? KtQualifiedExpression)?.invertSelectorFunction() ?: condition.negate()
|
||||
|
||||
val isParentFunUnit = element.getParentOfType<KtNamedFunction>(true)
|
||||
val isUnit = isParentFunUnit != null && isParentFunUnit.getReturnKtType().isUnit
|
||||
|
||||
val demorgansLawContext = if (areAllOperandsBoolean) {
|
||||
getBinaryExpression(newCondition)?.let(::splitBooleanSequence)?.let { expressions ->
|
||||
prepareDemorgansLawContext(expressions)
|
||||
}
|
||||
} else null
|
||||
|
||||
return Context(newCondition.createSmartPointer(), demorgansLawContext, isUnit, commentSaver)
|
||||
}
|
||||
|
||||
private fun getBinaryExpression(expression: KtExpression): KtBinaryExpression? {
|
||||
(expression as? KtPrefixExpression)?.let {
|
||||
if (it.operationReference.getReferencedNameElementType() == KtTokens.EXCL) {
|
||||
val binaryExpr = (it.baseExpression as? KtParenthesizedExpression)?.expression as? KtBinaryExpression
|
||||
return binaryExpr?.topmostBinaryExpression()
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getApplicabilityRange(): KotlinApplicabilityRange<KtIfExpression> = applicabilityTarget { ifExpression: KtIfExpression ->
|
||||
ifExpression.ifKeyword
|
||||
}
|
||||
|
||||
override fun isApplicableByPsi(element: KtIfExpression): Boolean {
|
||||
return element.condition != null && element.then != null
|
||||
}
|
||||
|
||||
override fun apply(element: KtIfExpression, context: AnalysisActionContext<Context>, updater: ModPsiUpdater) {
|
||||
val rBrace = parentBlockRBrace(element)
|
||||
if (rBrace != null) element.nextEolCommentOnSameLine()?.delete()
|
||||
|
||||
val analyzeContext = context.analyzeContext
|
||||
val newIf = handleSpecialCases(element, analyzeContext) ?: handleStandardCase(element, analyzeContext)
|
||||
|
||||
val commentRestoreRange = if (rBrace != null)
|
||||
PsiChildRange(newIf, rBrace)
|
||||
else
|
||||
PsiChildRange(newIf, parentBlockRBrace(newIf) ?: newIf)
|
||||
|
||||
context.analyzeContext.commentSaver.restore(commentRestoreRange)
|
||||
|
||||
val binaryExpr = newIf.condition?.let(::getBinaryExpression)
|
||||
if (binaryExpr != null) {
|
||||
context.analyzeContext.demorgansLawContext?.let { demorgansLawContext ->
|
||||
applyDemorgansLaw(binaryExpr, demorgansLawContext)
|
||||
}
|
||||
}
|
||||
|
||||
updater.moveTo(newIf)
|
||||
}
|
||||
|
||||
private fun handleStandardCase(ifExpression: KtIfExpression, context: Context): KtIfExpression {
|
||||
val psiFactory = KtPsiFactory(ifExpression.project)
|
||||
|
||||
val thenBranch = ifExpression.then!!
|
||||
val elseBranch = ifExpression.`else` ?: psiFactory.createEmptyBody()
|
||||
|
||||
val newThen = if (elseBranch is KtIfExpression)
|
||||
psiFactory.createSingleStatementBlock(elseBranch)
|
||||
else
|
||||
elseBranch
|
||||
|
||||
val newElse = if (thenBranch is KtBlockExpression && thenBranch.statements.isEmpty())
|
||||
null
|
||||
else
|
||||
thenBranch
|
||||
|
||||
val conditionLineNumber = ifExpression.condition?.getLineNumber(false)
|
||||
val thenBranchLineNumber = thenBranch.getLineNumber(false)
|
||||
val elseKeywordLineNumber = ifExpression.elseKeyword?.getLineNumber()
|
||||
val afterCondition = if (newThen !is KtBlockExpression && elseKeywordLineNumber != elseBranch.getLineNumber(false)) "\n" else ""
|
||||
val beforeElse = if (newThen !is KtBlockExpression && conditionLineNumber != elseKeywordLineNumber) "\n" else " "
|
||||
val afterElse = if (newElse !is KtBlockExpression && conditionLineNumber != thenBranchLineNumber) "\n" else " "
|
||||
|
||||
val newCondition = context.newCondition.element!!
|
||||
val newIf = if (newElse == null) {
|
||||
psiFactory.createExpressionByPattern("if ($0)$afterCondition$1", newCondition, newThen)
|
||||
} else {
|
||||
psiFactory.createExpressionByPattern("if ($0)$afterCondition$1${beforeElse}else$afterElse$2", newCondition, newThen, newElse)
|
||||
} as KtIfExpression
|
||||
|
||||
return ifExpression.replaced(newIf)
|
||||
}
|
||||
|
||||
private fun handleSpecialCases(ifExpression: KtIfExpression, context: Context): KtIfExpression? {
|
||||
val elseBranch = ifExpression.`else`
|
||||
if (elseBranch != null) return null
|
||||
|
||||
val psiFactory = KtPsiFactory(ifExpression.project)
|
||||
|
||||
val thenBranch = ifExpression.then!!
|
||||
val lastThenStatement = thenBranch.lastBlockStatementOrThis()
|
||||
val newCondition = context.newCondition.element!!
|
||||
if (lastThenStatement.isExitStatement()) {
|
||||
val block = ifExpression.parent as? KtBlockExpression
|
||||
if (block != null) {
|
||||
val rBrace = block.rBrace
|
||||
val afterIfInBlock = ifExpression.siblings(withItself = false).takeWhile { it != rBrace }.toList()
|
||||
val lastStatementInBlock = afterIfInBlock.lastIsInstanceOrNull<KtExpression>()
|
||||
if (lastStatementInBlock != null) {
|
||||
val exitStatementAfterIf = if (lastStatementInBlock.isExitStatement())
|
||||
lastStatementInBlock
|
||||
else
|
||||
exitStatementExecutedAfter(lastStatementInBlock, context)
|
||||
if (exitStatementAfterIf != null) {
|
||||
val first = afterIfInBlock.first()
|
||||
val last = afterIfInBlock.last()
|
||||
// build new then branch from statements after if (we will add exit statement if necessary later)
|
||||
val newThenRange = if (isEmptyReturn(lastThenStatement) && isEmptyReturn(lastStatementInBlock)) {
|
||||
PsiChildRange(first, lastStatementInBlock.prevSibling).trimWhiteSpaces()
|
||||
} else {
|
||||
PsiChildRange(first, last).trimWhiteSpaces()
|
||||
}
|
||||
val newIf =
|
||||
psiFactory.createExpressionByPattern("if ($0) { $1 }", newCondition, newThenRange) as KtIfExpression
|
||||
|
||||
// remove statements after if as they are moving under if
|
||||
block.deleteChildRange(first, last)
|
||||
|
||||
if (isEmptyReturn(lastThenStatement)) {
|
||||
if (block.parent is KtDeclarationWithBody && block.parent !is KtFunctionLiteral) {
|
||||
lastThenStatement.delete()
|
||||
}
|
||||
}
|
||||
val updatedIf = copyThenBranchAfter(ifExpression)
|
||||
|
||||
// check if we need to add exit statement to then branch
|
||||
if (exitStatementAfterIf != lastStatementInBlock) {
|
||||
// don't insert the exit statement, if the new if statement placement has the same exit statement executed after it
|
||||
val exitAfterNewIf = exitStatementExecutedAfter(updatedIf, context)
|
||||
if (exitAfterNewIf == null || !matches(exitAfterNewIf, exitStatementAfterIf)) {
|
||||
val newThen = newIf.then as KtBlockExpression
|
||||
newThen.addBefore(exitStatementAfterIf, newThen.rBrace)
|
||||
}
|
||||
}
|
||||
|
||||
return updatedIf.replace(newIf) as KtIfExpression
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val exitStatement = exitStatementExecutedAfter(ifExpression, context) ?: return null
|
||||
|
||||
val updatedIf = copyThenBranchAfter(ifExpression)
|
||||
val newIf = psiFactory.createExpressionByPattern("if ($0) $1", newCondition, exitStatement)
|
||||
return updatedIf.replace(newIf) as KtIfExpression
|
||||
}
|
||||
|
||||
private fun matches(exitExpr1: KtExpression, exitExpr2: KtExpression): Boolean {
|
||||
return if (exitExpr1 is KtReturnExpression && exitExpr2 is KtReturnExpression) {
|
||||
return exitExpr1 == exitExpr2 || (exitExpr1.returnedExpression == null && exitExpr2.returnedExpression == null)
|
||||
} else exitExpr1.javaClass == exitExpr2.javaClass
|
||||
}
|
||||
|
||||
private fun isEmptyReturn(statement: KtExpression) =
|
||||
statement is KtReturnExpression && statement.returnedExpression == null && statement.labeledExpression == null
|
||||
|
||||
private fun copyThenBranchAfter(ifExpression: KtIfExpression): KtIfExpression {
|
||||
val psiFactory = KtPsiFactory(ifExpression.project)
|
||||
val thenBranch = ifExpression.then ?: return ifExpression
|
||||
|
||||
val parent = ifExpression.parent
|
||||
if (parent !is KtBlockExpression) {
|
||||
assert(parent is KtContainerNode)
|
||||
val block = psiFactory.createEmptyBody()
|
||||
block.addAfter(ifExpression, block.lBrace)
|
||||
val newBlock = ifExpression.replaced(block)
|
||||
val newIf = newBlock.statements.single() as KtIfExpression
|
||||
return copyThenBranchAfter(newIf)
|
||||
}
|
||||
|
||||
if (thenBranch is KtBlockExpression) {
|
||||
(thenBranch.statements.lastOrNull() as? KtContinueExpression)?.delete()
|
||||
val range = thenBranch.contentRange()
|
||||
if (!range.isEmpty) {
|
||||
parent.addRangeAfter(range.first, range.last, ifExpression)
|
||||
parent.addAfter(psiFactory.createNewLine(), ifExpression)
|
||||
}
|
||||
} else if (thenBranch !is KtContinueExpression) {
|
||||
parent.addAfter(thenBranch, ifExpression)
|
||||
parent.addAfter(psiFactory.createNewLine(), ifExpression)
|
||||
}
|
||||
return ifExpression
|
||||
}
|
||||
|
||||
private fun exitStatementExecutedAfter(expression: KtExpression, context: Context): KtExpression? {
|
||||
when (val parent = expression.parent) {
|
||||
is KtBlockExpression -> {
|
||||
val lastStatement = parent.statements.last()
|
||||
return if (expression == lastStatement) {
|
||||
exitStatementExecutedAfter(parent, context)
|
||||
} else if (lastStatement.isExitStatement() &&
|
||||
expression.siblings(withItself = false).firstIsInstance<KtExpression>() == lastStatement
|
||||
) {
|
||||
lastStatement
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
is KtNamedFunction -> {
|
||||
if (parent.bodyExpression == expression && parent.hasBlockBody() && context.isParentFunUnit) {
|
||||
return KtPsiFactory(expression.project).createExpression("return")
|
||||
}
|
||||
}
|
||||
|
||||
is KtContainerNode -> when (val pparent = parent.parent) {
|
||||
is KtLoopExpression -> {
|
||||
if (expression == pparent.body) {
|
||||
return KtPsiFactory(expression.project).createExpression("continue")
|
||||
}
|
||||
}
|
||||
|
||||
is KtIfExpression -> {
|
||||
if (expression == pparent.then || expression == pparent.`else`) {
|
||||
return exitStatementExecutedAfter(pparent, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun KtExpression.isExitStatement(): Boolean = when (this) {
|
||||
is KtContinueExpression, is KtBreakExpression, is KtThrowExpression, is KtReturnExpression -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun parentBlockRBrace(element: KtIfExpression): PsiElement? = (element.parent as? KtBlockExpression)?.rBrace
|
||||
|
||||
private fun KtIfExpression.nextEolCommentOnSameLine(): PsiElement? = getLineNumber(false).let { lastLineNumber ->
|
||||
siblings(withItself = false)
|
||||
.takeWhile { it.getLineNumber() == lastLineNumber }
|
||||
.firstOrNull { it is PsiComment && it.node.elementType == KtTokens.EOL_COMMENT }
|
||||
}
|
||||
}
|
||||
|
||||
internal class Context(
|
||||
val newCondition: SmartPsiElementPointer<KtExpression>,
|
||||
val demorgansLawContext: DemorgansLawContext?,
|
||||
val isParentFunUnit: Boolean,
|
||||
val commentSaver: CommentSaver,
|
||||
)
|
||||
+293
@@ -4676,4 +4676,297 @@ public abstract class K2IntentionTestGenerated extends AbstractK2IntentionTest {
|
||||
runTest("../../../idea/tests/testData/intentions/convertBinaryExpressionWithDemorgansLaw/retainedParens.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../../../idea/tests/testData/intentions/invertIfCondition")
|
||||
public static class InvertIfCondition extends AbstractK2IntentionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("addSurroundingBlock_preserveComments.kt")
|
||||
public void testAddSurroundingBlock_preserveComments() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/addSurroundingBlock_preserveComments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("assignedToValue.kt")
|
||||
public void testAssignedToValue() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/assignedToValue.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("binaryExpression.kt")
|
||||
public void testBinaryExpression() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/binaryExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("booleanLiteral.kt")
|
||||
public void testBooleanLiteral() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/booleanLiteral.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("branchingIfStatements.kt")
|
||||
public void testBranchingIfStatements() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/branchingIfStatements.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("endOfLineCommentBug.kt")
|
||||
public void testEndOfLineCommentBug() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/endOfLineCommentBug.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("forLoopWithMultipleExpressions.kt")
|
||||
public void testForLoopWithMultipleExpressions() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/forLoopWithMultipleExpressions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionWithReturnExpression.kt")
|
||||
public void testFunctionWithReturnExpression() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/functionWithReturnExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifExpressionInsideForLoop.kt")
|
||||
public void testIfExpressionInsideForLoop() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/ifExpressionInsideForLoop.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifExpressionWithReturn.kt")
|
||||
public void testIfExpressionWithReturn() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/ifExpressionWithReturn.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifThenReturn.kt")
|
||||
public void testIfThenReturn() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/ifThenReturn.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifThenReturn2.kt")
|
||||
public void testIfThenReturn2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/ifThenReturn2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifThenReturn3.kt")
|
||||
public void testIfThenReturn3() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/ifThenReturn3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifThenReturn4.kt")
|
||||
public void testIfThenReturn4() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/ifThenReturn4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifWithBothBranchesReturn.kt")
|
||||
public void testIfWithBothBranchesReturn() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/ifWithBothBranchesReturn.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifWithBothBranchesSetter.kt")
|
||||
public void testIfWithBothBranchesSetter() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/ifWithBothBranchesSetter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("in.kt")
|
||||
public void testIn() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/in.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invertableOperator.kt")
|
||||
public void testInvertableOperator() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/invertableOperator.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("is.kt")
|
||||
public void testIs() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/is.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isBlank.kt")
|
||||
public void testIsBlank() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/isBlank.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isEmpty.kt")
|
||||
public void testIsEmpty() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/isEmpty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isNotBlank.kt")
|
||||
public void testIsNotBlank() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/isNotBlank.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isNotEmpty.kt")
|
||||
public void testIsNotEmpty() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/isNotEmpty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaNonLocalAndLocalReturn.kt")
|
||||
public void testLambdaNonLocalAndLocalReturn() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lambdaNonLocalAndLocalReturn.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaNonLocalReturn.kt")
|
||||
public void testLambdaNonLocalReturn() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lambdaNonLocalReturn.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lastStatement1.kt")
|
||||
public void testLastStatement1() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lastStatement1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lastStatement2.kt")
|
||||
public void testLastStatement2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lastStatement2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lastStatement3.kt")
|
||||
public void testLastStatement3() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lastStatement3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lastStatementBeforeBreak.kt")
|
||||
public void testLastStatementBeforeBreak() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lastStatementBeforeBreak.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lastStatementBeforeContinue.kt")
|
||||
public void testLastStatementBeforeContinue() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lastStatementBeforeContinue.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lastStatementBeforeReturn.kt")
|
||||
public void testLastStatementBeforeReturn() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lastStatementBeforeReturn.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lastStatementInLambda.kt")
|
||||
public void testLastStatementInLambda() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lastStatementInLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lastStatementInLoop.kt")
|
||||
public void testLastStatementInLoop() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lastStatementInLoop.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lastStatementInLoop2.kt")
|
||||
public void testLastStatementInLoop2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lastStatementInLoop2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lastStatementNonUnitMethod.kt")
|
||||
public void testLastStatementNonUnitMethod() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/lastStatementNonUnitMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("negatedExpression.kt")
|
||||
public void testNegatedExpression() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/negatedExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("negatedIsBlank.kt")
|
||||
public void testNegatedIsBlank() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/negatedIsBlank.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("negatedIsEmpty.kt")
|
||||
public void testNegatedIsEmpty() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/negatedIsEmpty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("negatedIsNotBlank.kt")
|
||||
public void testNegatedIsNotBlank() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/negatedIsNotBlank.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("negatedIsNotEmpty.kt")
|
||||
public void testNegatedIsNotEmpty() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/negatedIsNotEmpty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedIfWithReturn.kt")
|
||||
public void testNestedIfWithReturn() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/nestedIfWithReturn.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notBlock.kt")
|
||||
public void testNotBlock() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/notBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notBlock2.kt")
|
||||
public void testNotBlock2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/notBlock2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notBlock3.kt")
|
||||
public void testNotBlock3() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/notBlock3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notBlock4.kt")
|
||||
public void testNotBlock4() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/notBlock4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notBlock5.kt")
|
||||
public void testNotBlock5() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/notBlock5.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notBlock6.kt")
|
||||
public void testNotBlock6() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/notBlock6.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notBlock7.kt")
|
||||
public void testNotBlock7() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/notBlock7.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notBlock8.kt")
|
||||
public void testNotBlock8() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/notBlock8.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notIn.kt")
|
||||
public void testNotIn() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/notIn.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notIs.kt")
|
||||
public void testNotIs() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/notIs.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("returnIfExpression.kt")
|
||||
public void testReturnIfExpression() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/returnIfExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/simple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unnecessaryContinue.kt")
|
||||
public void testUnnecessaryContinue() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/unnecessaryContinue.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unnecessaryContinue2.kt")
|
||||
public void testUnnecessaryContinue2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/unnecessaryContinue2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unnecessaryContinue3.kt")
|
||||
public void testUnnecessaryContinue3() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/unnecessaryContinue3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("valueAndReturnBranches.kt")
|
||||
public void testValueAndReturnBranches() throws Exception {
|
||||
runTest("../../../idea/tests/testData/intentions/invertIfCondition/valueAndReturnBranches.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-6
@@ -14,18 +14,14 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
context(KtAnalysisSession)
|
||||
@OptIn(UnsafeCastFunction::class)
|
||||
fun prepareDemorgansLawContext(element: KtBinaryExpression): DemorgansLawContext? {
|
||||
val expr = element.topmostBinaryExpression()
|
||||
val operands = splitBooleanSequence(expr) ?: return null
|
||||
if (!expr.left.isBoolean || !expr.right.isBoolean) return null
|
||||
fun prepareDemorgansLawContext(operands: List<KtExpression>): DemorgansLawContext {
|
||||
val pointers = operands.asReversed().map { operand ->
|
||||
operand.safeAs<KtQualifiedExpression>()?.invertSelectorFunction() ?: operand.negate(false) { it.isBoolean }
|
||||
}.map { it.createSmartPointer() }
|
||||
return DemorgansLawContext(pointers)
|
||||
}
|
||||
|
||||
fun applyDemorgansLaw(element: KtBinaryExpression, context: DemorgansLawContext) {
|
||||
val expression = element.topmostBinaryExpression()
|
||||
fun applyDemorgansLaw(expression: KtBinaryExpression, context: DemorgansLawContext) {
|
||||
val operatorText = when (expression.operationToken) {
|
||||
KtTokens.ANDAND -> KtTokens.OROR.value
|
||||
KtTokens.OROR -> KtTokens.ANDAND.value
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.k2.codeinsight.intentions.InvertIfConditionIntention
|
||||
+1
@@ -48,6 +48,7 @@ internal fun MutableTWorkspace.generateK2IntentionTests() {
|
||||
model("${idea}intentions/branched/ifWhen/whenToIf", pattern = pattern)
|
||||
model("code-insight/intentions-k2/tests/testData/intentions", pattern = pattern)
|
||||
model("${idea}intentions/convertBinaryExpressionWithDemorgansLaw", pattern = pattern)
|
||||
model("${idea}intentions/invertIfCondition", pattern = pattern)
|
||||
}
|
||||
|
||||
testClass<AbstractK2GotoTestOrCodeActionTest> {
|
||||
|
||||
Reference in New Issue
Block a user