mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[kotlin] port IfThenToElvisInspection to k2
^KTIJ-29052 Fixed GitOrigin-RevId: 3c28a6169b8e49c7d29a949f6bc7ed185507168e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
39818bd035
commit
10960866f0
+12
-1
@@ -10,6 +10,8 @@ import org.jetbrains.kotlin.analysis.api.calls.KtCallCandidateInfo
|
||||
import org.jetbrains.kotlin.analysis.api.calls.KtCallableMemberCall
|
||||
import org.jetbrains.kotlin.analysis.api.calls.KtFunctionCall
|
||||
import org.jetbrains.kotlin.analysis.api.calls.KtImplicitReceiverValue
|
||||
import org.jetbrains.kotlin.analysis.api.calls.KtReceiverValue
|
||||
import org.jetbrains.kotlin.analysis.api.calls.KtSmartCastedReceiverValue
|
||||
import org.jetbrains.kotlin.analysis.api.calls.singleFunctionCallOrNull
|
||||
import org.jetbrains.kotlin.analysis.api.components.buildClassType
|
||||
import org.jetbrains.kotlin.analysis.api.signatures.KtFunctionLikeSignature
|
||||
@@ -185,6 +187,15 @@ fun KtReference.resolveToExpandedSymbol(): KtSymbol? = when (val symbol = resolv
|
||||
else -> symbol
|
||||
}
|
||||
|
||||
/**
|
||||
* @return implicit receivers of [this], including implicit receivers with smart casts, which are unwrapped to [KtImplicitReceiverValue]
|
||||
*/
|
||||
fun KtCallableMemberCall<*, *>.getImplicitReceivers(): List<KtImplicitReceiverValue> = partiallyAppliedSymbol
|
||||
.let { listOfNotNull(it.dispatchReceiver, it.extensionReceiver) }
|
||||
.filterIsInstance<KtImplicitReceiverValue>()
|
||||
.map { it.unwrapSmartCasts() }
|
||||
.filterIsInstance<KtImplicitReceiverValue>()
|
||||
|
||||
private tailrec fun KtReceiverValue.unwrapSmartCasts(): KtReceiverValue = when (this) {
|
||||
is KtSmartCastedReceiverValue -> original.unwrapSmartCasts()
|
||||
else -> this
|
||||
}
|
||||
@@ -6,19 +6,28 @@ import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
fun KtExpression.unwrapBlockOrParenthesis(): KtExpression {
|
||||
/**
|
||||
* If [this] is [KtBlockExpression], returns, after deparenthesizing, a single block statement or `null` if multiple statements are present.
|
||||
* Otherwise, simply returns deparenthesized [this].
|
||||
*/
|
||||
fun KtExpression.getSingleUnwrappedStatement(): KtExpression? {
|
||||
val innerExpression = KtPsiUtil.safeDeparenthesize(this, true)
|
||||
|
||||
if (innerExpression is KtBlockExpression) {
|
||||
val statement = innerExpression.statements.singleOrNull() ?: return this
|
||||
val statement = innerExpression.statements.singleOrNull() ?: return null
|
||||
val deparenthesized = KtPsiUtil.safeDeparenthesize(statement, true)
|
||||
if (deparenthesized is KtLambdaExpression) return this
|
||||
if (deparenthesized is KtLambdaExpression) return null
|
||||
return deparenthesized
|
||||
}
|
||||
|
||||
return innerExpression
|
||||
}
|
||||
|
||||
/**
|
||||
* See [getSingleUnwrappedStatement].
|
||||
*/
|
||||
fun KtExpression.getSingleUnwrappedStatementOrThis(): KtExpression = getSingleUnwrappedStatement() ?: this
|
||||
|
||||
fun KtBinaryExpression.expressionComparedToNull(): KtExpression? {
|
||||
val operationToken = this.operationToken
|
||||
if (operationToken != KtTokens.EQEQ && operationToken != KtTokens.EXCLEQ) return null
|
||||
@@ -32,4 +41,4 @@ fun KtBinaryExpression.expressionComparedToNull(): KtExpression? {
|
||||
return if (leftIsNull) right else left
|
||||
}
|
||||
|
||||
fun KtExpression?.isNullExpression(): Boolean = this?.unwrapBlockOrParenthesis()?.node?.elementType == KtNodeTypes.NULL
|
||||
fun KtExpression?.isNullExpression(): Boolean = this?.getSingleUnwrappedStatementOrThis()?.node?.elementType == KtNodeTypes.NULL
|
||||
+16
@@ -169,4 +169,20 @@ fun replaceSamConstructorCall(callExpression: KtCallExpression): KtLambdaExpress
|
||||
?: throw AssertionError("SAM constructor should have a FunctionLiteralExpression as single argument: ${callExpression.getElementTextWithContext()}")
|
||||
val ktExpression = callExpression.getQualifiedExpressionForSelectorOrThis()
|
||||
return runWriteActionIfPhysical(ktExpression) { ktExpression.replace(functionalArgument) as KtLambdaExpression }
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the expression which was actually inserted in the tree
|
||||
*/
|
||||
fun KtExpression.prependDotQualifiedReceiver(receiver: KtExpression, factory: KtPsiFactory): KtExpression {
|
||||
val dotQualified = factory.createExpressionByPattern("$0.$1", receiver, this)
|
||||
return this.replaced(dotQualified)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the expression which was actually inserted in the tree
|
||||
*/
|
||||
fun KtExpression.appendDotQualifiedSelector(selector: KtExpression, factory: KtPsiFactory): KtExpression {
|
||||
val dotQualified = factory.createExpressionByPattern("$0.$1", this, selector)
|
||||
return this.replaced(dotQualified)
|
||||
}
|
||||
+2
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.idea.base.psi.textRangeIn
|
||||
/**
|
||||
* The ranges are relative to the passed element,
|
||||
* i.e., if range covers the whole element when it should return `[0, element.length)`.
|
||||
*
|
||||
* See [org.jetbrains.kotlin.idea.codeinsights.impl.base.applicators.ApplicabilityRanges] for the commonly occurring applicability ranges.
|
||||
*/
|
||||
object ApplicabilityRange {
|
||||
|
||||
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.codeInsight
|
||||
|
||||
import com.intellij.modcommand.ModPsiUpdater
|
||||
import com.intellij.psi.util.parentsOfType
|
||||
import com.intellij.util.concurrency.annotations.RequiresWriteLock
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.calls.KtCallableMemberCall
|
||||
import org.jetbrains.kotlin.analysis.api.calls.KtSimpleFunctionCall
|
||||
import org.jetbrains.kotlin.analysis.api.calls.singleCallOrNull
|
||||
import org.jetbrains.kotlin.analysis.api.calls.successfulCallOrNull
|
||||
import org.jetbrains.kotlin.idea.base.analysis.api.utils.getImplicitReceivers
|
||||
import org.jetbrains.kotlin.idea.base.psi.expressionComparedToNull
|
||||
import org.jetbrains.kotlin.idea.base.psi.getSingleUnwrappedStatement
|
||||
import org.jetbrains.kotlin.idea.base.psi.prependDotQualifiedReceiver
|
||||
import org.jetbrains.kotlin.idea.base.psi.replaced
|
||||
import org.jetbrains.kotlin.idea.codeinsight.utils.getLeftMostReceiverExpressionOrThis
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.insertSafeCallsAfterReceiver
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.isSimplifiableTo
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.replaceVariableCallsWithExplicitInvokeCalls
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.wrapWithLet
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny
|
||||
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
|
||||
import org.jetbrains.kotlin.utils.exceptions.withPsiEntry
|
||||
|
||||
@ApiStatus.Internal
|
||||
data class IfThenTransformationData(
|
||||
val ifExpression: KtIfExpression,
|
||||
val condition: KtOperationExpression,
|
||||
/**
|
||||
* Expression checked in [condition].
|
||||
*/
|
||||
val checkedExpression: KtExpression,
|
||||
val baseClause: KtExpression,
|
||||
val negatedClause: KtExpression?,
|
||||
)
|
||||
|
||||
@ApiStatus.Internal
|
||||
enum class TransformIfThenReceiverMode {
|
||||
ADD_EXPLICIT_THIS,
|
||||
|
||||
/**
|
||||
* Indicates that the base clause and checked expression are the same.
|
||||
*/
|
||||
REPLACE_BASE_CLAUSE,
|
||||
|
||||
/**
|
||||
* Indicates that checked expression is a receiver in the base clause, and we need to find it and then replace it.
|
||||
*/
|
||||
FIND_AND_REPLACE_MATCHING_RECEIVER,
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
object IfThenTransformationUtils {
|
||||
@RequiresWriteLock
|
||||
fun transformBaseClause(data: IfThenTransformationData, strategy: IfThenTransformationStrategy): KtExpression {
|
||||
val factory = KtPsiFactory(data.baseClause.project)
|
||||
|
||||
val newReceiverExpression = when (val condition = data.condition) {
|
||||
is KtIsExpression -> {
|
||||
val typeReference = condition.typeReference
|
||||
?: errorWithAttachment("Null type reference in condition") { withPsiEntry("ifExpression", data.ifExpression) }
|
||||
|
||||
factory.createExpressionByPattern("$0 as? $1", condition.leftHandSide, typeReference)
|
||||
}
|
||||
|
||||
else -> data.checkedExpression
|
||||
}
|
||||
|
||||
return when (strategy) {
|
||||
is IfThenTransformationStrategy.WrapWithLet -> data.baseClause.wrapWithLet(
|
||||
newReceiverExpression,
|
||||
expressionsToReplaceWithLambdaParameter = collectTextBasedUsages(data)
|
||||
)
|
||||
|
||||
is IfThenTransformationStrategy.AddSafeAccess -> {
|
||||
// step 1. replace variable calls with explicit invoke calls
|
||||
val newBaseClause = data.baseClause.getLeftMostReceiverExpressionOrThis()
|
||||
// TODO: use `OperatorToFunctionConverter.convert` instead
|
||||
.replaceVariableCallsWithExplicitInvokeCalls(strategy.variableCallsToAddInvokeTo)
|
||||
|
||||
// step 2. add an explicit receiver or replace the existing one
|
||||
val replacedReceiver = when (strategy.transformReceiverMode) {
|
||||
TransformIfThenReceiverMode.ADD_EXPLICIT_THIS -> {
|
||||
val leftMostReceiver = newBaseClause.getLeftMostReceiverExpressionOrThis()
|
||||
val qualified = leftMostReceiver.prependDotQualifiedReceiver(newReceiverExpression, factory)
|
||||
|
||||
(qualified as KtQualifiedExpression).receiverExpression
|
||||
}
|
||||
|
||||
TransformIfThenReceiverMode.REPLACE_BASE_CLAUSE -> newBaseClause.replaced(newReceiverExpression)
|
||||
|
||||
TransformIfThenReceiverMode.FIND_AND_REPLACE_MATCHING_RECEIVER -> {
|
||||
val receiverToReplace = newBaseClause.getMatchingReceiver(data.checkedExpression.text) ?: error("")
|
||||
receiverToReplace.replaced(newReceiverExpression)
|
||||
}
|
||||
}
|
||||
|
||||
// step 3. add safe access after replaced receiver
|
||||
replacedReceiver.insertSafeCallsAfterReceiver()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun buildTransformationData(ifExpression: KtIfExpression): IfThenTransformationData? {
|
||||
val condition = ifExpression.condition?.getSingleUnwrappedStatement() as? KtOperationExpression ?: return null
|
||||
val thenClause = ifExpression.then?.let { it.getSingleUnwrappedStatement() ?: return null }
|
||||
val elseClause = ifExpression.`else`?.let { it.getSingleUnwrappedStatement() ?: return null }
|
||||
val receiverExpression = condition.checkedExpression()?.getSingleUnwrappedStatement() ?: return null
|
||||
|
||||
val (baseClause, negatedClause) = when (condition) {
|
||||
is KtBinaryExpression -> when (condition.operationToken) {
|
||||
KtTokens.EQEQ -> elseClause to thenClause
|
||||
KtTokens.EXCLEQ -> thenClause to elseClause
|
||||
else -> return null
|
||||
}
|
||||
|
||||
is KtIsExpression -> {
|
||||
when (condition.isNegated) {
|
||||
true -> elseClause to thenClause
|
||||
false -> thenClause to elseClause
|
||||
}
|
||||
}
|
||||
|
||||
else -> return null
|
||||
}
|
||||
|
||||
if (baseClause == null) return null
|
||||
|
||||
return IfThenTransformationData(ifExpression, condition, receiverExpression, baseClause, negatedClause)
|
||||
}
|
||||
|
||||
fun KtExpression.checkedExpression(): KtExpression? = when (this) {
|
||||
is KtBinaryExpression -> expressionComparedToNull()
|
||||
is KtIsExpression -> leftHandSide
|
||||
else -> null
|
||||
}
|
||||
|
||||
/**
|
||||
* @return usages of [IfThenTransformationData.checkedExpression] based on its text and `KClass`, excluding usages from nested scopes
|
||||
*/
|
||||
fun collectTextBasedUsages(data: IfThenTransformationData): List<KtExpression> = data.baseClause.collectDescendantsOfType<KtExpression>(
|
||||
canGoInside = { it !is KtBlockExpression },
|
||||
predicate = { it::class == data.checkedExpression::class && it.text == data.checkedExpression.text },
|
||||
)
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
sealed class IfThenTransformationStrategy {
|
||||
abstract fun withWritableData(updater: ModPsiUpdater): IfThenTransformationStrategy
|
||||
|
||||
/**
|
||||
* Returns `true` if the transformation is expected to make code more Kotlin-idiomatic, and so it should be suggested.
|
||||
*/
|
||||
abstract fun shouldSuggestTransformation(): Boolean
|
||||
|
||||
data object WrapWithLet : IfThenTransformationStrategy() {
|
||||
override fun withWritableData(updater: ModPsiUpdater): WrapWithLet = WrapWithLet
|
||||
|
||||
override fun shouldSuggestTransformation(): Boolean = false
|
||||
}
|
||||
|
||||
data class AddSafeAccess(
|
||||
val variableCallsToAddInvokeTo: Set<KtCallExpression>,
|
||||
val transformReceiverMode: TransformIfThenReceiverMode,
|
||||
val newReceiverIsSafeCast: Boolean,
|
||||
) : IfThenTransformationStrategy() {
|
||||
override fun withWritableData(updater: ModPsiUpdater): AddSafeAccess = this.copy(
|
||||
variableCallsToAddInvokeTo.map { updater.getWritable<KtCallExpression>(it) }.toSet()
|
||||
)
|
||||
|
||||
override fun shouldSuggestTransformation(): Boolean {
|
||||
val newReceiverIsSafeCastInParentheses =
|
||||
newReceiverIsSafeCast && transformReceiverMode != TransformIfThenReceiverMode.REPLACE_BASE_CLAUSE
|
||||
|
||||
return variableCallsToAddInvokeTo.isEmpty() && !newReceiverIsSafeCastInParentheses
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
context(KtAnalysisSession)
|
||||
fun create(data: IfThenTransformationData): IfThenTransformationStrategy? {
|
||||
val newReceiverIsSafeCast = data.condition is KtIsExpression
|
||||
|
||||
return if (data.checkedExpression is KtThisExpression && IfThenTransformationUtils.collectTextBasedUsages(data).isEmpty()) {
|
||||
val leftMostReceiver = data.baseClause.getLeftMostReceiverExpressionOrThis()
|
||||
if (!leftMostReceiver.hasImplicitReceiverMatchingThisExpression(data.checkedExpression)) return null
|
||||
|
||||
AddSafeAccess(leftMostReceiver.collectVariableCalls(), TransformIfThenReceiverMode.ADD_EXPLICIT_THIS, newReceiverIsSafeCast)
|
||||
} else {
|
||||
val receiverToReplace = data.baseClause.getMatchingReceiver(data.checkedExpression.text) ?: return WrapWithLet
|
||||
val variableCalls = receiverToReplace.collectVariableCalls()
|
||||
|
||||
val transformReceiverMode = if (variableCalls.isEmpty() && data.baseClause.isSimplifiableTo(data.checkedExpression)) {
|
||||
TransformIfThenReceiverMode.REPLACE_BASE_CLAUSE
|
||||
} else TransformIfThenReceiverMode.FIND_AND_REPLACE_MATCHING_RECEIVER
|
||||
|
||||
AddSafeAccess(variableCalls, transformReceiverMode, newReceiverIsSafeCast)
|
||||
}
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private fun KtExpression.hasImplicitReceiverMatchingThisExpression(thisExpression: KtThisExpression): Boolean {
|
||||
val thisExpressionSymbol = thisExpression.instanceReference.mainReference.resolveToSymbol() ?: return false
|
||||
// we need to resolve callee instead of call, because in case of variable call, call is resolved to `invoke`
|
||||
val callableMemberCall = this.getCalleeExpressionIfAny()?.resolveCallableMemberCall() ?: return false
|
||||
|
||||
return callableMemberCall.getImplicitReceivers().any { it.symbol == thisExpressionSymbol }
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private fun KtExpression.resolveCallableMemberCall(): KtCallableMemberCall<*, *>? = this.resolveCall()?.successfulCallOrNull()
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private fun KtExpression.collectVariableCalls(): Set<KtCallExpression> = this
|
||||
.parentsOfType<KtExpression>(withSelf = true)
|
||||
.mapNotNull { it.getSelectorOrThis() as? KtCallExpression }
|
||||
.filter { it.resolveCall()?.singleCallOrNull<KtSimpleFunctionCall>()?.isImplicitInvoke == true }
|
||||
.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note, that if [IfThenTransformationData.checkedExpression] is used in variable call, variable call will be returned, e.g., for:
|
||||
* ```
|
||||
* if (a is Function0<*>) {
|
||||
* a().hashCode()
|
||||
* } else null
|
||||
* ```
|
||||
* `a()` will be returned.
|
||||
*/
|
||||
private fun KtExpression.getMatchingReceiver(targetText: String): KtExpression? {
|
||||
val leftMostReceiver = this.getLeftMostReceiverExpressionOrThis()
|
||||
|
||||
return leftMostReceiver.parentsOfType<KtExpression>(withSelf = true).firstOrNull { parent ->
|
||||
val valueArgumentList = (parent.getSelectorOrThis() as? KtCallExpression)?.valueArgumentList
|
||||
parent.text.removeSuffix(valueArgumentList?.text.orEmpty()) == targetText
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtExpression.getSelectorOrThis(): KtExpression = (this as? KtQualifiedExpression)?.selectorExpression ?: this
|
||||
+73
@@ -4,10 +4,17 @@ package org.jetbrains.kotlin.idea.codeinsights.impl.base
|
||||
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.idea.base.codeInsight.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.base.psi.appendDotQualifiedSelector
|
||||
import org.jetbrains.kotlin.idea.base.psi.getSingleUnwrappedStatementOrThis
|
||||
import org.jetbrains.kotlin.idea.base.psi.isOneLiner
|
||||
import org.jetbrains.kotlin.idea.base.psi.replaced
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver
|
||||
|
||||
fun KtExpression.isComplexInitializer(): Boolean {
|
||||
fun KtExpression.isElvisExpression(): Boolean = this is KtBinaryExpression && operationToken == KtTokens.ELVIS
|
||||
@@ -33,3 +40,69 @@ fun KtCallableDeclaration.hasUsages(inElements: Collection<KtElement>): Boolean
|
||||
|
||||
fun KtExpression.isExitStatement(): Boolean =
|
||||
this is KtContinueExpression || this is KtBreakExpression || this is KtThrowExpression || this is KtReturnExpression
|
||||
|
||||
fun KtExpression.isSimplifiableTo(other: KtExpression): Boolean = this.getSingleUnwrappedStatementOrThis().text == other.text
|
||||
|
||||
fun KtExpression.wrapWithLet(
|
||||
receiverExpression: KtExpression,
|
||||
expressionsToReplaceWithLambdaParameter: List<KtExpression>
|
||||
): KtExpression {
|
||||
val factory = KtPsiFactory(project)
|
||||
|
||||
val implicitParameterName = StandardNames.IMPLICIT_LAMBDA_PARAMETER_NAME.identifier
|
||||
val lambdaParameterName = KotlinNameSuggester.suggestNameByName(implicitParameterName) { candidate ->
|
||||
collectDescendantsOfType<KtNameReferenceExpression> { it.text == candidate }.isEmpty()
|
||||
}
|
||||
|
||||
for (expressionToReplace in expressionsToReplaceWithLambdaParameter) {
|
||||
expressionToReplace.replace(factory.createExpression(lambdaParameterName))
|
||||
}
|
||||
val lambdaParameterPattern = if (lambdaParameterName != implicitParameterName) "$lambdaParameterName -> " else ""
|
||||
|
||||
return factory.createExpressionByPattern("$0?.let { $lambdaParameterPattern$1 }", receiverExpression, this)
|
||||
}
|
||||
|
||||
tailrec fun KtExpression.insertSafeCallsAfterReceiver(): KtExpression {
|
||||
return when (val qualified = this.getQualifiedExpressionForReceiver()) {
|
||||
is KtDotQualifiedExpression -> {
|
||||
val factory = KtPsiFactory(project)
|
||||
val selector = qualified.selectorExpression ?: return this
|
||||
val newQualified = factory.createExpressionByPattern("$0?.$1", qualified.receiverExpression, selector)
|
||||
|
||||
qualified.replaced(newQualified).insertSafeCallsAfterReceiver()
|
||||
}
|
||||
|
||||
is KtSafeQualifiedExpression -> qualified.insertSafeCallsAfterReceiver()
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces calls present in [variableCalls] with variable access + `invoke()`, starting with selector of [this] and continuing with calls
|
||||
* that follow [this].
|
||||
* E.g., if `foo().bar()` from `foo().bar().baz()` is provided, with all calls being variable calls,
|
||||
* then the selector `bar()` and the following call `baz()` are replaced, resulting in `foo().bar.invoke().baz.invoke()`.
|
||||
*/
|
||||
// TODO: remove this function and replace its usages with `OperatorToFunctionConverter.convert`
|
||||
tailrec fun KtExpression.replaceVariableCallsWithExplicitInvokeCalls(variableCalls: Set<KtCallExpression>): KtExpression {
|
||||
val factory = KtPsiFactory(project)
|
||||
|
||||
val callExpression = when (this) {
|
||||
is KtCallExpression -> this
|
||||
is KtQualifiedExpression -> selectorExpression as? KtCallExpression
|
||||
else -> null
|
||||
}
|
||||
val valueArgumentList = callExpression?.valueArgumentList
|
||||
|
||||
val newExpression = if (callExpression in variableCalls && valueArgumentList != null) {
|
||||
val newInvokeCall = factory.createExpressionByPattern("invoke$0", valueArgumentList.text)
|
||||
|
||||
valueArgumentList.delete()
|
||||
|
||||
this.appendDotQualifiedSelector(selector = newInvokeCall, factory)
|
||||
} else this
|
||||
|
||||
val qualified = newExpression.getQualifiedExpressionForReceiver() ?: return newExpression
|
||||
|
||||
return qualified.replaceVariableCallsWithExplicitInvokeCalls(variableCalls)
|
||||
}
|
||||
+13
@@ -6,6 +6,7 @@ import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import com.intellij.psi.util.endOffset
|
||||
import com.intellij.psi.util.startOffset
|
||||
import org.jetbrains.kotlin.idea.base.psi.textRangeIn
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicators.ApplicabilityRange
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -70,4 +71,16 @@ object ApplicabilityRanges {
|
||||
|
||||
fun declarationName(element: KtNamedDeclaration) =
|
||||
ApplicabilityRange.single(element) { it.nameIdentifier }
|
||||
|
||||
fun ifExpressionExcludingBranches(element: KtIfExpression): List<TextRange> {
|
||||
val rightOffset = element.rightParenthesis?.endOffset
|
||||
|
||||
val textRange = if (rightOffset == null) {
|
||||
element.ifKeyword.textRangeIn(element)
|
||||
} else {
|
||||
TextRange(element.ifKeyword.startOffset, rightOffset).shiftLeft(element.startOffset)
|
||||
}
|
||||
|
||||
return listOf(textRange)
|
||||
}
|
||||
}
|
||||
+7
@@ -320,6 +320,13 @@
|
||||
level="INFORMATION" key="redundant.else" bundle="messages.KotlinBundle"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.k2.codeinsight.inspections.JoinDeclarationAndAssignmentInspection"
|
||||
language="kotlin"
|
||||
groupPath="Kotlin"
|
||||
groupBundle="messages.KotlinBundle" groupKey="group.names.style.issues"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING" key="inspection.if.then.to.safe.access.display.name" bundle="messages.KotlinBundle"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.k2.codeinsight.inspections.IfThenToSafeAccessInspection"
|
||||
language="kotlin"
|
||||
groupPath="Kotlin"
|
||||
groupBundle="messages.KotlinBundle" groupKey="group.names.redundant.constructs"
|
||||
|
||||
+2
-14
@@ -9,19 +9,17 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.base.psi.isMultiLine
|
||||
import org.jetbrains.kotlin.idea.base.psi.textRangeIn
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.codeInsight.FoldInitializerAndIfExpressionData
|
||||
import org.jetbrains.kotlin.idea.codeInsight.joinLines
|
||||
import org.jetbrains.kotlin.idea.codeInsight.prepareData
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinApplicableInspectionBase
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinModCommandQuickFix
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.applicators.ApplicabilityRanges
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
internal class FoldInitializerAndIfToElvisInspection :
|
||||
@@ -79,17 +77,7 @@ internal class FoldInitializerAndIfToElvisInspection :
|
||||
}
|
||||
}
|
||||
|
||||
override fun getApplicableRanges(element: KtIfExpression): List<TextRange> {
|
||||
val rightOffset = element.rightParenthesis?.endOffset
|
||||
|
||||
val textRange = if (rightOffset == null) {
|
||||
element.ifKeyword.textRangeIn(element)
|
||||
} else {
|
||||
TextRange(element.ifKeyword.startOffset, rightOffset).shiftLeft(element.startOffset)
|
||||
}
|
||||
|
||||
return listOf(textRange)
|
||||
}
|
||||
override fun getApplicableRanges(element: KtIfExpression): List<TextRange> = ApplicabilityRanges.ifExpressionExcludingBranches(element)
|
||||
|
||||
override fun isApplicableByPsi(element: KtIfExpression): Boolean {
|
||||
fun KtExpression.isElvisExpression(): Boolean = this is KtBinaryExpression && operationToken == KtTokens.ELVIS
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// Copyright 2000-2024 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.inspections
|
||||
|
||||
import com.intellij.codeInspection.CleanupLocalInspectionTool
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.codeInspection.util.InspectionMessage
|
||||
import com.intellij.codeInspection.util.IntentionFamilyName
|
||||
import com.intellij.codeInspection.util.IntentionName
|
||||
import com.intellij.modcommand.ModPsiUpdater
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.components.KtDiagnosticCheckerFilter
|
||||
import org.jetbrains.kotlin.analysis.api.fir.diagnostics.KtFirDiagnostic
|
||||
import org.jetbrains.kotlin.idea.base.psi.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.codeInsight.IfThenTransformationUtils
|
||||
import org.jetbrains.kotlin.idea.codeInsight.IfThenTransformationData
|
||||
import org.jetbrains.kotlin.idea.codeInsight.IfThenTransformationStrategy
|
||||
import org.jetbrains.kotlin.idea.codeInsight.TransformIfThenReceiverMode
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinApplicableInspectionBase
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinModCommandQuickFix
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.applicators.ApplicabilityRanges
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
internal class IfThenToSafeAccessInspection :
|
||||
KotlinApplicableInspectionBase.Simple<KtIfExpression, IfThenTransformationStrategy>(),
|
||||
CleanupLocalInspectionTool {
|
||||
|
||||
override fun getProblemDescription(element: KtIfExpression, context: IfThenTransformationStrategy): @InspectionMessage String =
|
||||
KotlinBundle.message("foldable.if.then")
|
||||
|
||||
override fun getProblemHighlightType(element: KtIfExpression, context: IfThenTransformationStrategy): ProblemHighlightType {
|
||||
if (!context.shouldSuggestTransformation()) return ProblemHighlightType.INFORMATION
|
||||
|
||||
return super.getProblemHighlightType(element, context)
|
||||
}
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): KtVisitor<*, *> = object : KtVisitorVoid() {
|
||||
override fun visitIfExpression(expression: KtIfExpression) {
|
||||
visitTargetElement(expression, holder, isOnTheFly)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getApplicableRanges(element: KtIfExpression): List<TextRange> = ApplicabilityRanges.ifExpressionExcludingBranches(element)
|
||||
|
||||
override fun isApplicableByPsi(element: KtIfExpression): Boolean {
|
||||
val data = IfThenTransformationUtils.buildTransformationData(element) ?: return false
|
||||
|
||||
// negated clause is present, but it does not evaluate to `null`
|
||||
if (data.negatedClause?.isNullExpression() == false) return false
|
||||
|
||||
val condition = data.condition
|
||||
if (condition is KtIsExpression && condition.typeReference == null) return false
|
||||
|
||||
// there are no usages of expression, except possibly at nested levels, which are currently not supported
|
||||
if (data.checkedExpression !is KtThisExpression && IfThenTransformationUtils.collectTextBasedUsages(data).isEmpty()) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
override fun prepareContext(element: KtIfExpression): IfThenTransformationStrategy? {
|
||||
val data = IfThenTransformationUtils.buildTransformationData(element) as IfThenTransformationData
|
||||
|
||||
if (data.negatedClause == null && data.baseClause.isUsedAsExpression()) return null
|
||||
|
||||
// every usage is expected to have smart cast info;
|
||||
// if smart cast is unstable, replacing usage with `it` can break code logic
|
||||
if (IfThenTransformationUtils.collectTextBasedUsages(data).any { it.doesNotHaveStableSmartCast() }) return null
|
||||
|
||||
if (conditionIsSenseless(data)) return null
|
||||
|
||||
return IfThenTransformationStrategy.create(data)
|
||||
}
|
||||
|
||||
override fun createQuickFix(
|
||||
element: KtIfExpression,
|
||||
context: IfThenTransformationStrategy,
|
||||
): KotlinModCommandQuickFix<KtIfExpression> = object : KotlinModCommandQuickFix<KtIfExpression>() {
|
||||
override fun getFamilyName(): @IntentionFamilyName String = KotlinBundle.message("simplify.foldable.if.then")
|
||||
|
||||
override fun getName(): @IntentionName String {
|
||||
val transformReceiverMode = (context as? IfThenTransformationStrategy.AddSafeAccess)?.transformReceiverMode
|
||||
|
||||
return if (transformReceiverMode == TransformIfThenReceiverMode.REPLACE_BASE_CLAUSE) {
|
||||
if (context.newReceiverIsSafeCast) {
|
||||
KotlinBundle.message("replace.if.expression.with.safe.cast.expression")
|
||||
} else {
|
||||
KotlinBundle.message("remove.redundant.if.expression")
|
||||
}
|
||||
} else {
|
||||
KotlinBundle.message("replace.if.expression.with.safe.access.expression")
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyFix(project: Project, element: KtIfExpression, updater: ModPsiUpdater) {
|
||||
val data = IfThenTransformationUtils.buildTransformationData(element) as IfThenTransformationData
|
||||
val transformedBaseClause = IfThenTransformationUtils.transformBaseClause(data, context.withWritableData(updater))
|
||||
|
||||
element.replace(transformedBaseClause)
|
||||
}
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private fun KtExpression.doesNotHaveStableSmartCast(): Boolean {
|
||||
val expressionToCheck = when (this) {
|
||||
is KtThisExpression -> instanceReference
|
||||
else -> this
|
||||
}
|
||||
return expressionToCheck.getSmartCastInfo()?.isStable != true
|
||||
}
|
||||
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private fun conditionIsSenseless(data: IfThenTransformationData): Boolean = data.condition
|
||||
.getDiagnostics(KtDiagnosticCheckerFilter.ONLY_COMMON_CHECKERS)
|
||||
.map { it.diagnosticClass }
|
||||
.any { it == KtFirDiagnostic.SenselessComparison::class || it == KtFirDiagnostic.UselessIsCheck::class }
|
||||
}
|
||||
+453
@@ -488,6 +488,459 @@ public abstract class K2LocalInspectionTestGenerated extends AbstractK2LocalInsp
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess")
|
||||
public static class IfThenToSafeAccess extends AbstractK2LocalInspectionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("argumentInCallWithReceiver.kt")
|
||||
public void testArgumentInCallWithReceiver() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/argumentInCallWithReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("blockHasMoreThanOneStatement.kt")
|
||||
public void testBlockHasMoreThanOneStatement() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/blockHasMoreThanOneStatement.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("blockUsesDifferentVar.kt")
|
||||
public void testBlockUsesDifferentVar() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/blockUsesDifferentVar.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("call.kt")
|
||||
public void testCall() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/call.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("call2.kt")
|
||||
public void testCall2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/call2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("call3.kt")
|
||||
public void testCall3() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/call3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("call4.kt")
|
||||
public void testCall4() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/call4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callChainWithImplicitReceiver.kt")
|
||||
public void testCallChainWithImplicitReceiver() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/callChainWithImplicitReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callChainWithVariableCalls.kt")
|
||||
public void testCallChainWithVariableCalls() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/callChainWithVariableCalls.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callInvokeOperator.kt")
|
||||
public void testCallInvokeOperator() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/callInvokeOperator.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callInvokeOperator2.kt")
|
||||
public void testCallInvokeOperator2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/callInvokeOperator2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callInvokeOperator3.kt")
|
||||
public void testCallInvokeOperator3() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/callInvokeOperator3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callInvokeOperator4.kt")
|
||||
public void testCallInvokeOperator4() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/callInvokeOperator4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callVariable.kt")
|
||||
public void testCallVariable() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/callVariable.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callVariable2.kt")
|
||||
public void testCallVariable2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/callVariable2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callWithNamedArgument.kt")
|
||||
public void testCallWithNamedArgument() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/callWithNamedArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("conditionComparesNullWithNull.kt")
|
||||
public void testConditionComparesNullWithNull() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/conditionComparesNullWithNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("conditionInParentheses.kt")
|
||||
public void testConditionInParentheses() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/conditionInParentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("conditionInvalidBinaryExp.kt")
|
||||
public void testConditionInvalidBinaryExp() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/conditionInvalidBinaryExp.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("conditionNotBinaryExpr.kt")
|
||||
public void testConditionNotBinaryExpr() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/conditionNotBinaryExpr.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("doesNotinlineValueIfUsedMoreThanOnce.kt")
|
||||
public void testDoesNotinlineValueIfUsedMoreThanOnce() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/doesNotinlineValueIfUsedMoreThanOnce.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("doesNotinlineValueOutsideOfScope.kt")
|
||||
public void testDoesNotinlineValueOutsideOfScope() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/doesNotinlineValueOutsideOfScope.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("emptyCondition.kt")
|
||||
public void testEmptyCondition() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/emptyCondition.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("emptyElseBlock.kt")
|
||||
public void testEmptyElseBlock() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/emptyElseBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("emptyThenBlock.kt")
|
||||
public void testEmptyThenBlock() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/emptyThenBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifAndElseBothInBlocks.kt")
|
||||
public void testIfAndElseBothInBlocks() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/ifAndElseBothInBlocks.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifAndElseBothInBlocks2.kt")
|
||||
public void testIfAndElseBothInBlocks2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/ifAndElseBothInBlocks2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifAndElseNotInBlocks.kt")
|
||||
public void testIfAndElseNotInBlocks() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/ifAndElseNotInBlocks.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifAndElseNotInBlocks2.kt")
|
||||
public void testIfAndElseNotInBlocks2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/ifAndElseNotInBlocks2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifAsExpression.kt")
|
||||
public void testIfAsExpression() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/ifAsExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitReceiver.kt")
|
||||
public void testImplicitReceiver() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/implicitReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitReceiver2.kt")
|
||||
public void testImplicitReceiver2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/implicitReceiver2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitReceiver3.kt")
|
||||
public void testImplicitReceiver3() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/implicitReceiver3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitReceiverInApply.kt")
|
||||
public void testImplicitReceiverInApply() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/implicitReceiverInApply.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitReceiverIsNotLeftMostReceiver.kt")
|
||||
public void testImplicitReceiverIsNotLeftMostReceiver() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/implicitReceiverIsNotLeftMostReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitReceiverVariableCall.kt")
|
||||
public void testImplicitReceiverVariableCall() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/implicitReceiverVariableCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isCheckSimple.kt")
|
||||
public void testIsCheckSimple() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/isCheckSimple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isCheckWithSelectorChain.kt")
|
||||
public void testIsCheckWithSelectorChain() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/isCheckWithSelectorChain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isCondition.kt")
|
||||
public void testIsCondition() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/isCondition.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isNotCondition.kt")
|
||||
public void testIsNotCondition() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/isNotCondition.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isNotNullable.kt")
|
||||
public void testIsNotNullable() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/isNotNullable.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isNullable.kt")
|
||||
public void testIsNullable() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/isNullable.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lhsEqualsNull.kt")
|
||||
public void testLhsEqualsNull() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/lhsEqualsNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lhsNotEqualsNull.kt")
|
||||
public void testLhsNotEqualsNull() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/lhsNotEqualsNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lhsNotEqualsNull2.kt")
|
||||
public void testLhsNotEqualsNull2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/lhsNotEqualsNull2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localVar.kt")
|
||||
public void testLocalVar() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/localVar.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("missingNecessaryElseClause.kt")
|
||||
public void testMissingNecessaryElseClause() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/missingNecessaryElseClause.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("missingNecessaryThenClause.kt")
|
||||
public void testMissingNecessaryThenClause() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/missingNecessaryThenClause.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multipleUsagesNullCheck.kt")
|
||||
public void testMultipleUsagesNullCheck() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/multipleUsagesNullCheck.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multipleUsagesThis.kt")
|
||||
public void testMultipleUsagesThis() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/multipleUsagesThis.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multipleUsagesTypeCheck.kt")
|
||||
public void testMultipleUsagesTypeCheck() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/multipleUsagesTypeCheck.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multipleUsagesVariableCalls.kt")
|
||||
public void testMultipleUsagesVariableCalls() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/multipleUsagesVariableCalls.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noCondition.kt")
|
||||
public void testNoCondition() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/noCondition.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noElseBlock.kt")
|
||||
public void testNoElseBlock() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/noElseBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noElseBlock2.kt")
|
||||
public void testNoElseBlock2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/noElseBlock2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noElseBlockAsExpression.kt")
|
||||
public void testNoElseBlockAsExpression() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/noElseBlockAsExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noNullInCondition.kt")
|
||||
public void testNoNullInCondition() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/noNullInCondition.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noNullInCondition2.kt")
|
||||
public void testNoNullInCondition2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/noNullInCondition2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noStdResultInCallChain.kt")
|
||||
public void testNoStdResultInCallChain() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/noStdResultInCallChain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noThenBlock.kt")
|
||||
public void testNoThenBlock() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/noThenBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noThenBlock2.kt")
|
||||
public void testNoThenBlock2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/noThenBlock2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notApplicableForFunction.kt")
|
||||
public void testNotApplicableForFunction() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/notApplicableForFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notApplicableForNonLocalVar.kt")
|
||||
public void testNotApplicableForNonLocalVar() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/notApplicableForNonLocalVar.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notApplicableResultInCallChain.kt")
|
||||
public void testNotApplicableResultInCallChain() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/notApplicableResultInCallChain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nullCheckSimple.kt")
|
||||
public void testNullCheckSimple() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/nullCheckSimple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nullCheckWithSelectorCallChain.kt")
|
||||
public void testNullCheckWithSelectorCallChain() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/nullCheckWithSelectorCallChain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("otherBlockHasMoreThanOneStatement.kt")
|
||||
public void testOtherBlockHasMoreThanOneStatement() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/otherBlockHasMoreThanOneStatement.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("property.kt")
|
||||
public void testProperty() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/property.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyNotNull.kt")
|
||||
public void testPropertyNotNull() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/propertyNotNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyVariableCall.kt")
|
||||
public void testPropertyVariableCall() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/propertyVariableCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyWithProperty.kt")
|
||||
public void testPropertyWithProperty() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/propertyWithProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("replaceWithLetInMember.kt")
|
||||
public void testReplaceWithLetInMember() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/replaceWithLetInMember.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("replaceWithLetInMemberWithIs.kt")
|
||||
public void testReplaceWithLetInMemberWithIs() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/replaceWithLetInMemberWithIs.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("resultCall.kt")
|
||||
public void testResultCall() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/resultCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("rhsEqualsNull.kt")
|
||||
public void testRhsEqualsNull() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/rhsEqualsNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("rhsEqualsNull2.kt")
|
||||
public void testRhsEqualsNull2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/rhsEqualsNull2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("rhsNotEqualsNull.kt")
|
||||
public void testRhsNotEqualsNull() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/rhsNotEqualsNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("rhsNotEqualsNull2.kt")
|
||||
public void testRhsNotEqualsNull2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/rhsNotEqualsNull2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("senselessComparison.kt")
|
||||
public void testSenselessComparison() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/senselessComparison.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("thenAndElseBothNull.kt")
|
||||
public void testThenAndElseBothNull() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/thenAndElseBothNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("thenAndElseNotNull.kt")
|
||||
public void testThenAndElseNotNull() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/thenAndElseNotNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unacceptableEmptyElseBlock.kt")
|
||||
public void testUnacceptableEmptyElseBlock() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/unacceptableEmptyElseBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unacceptableEmptyThenBlock.kt")
|
||||
public void testUnacceptableEmptyThenBlock() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/unacceptableEmptyThenBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unacceptableNoElseBlock.kt")
|
||||
public void testUnacceptableNoElseBlock() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/unacceptableNoElseBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unacceptableNoThenBlock.kt")
|
||||
public void testUnacceptableNoThenBlock() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/unacceptableNoThenBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usageInBlock.kt")
|
||||
public void testUsageInBlock() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/usageInBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usageInBlock2.kt")
|
||||
public void testUsageInBlock2() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/usageInBlock2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("uselessIsCheck.kt")
|
||||
public void testUselessIsCheck() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/uselessIsCheck.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("willNotInlineClassProperty.kt")
|
||||
public void testWillNotInlineClassProperty() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/branched/ifThenToSafeAccess/willNotInlineClassProperty.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../../../idea/tests/testData/inspectionsLocal/conventionNameCalls/replaceGetOrSet")
|
||||
public static class ReplaceGetOrSet extends AbstractK2LocalInspectionTest {
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.base.psi.replaced
|
||||
import org.jetbrains.kotlin.idea.base.psi.unwrapBlockOrParenthesis
|
||||
import org.jetbrains.kotlin.idea.base.psi.getSingleUnwrappedStatementOrThis
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.base.util.reformat
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.intentions.KotlinApplicableModCommandAction
|
||||
@@ -205,7 +205,7 @@ internal class IfToWhenIntention : KotlinApplicableModCommandAction<KtIfExpressi
|
||||
|
||||
private fun BuilderByPattern<*>.appendElseBlock(block: KtExpression?, unwrapBlockOrParenthesis: Boolean = false) {
|
||||
appendFixedText("else->")
|
||||
appendExpression(if (unwrapBlockOrParenthesis) block?.unwrapBlockOrParenthesis() else block)
|
||||
appendExpression(if (unwrapBlockOrParenthesis) block?.getSingleUnwrappedStatementOrThis() else block)
|
||||
appendFixedText("\n")
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -204,8 +204,11 @@ private fun getNegatedOperatorText(token: IElementType): String {
|
||||
return negatedOperator.value
|
||||
}
|
||||
|
||||
fun KtDotQualifiedExpression.getLeftMostReceiverExpression(): KtExpression =
|
||||
(receiverExpression as? KtDotQualifiedExpression)?.getLeftMostReceiverExpression() ?: receiverExpression
|
||||
fun KtQualifiedExpression.getLeftMostReceiverExpression(): KtExpression =
|
||||
(receiverExpression as? KtQualifiedExpression)?.getLeftMostReceiverExpression() ?: receiverExpression
|
||||
|
||||
fun KtExpression.getLeftMostReceiverExpressionOrThis(): KtExpression =
|
||||
(this as? KtQualifiedExpression)?.getLeftMostReceiverExpression() ?: this
|
||||
|
||||
fun KtDotQualifiedExpression.replaceFirstReceiver(
|
||||
factory: KtPsiFactory,
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
import com.intellij.psi.createSmartPointer
|
||||
import org.jetbrains.kotlin.idea.base.psi.getOrCreateCompanionObject
|
||||
import org.jetbrains.kotlin.idea.base.psi.unwrapBlockOrParenthesis
|
||||
import org.jetbrains.kotlin.idea.base.psi.getSingleUnwrappedStatementOrThis
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -181,7 +181,7 @@ class ParcelMigrateToParcelizeQuickFixApplicator<CONTEXT>(
|
||||
?.toList() ?: emptyList()
|
||||
|
||||
val describeContentsFunctionToRemove = parcelableClass.findDescribeContentsOverride()?.takeIf {
|
||||
val returnExpr = it.bodyExpression?.unwrapBlockOrParenthesis()
|
||||
val returnExpr = it.bodyExpression?.getSingleUnwrappedStatementOrThis()
|
||||
return@takeIf (
|
||||
returnExpr is KtReturnExpression
|
||||
&& returnExpr.getTargetLabel() == null
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.idea.base.psi.replaced
|
||||
import org.jetbrains.kotlin.idea.base.psi.unwrapBlockOrParenthesis
|
||||
import org.jetbrains.kotlin.idea.base.psi.getSingleUnwrappedStatementOrThis
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
|
||||
@@ -66,7 +66,7 @@ private class SimplifyFix(
|
||||
|
||||
override fun applyFix(ifExpression: KtIfExpression) {
|
||||
val branch = ifExpression.branch(conditionValue)?.let {
|
||||
if (keepBraces) it else it.unwrapBlockOrParenthesis()
|
||||
if (keepBraces) it else it.getSingleUnwrappedStatementOrThis()
|
||||
} ?: return
|
||||
ifExpression.replaceWithBranch(branch, isUsedAsExpression, keepBraces)
|
||||
}
|
||||
|
||||
+3
-3
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeAsReplacement
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractApplicabilityBasedInspection
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.isSimplifiableTo
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
@@ -95,7 +95,7 @@ class ConvertTwoComparisonsToRangeCheckInspection :
|
||||
firstLess: KtExpression, firstGreater: KtExpression, firstStrict: Boolean,
|
||||
secondLess: KtExpression, secondGreater: KtExpression, secondStrict: Boolean
|
||||
) = when {
|
||||
firstGreater !is KtConstantExpression && firstGreater.evaluatesTo(secondLess) ->
|
||||
firstGreater !is KtConstantExpression && firstGreater.isSimplifiableTo(secondLess) ->
|
||||
generateRangeExpressionData(
|
||||
firstGreater,
|
||||
min = firstLess,
|
||||
@@ -103,7 +103,7 @@ class ConvertTwoComparisonsToRangeCheckInspection :
|
||||
incrementMinByOne = firstStrict,
|
||||
decrementMaxByOne = secondStrict
|
||||
)
|
||||
firstLess !is KtConstantExpression && firstLess.evaluatesTo(secondGreater) ->
|
||||
firstLess !is KtConstantExpression && firstLess.isSimplifiableTo(secondGreater) ->
|
||||
generateRangeExpressionData(
|
||||
firstLess,
|
||||
min = secondLess,
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import com.intellij.codeInspection.LocalInspectionToolSession
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.base.psi.isNullExpression
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
|
||||
+22
-15
@@ -9,17 +9,22 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.base.psi.getSingleUnwrappedStatementOrThis
|
||||
import org.jetbrains.kotlin.idea.base.psi.replaced
|
||||
import org.jetbrains.kotlin.idea.base.psi.unwrapBlockOrParenthesis
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.safeAnalyzeNonSourceRootCode
|
||||
import org.jetbrains.kotlin.idea.codeInsight.IfThenTransformationUtils
|
||||
import org.jetbrains.kotlin.idea.codeInsight.IfThenTransformationData
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractApplicabilityBasedInspection
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.isSimplifiableTo
|
||||
import org.jetbrains.kotlin.idea.formatter.rightMarginOrDefault
|
||||
import org.jetbrains.kotlin.idea.inspections.branchedTransformations.IfThenToSafeAccessInspection.Util.renameLetParameter
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.*
|
||||
import org.jetbrains.kotlin.idea.util.CommentSaver
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteActionIfPhysical
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getType
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
@@ -54,14 +59,15 @@ class IfThenToElvisInspection @JvmOverloads constructor(
|
||||
val INTENTION_TEXT get() = KotlinBundle.message("replace.if.expression.with.elvis.expression")
|
||||
|
||||
fun convert(element: KtIfExpression, editor: Editor?, inlineWithPrompt: Boolean) {
|
||||
val ifThenToSelectData = element.buildSelectTransformationData() ?: return
|
||||
val ifThenToSelectData = IfThenTransformationUtils.buildTransformationData(element) ?: return
|
||||
val context = element.safeAnalyzeNonSourceRootCode()
|
||||
|
||||
val psiFactory = KtPsiFactory(element.project)
|
||||
|
||||
val commentSaver = CommentSaver(element, saveLineBreaks = false)
|
||||
val margin = element.containingKtFile.rightMarginOrDefault
|
||||
val elvis = runWriteActionIfPhysical(element) {
|
||||
val replacedBaseClause = ifThenToSelectData.replacedBaseClause(psiFactory)
|
||||
val replacedBaseClause = ifThenToSelectData.replacedBaseClause(psiFactory, context)
|
||||
val negatedClause = ifThenToSelectData.negatedClause!!
|
||||
val newExpr = element.replaced(
|
||||
psiFactory.createExpressionByPattern(
|
||||
@@ -83,35 +89,36 @@ class IfThenToElvisInspection @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
fun isApplicableTo(element: KtIfExpression, expressionShouldBeStable: Boolean): Boolean {
|
||||
val ifThenToSelectData = element.buildSelectTransformationData() ?: return false
|
||||
val ifThenToSelectData = IfThenTransformationUtils.buildTransformationData(element) ?: return false
|
||||
val context = element.safeAnalyzeNonSourceRootCode()
|
||||
if (expressionShouldBeStable &&
|
||||
!ifThenToSelectData.receiverExpression.isStableSimpleExpression(ifThenToSelectData.context)
|
||||
!ifThenToSelectData.checkedExpression.isStableSimpleExpression(context)
|
||||
) return false
|
||||
|
||||
val type = element.getType(ifThenToSelectData.context) ?: return false
|
||||
val type = element.getType(context) ?: return false
|
||||
if (KotlinBuiltIns.isUnit(type)) return false
|
||||
|
||||
return ifThenToSelectData.clausesReplaceableByElvis()
|
||||
return ifThenToSelectData.clausesReplaceableByElvis(context)
|
||||
}
|
||||
|
||||
private fun KtExpression.isNullOrBlockExpression(): Boolean {
|
||||
val innerExpression = this.unwrapBlockOrParenthesis()
|
||||
val innerExpression = this.getSingleUnwrappedStatementOrThis()
|
||||
return innerExpression is KtBlockExpression || innerExpression.node.elementType == KtNodeTypes.NULL
|
||||
}
|
||||
|
||||
private fun IfThenToSelectData.clausesReplaceableByElvis(): Boolean =
|
||||
private fun IfThenTransformationData.clausesReplaceableByElvis(context: BindingContext): Boolean =
|
||||
when {
|
||||
negatedClause == null || negatedClause.isNullOrBlockExpression() ->
|
||||
negatedClause == null || negatedClause?.isNullOrBlockExpression() == true ->
|
||||
false
|
||||
negatedClause is KtThrowExpression && negatedClause.throwsNullPointerExceptionWithNoArguments() ->
|
||||
(negatedClause as? KtThrowExpression)?.throwsNullPointerExceptionWithNoArguments() == true ->
|
||||
false
|
||||
conditionHasIncompatibleTypes() ->
|
||||
conditionHasIncompatibleTypes(context) ->
|
||||
false
|
||||
baseClause.evaluatesTo(receiverExpression) ->
|
||||
baseClause.isSimplifiableTo(checkedExpression) ->
|
||||
true
|
||||
baseClause.anyArgumentEvaluatesTo(receiverExpression) ->
|
||||
baseClause.anyArgumentEvaluatesTo(checkedExpression) ->
|
||||
true
|
||||
hasImplicitReceiverReplaceableBySafeCall() || baseClause.hasFirstReceiverOf(receiverExpression) ->
|
||||
hasImplicitReceiverReplaceableBySafeCall(context) || baseClause.hasFirstReceiverOf(checkedExpression) ->
|
||||
!baseClause.hasNullableType(context)
|
||||
else ->
|
||||
false
|
||||
|
||||
+20
-15
@@ -11,10 +11,13 @@ import org.jetbrains.annotations.Nls
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.base.psi.replaced
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.safeAnalyzeNonSourceRootCode
|
||||
import org.jetbrains.kotlin.idea.codeInsight.IfThenTransformationUtils
|
||||
import org.jetbrains.kotlin.idea.codeInsight.IfThenTransformationData
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractApplicabilityBasedInspection
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.isSimplifiableTo
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.*
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinVariableInplaceRenameHandler
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -49,8 +52,8 @@ class IfThenToSafeAccessInspection @JvmOverloads constructor(private val inlineW
|
||||
object Util {
|
||||
@Nls
|
||||
fun fixTextFor(element: KtIfExpression): String {
|
||||
val ifThenToSelectData = element.buildSelectTransformationData()
|
||||
return if (ifThenToSelectData?.let { it.baseClause.evaluatesTo(it.receiverExpression) } == true) {
|
||||
val ifThenToSelectData = IfThenTransformationUtils.buildTransformationData(element)
|
||||
return if (ifThenToSelectData?.let { it.baseClause.isSimplifiableTo(it.checkedExpression) } == true) {
|
||||
if (ifThenToSelectData.condition is KtIsExpression) {
|
||||
KotlinBundle.message("replace.if.expression.with.safe.cast.expression")
|
||||
} else {
|
||||
@@ -62,11 +65,12 @@ class IfThenToSafeAccessInspection @JvmOverloads constructor(private val inlineW
|
||||
}
|
||||
|
||||
fun convert(ifExpression: KtIfExpression, editor: Editor?, inlineWithPrompt: Boolean) {
|
||||
val ifThenToSelectData = ifExpression.buildSelectTransformationData() ?: return
|
||||
val ifThenToSelectData = IfThenTransformationUtils.buildTransformationData(ifExpression) ?: return
|
||||
val context = ifExpression.safeAnalyzeNonSourceRootCode()
|
||||
|
||||
val psiFactory = KtPsiFactory(ifExpression.project)
|
||||
val resultExpr = runWriteAction {
|
||||
val replacedBaseClause = ifThenToSelectData.replacedBaseClause(psiFactory)
|
||||
val replacedBaseClause = ifThenToSelectData.replacedBaseClause(psiFactory, context)
|
||||
val newExpr = ifExpression.replaced(replacedBaseClause)
|
||||
KtPsiUtil.deparenthesize(newExpr)
|
||||
}
|
||||
@@ -78,12 +82,13 @@ class IfThenToSafeAccessInspection @JvmOverloads constructor(private val inlineW
|
||||
}
|
||||
|
||||
fun isApplicableTo(element: KtIfExpression, expressionShouldBeStable: Boolean): Boolean {
|
||||
val ifThenToSelectData = element.buildSelectTransformationData() ?: return false
|
||||
val ifThenToSelectData = IfThenTransformationUtils.buildTransformationData(element) ?: return false
|
||||
val context = element.safeAnalyzeNonSourceRootCode()
|
||||
if (expressionShouldBeStable &&
|
||||
!ifThenToSelectData.receiverExpression.isStableSimpleExpression(ifThenToSelectData.context)
|
||||
!ifThenToSelectData.checkedExpression.isStableSimpleExpression(context)
|
||||
) return false
|
||||
|
||||
return ifThenToSelectData.clausesReplaceableBySafeCall()
|
||||
return ifThenToSelectData.clausesReplaceableBySafeCall(context)
|
||||
}
|
||||
|
||||
internal fun KtSafeQualifiedExpression.renameLetParameter(editor: Editor) {
|
||||
@@ -97,18 +102,18 @@ class IfThenToSafeAccessInspection @JvmOverloads constructor(private val inlineW
|
||||
}
|
||||
}
|
||||
|
||||
private fun IfThenToSelectData.clausesReplaceableBySafeCall(): Boolean = when {
|
||||
conditionHasIncompatibleTypes() -> false
|
||||
private fun IfThenTransformationData.clausesReplaceableBySafeCall(context: BindingContext): Boolean = when {
|
||||
conditionHasIncompatibleTypes(context) -> false
|
||||
negatedClause == null && baseClause.isUsedAsExpression(context) -> false
|
||||
negatedClause != null && !negatedClause.isNullExpression() -> false
|
||||
context.diagnostics.forElement(condition)
|
||||
.any { it.factory == Errors.SENSELESS_COMPARISON || it.factory == Errors.USELESS_IS_CHECK } -> false
|
||||
baseClause.evaluatesTo(receiverExpression) -> true
|
||||
(baseClause as? KtCallExpression)?.calleeExpression?.evaluatesTo(receiverExpression) == true
|
||||
baseClause.isSimplifiableTo(checkedExpression) -> true
|
||||
(baseClause as? KtCallExpression)?.calleeExpression?.isSimplifiableTo(checkedExpression) == true
|
||||
&& baseClause.isCallingInvokeFunction(context) -> true
|
||||
baseClause.hasFirstReceiverOf(receiverExpression) -> withoutResultInCallChain(baseClause, context)
|
||||
baseClause.anyArgumentEvaluatesTo(receiverExpression) -> true
|
||||
receiverExpression is KtThisExpression -> getImplicitReceiver()?.let { it.type == receiverExpression.getType(context) } == true
|
||||
baseClause.hasFirstReceiverOf(checkedExpression) -> withoutResultInCallChain(baseClause, context)
|
||||
baseClause.anyArgumentEvaluatesTo(checkedExpression) -> true
|
||||
checkedExpression is KtThisExpression -> getImplicitReceiver(context)?.let { it.type == checkedExpression.getType(context) } == true
|
||||
else -> false
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ package org.jetbrains.kotlin.idea.inspections.substring
|
||||
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractApplicabilityBasedInspection
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.isSimplifiableTo
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableSimpleExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.toResolvedCall
|
||||
@@ -32,7 +32,7 @@ abstract class ReplaceSubstringInspection : AbstractApplicabilityBasedInspection
|
||||
protected fun isIndexOfCall(expression: KtExpression?, expectedReceiver: KtExpression): Boolean {
|
||||
return expression is KtDotQualifiedExpression
|
||||
&& expression.isMethodCall("kotlin.text.indexOf")
|
||||
&& expression.receiverExpression.evaluatesTo(expectedReceiver)
|
||||
&& expression.receiverExpression.isSimplifiableTo(expectedReceiver)
|
||||
&& expression.callExpression!!.valueArguments.size == 1
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ package org.jetbrains.kotlin.idea.inspections.substring
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.isSimplifiableTo
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
@@ -39,5 +39,5 @@ class ReplaceSubstringWithDropLastInspection : ReplaceSubstringInspection() {
|
||||
private fun isLengthAccess(expression: KtExpression?, expectedReceiver: KtExpression): Boolean =
|
||||
expression is KtDotQualifiedExpression
|
||||
&& expression.selectorExpression.let { it is KtNameReferenceExpression && it.getReferencedName() == "length" }
|
||||
&& expression.receiverExpression.evaluatesTo(expectedReceiver)
|
||||
&& expression.receiverExpression.isSimplifiableTo(expectedReceiver)
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingIntention
|
||||
import org.jetbrains.kotlin.idea.core.setType
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.base.psi.isNullExpression
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtNullableType
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ package org.jetbrains.kotlin.idea.intentions.branchedTransformations
|
||||
|
||||
import org.jetbrains.kotlin.cfg.WhenChecker
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.base.psi.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.safeAnalyzeNonSourceRootCode
|
||||
import org.jetbrains.kotlin.idea.base.psi.replaced
|
||||
|
||||
+114
-153
@@ -13,16 +13,19 @@ import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.base.codeInsight.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.base.psi.expressionComparedToNull
|
||||
import org.jetbrains.kotlin.idea.base.psi.getSingleUnwrappedStatementOrThis
|
||||
import org.jetbrains.kotlin.idea.base.psi.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.base.psi.replaced
|
||||
import org.jetbrains.kotlin.idea.base.psi.textRangeIn
|
||||
import org.jetbrains.kotlin.idea.base.psi.unwrapBlockOrParenthesis
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.safeAnalyzeNonSourceRootCode
|
||||
import org.jetbrains.kotlin.idea.codeInsight.IfThenTransformationData
|
||||
import org.jetbrains.kotlin.idea.codeInsight.IfThenTransformationUtils.checkedExpression
|
||||
import org.jetbrains.kotlin.idea.codeinsight.utils.getLeftMostReceiverExpression
|
||||
import org.jetbrains.kotlin.idea.codeinsight.utils.replaceFirstReceiver
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.isSimplifiableTo
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlinePropertyHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.K1IntroduceVariableHandler
|
||||
@@ -50,12 +53,12 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.constant
|
||||
|
||||
fun KtExpression?.isTrivialStatementBody(): Boolean = when (this?.unwrapBlockOrParenthesis()) {
|
||||
fun KtExpression?.isTrivialStatementBody(): Boolean = when (this?.getSingleUnwrappedStatementOrThis()) {
|
||||
is KtIfExpression, is KtBlockExpression -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
fun KtExpression?.isNullExpression(): Boolean = this?.unwrapBlockOrParenthesis()?.node?.elementType == KtNodeTypes.NULL
|
||||
fun KtExpression?.isNullExpression(): Boolean = isNullExpression()
|
||||
|
||||
fun KtExpression?.isNullExpressionOrEmptyBlock(): Boolean =
|
||||
this.isNullExpression() || this is KtBlockExpression && this.statements.isEmpty()
|
||||
@@ -77,13 +80,10 @@ fun KtThrowExpression.throwsNullPointerExceptionWithNoArguments(): Boolean {
|
||||
} && thrownExpression.valueArguments.isEmpty()
|
||||
}
|
||||
|
||||
fun KtExpression.evaluatesTo(other: KtExpression): Boolean =
|
||||
this.unwrapBlockOrParenthesis().text == other.text
|
||||
|
||||
fun KtExpression.anyArgumentEvaluatesTo(argument: KtExpression): Boolean {
|
||||
val callExpression = this as? KtCallExpression ?: return false
|
||||
val arguments = callExpression.valueArguments.map { it.getArgumentExpression() }
|
||||
return arguments.any { it?.evaluatesTo(argument) == true } && arguments.all { it is KtNameReferenceExpression }
|
||||
return arguments.any { it?.isSimplifiableTo(argument) == true } && arguments.all { it is KtNameReferenceExpression }
|
||||
}
|
||||
|
||||
fun KtExpression.convertToIfNotNullExpression(
|
||||
@@ -171,161 +171,128 @@ private fun KtExpression.toDataFlowValue(context: BindingContext): DataFlowValue
|
||||
return dataFlowValueFactory.createDataFlowValue(this, expressionType, context, findModuleDescriptor())
|
||||
}
|
||||
|
||||
data class IfThenToSelectData(
|
||||
val context: BindingContext,
|
||||
val condition: KtOperationExpression,
|
||||
val receiverExpression: KtExpression,
|
||||
val baseClause: KtExpression,
|
||||
val negatedClause: KtExpression?
|
||||
) {
|
||||
internal fun replacedBaseClause(factory: KtPsiFactory): KtExpression {
|
||||
val newReceiver = (condition as? KtIsExpression)?.let {
|
||||
factory.createExpressionByPattern(
|
||||
"$0 as? $1",
|
||||
it.leftHandSide,
|
||||
it.typeReference!!
|
||||
)
|
||||
}
|
||||
internal fun IfThenTransformationData.replacedBaseClause(factory: KtPsiFactory, context: BindingContext): KtExpression {
|
||||
val baseClause = baseClause
|
||||
val newReceiver = (condition as? KtIsExpression)?.let {
|
||||
factory.createExpressionByPattern(
|
||||
"$0 as? $1",
|
||||
it.leftHandSide,
|
||||
it.typeReference!!
|
||||
)
|
||||
}
|
||||
|
||||
return if (baseClause.evaluatesTo(receiverExpression)) {
|
||||
if (condition is KtIsExpression) newReceiver!! else baseClause
|
||||
} else {
|
||||
when {
|
||||
condition is KtIsExpression -> {
|
||||
when {
|
||||
baseClause is KtDotQualifiedExpression -> baseClause.replaceFirstReceiver(
|
||||
factory, newReceiver!!, safeAccess = true
|
||||
)
|
||||
return if (baseClause.isSimplifiableTo(checkedExpression)) {
|
||||
if (condition is KtIsExpression) newReceiver!! else baseClause
|
||||
} else {
|
||||
when {
|
||||
condition is KtIsExpression -> {
|
||||
when {
|
||||
baseClause is KtDotQualifiedExpression -> baseClause.replaceFirstReceiver(
|
||||
factory, newReceiver!!, safeAccess = true
|
||||
)
|
||||
|
||||
hasImplicitReceiverReplaceableBySafeCall() -> factory.createExpressionByPattern(
|
||||
"$0?.$1",
|
||||
newReceiver!!,
|
||||
baseClause
|
||||
).insertSafeCalls(
|
||||
factory
|
||||
)
|
||||
hasImplicitReceiverReplaceableBySafeCall(context) -> factory.createExpressionByPattern(
|
||||
"$0?.$1",
|
||||
newReceiver!!,
|
||||
baseClause
|
||||
).insertSafeCalls(
|
||||
factory
|
||||
)
|
||||
|
||||
baseClause is KtCallExpression -> baseClause.replaceCallWithLet(newReceiver!!, factory)
|
||||
else -> error("Illegal state")
|
||||
}
|
||||
}
|
||||
|
||||
hasImplicitReceiverReplaceableBySafeCall() -> factory.createExpressionByPattern(
|
||||
"$0?.$1",
|
||||
receiverExpression,
|
||||
baseClause
|
||||
).insertSafeCalls(factory)
|
||||
|
||||
baseClause is KtCallExpression -> {
|
||||
val callee = baseClause.calleeExpression
|
||||
if (callee != null && baseClause.isCallingInvokeFunction(context)) {
|
||||
factory.createExpressionByPattern("$0?.invoke()", callee)
|
||||
} else {
|
||||
baseClause.replaceCallWithLet(receiverExpression, factory)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
var replaced = baseClause.insertSafeCalls(factory)
|
||||
if (replaced is KtQualifiedExpression) {
|
||||
val call = replaced.callExpression
|
||||
val callee = call?.calleeExpression
|
||||
if (callee != null && callee.text != "invoke" && call.isCallingInvokeFunction(context)) {
|
||||
replaced = factory.createExpressionByPattern("$0?.$1?.invoke()", replaced.receiverExpression, callee)
|
||||
}
|
||||
}
|
||||
replaced
|
||||
baseClause is KtCallExpression -> baseClause.replaceCallWithLet(newReceiver!!, checkedExpression, factory)
|
||||
else -> error("Illegal state")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KtExpression.isCallingInvokeFunction(context: BindingContext): Boolean {
|
||||
if (this !is KtCallExpression) return false
|
||||
val resolvedCall = getResolvedCall(context) ?: resolveToCall() ?: return false
|
||||
val descriptor = resolvedCall.resultingDescriptor as? SimpleFunctionDescriptor ?: return false
|
||||
return descriptor is FunctionInvokeDescriptor || descriptor.isOperator && descriptor.name.asString() == "invoke"
|
||||
}
|
||||
hasImplicitReceiverReplaceableBySafeCall(context) -> factory.createExpressionByPattern(
|
||||
"$0?.$1",
|
||||
checkedExpression,
|
||||
baseClause
|
||||
).insertSafeCalls(factory)
|
||||
|
||||
internal fun getImplicitReceiver(): ImplicitReceiver? {
|
||||
val resolvedCall = baseClause.getResolvedCall(context) ?: return null
|
||||
if (resolvedCall.getExplicitReceiverValue() != null) return null
|
||||
return resolvedCall.getImplicitReceiverValue()
|
||||
}
|
||||
|
||||
internal fun hasImplicitReceiverReplaceableBySafeCall(): Boolean =
|
||||
receiverExpression is KtThisExpression && getImplicitReceiver() != null
|
||||
|
||||
private fun KtCallExpression.replaceCallWithLet(
|
||||
receiver: KtExpression,
|
||||
factory: KtPsiFactory
|
||||
): KtExpression {
|
||||
val needExplicitParameter = valueArguments.any { it.getArgumentExpression()?.text == StandardNames.IMPLICIT_LAMBDA_PARAMETER_NAME.identifier }
|
||||
val parameterName = if (needExplicitParameter) {
|
||||
val scope = getResolutionScope()
|
||||
KotlinNameSuggester.suggestNameByName("it") { scope.findVariable(Name.identifier(it), NoLookupLocation.FROM_IDE) == null }
|
||||
} else {
|
||||
"it"
|
||||
}
|
||||
return factory.buildExpression {
|
||||
appendExpression(receiver)
|
||||
appendFixedText("?.let {")
|
||||
if (needExplicitParameter) appendFixedText(" $parameterName ->")
|
||||
appendExpression(calleeExpression)
|
||||
appendFixedText("(")
|
||||
valueArguments.forEachIndexed { index, arg ->
|
||||
if (index != 0) appendFixedText(", ")
|
||||
val argName = arg.getArgumentName()?.asName
|
||||
if (argName != null) {
|
||||
appendName(argName)
|
||||
appendFixedText(" = ")
|
||||
baseClause is KtCallExpression -> {
|
||||
val callee = baseClause.calleeExpression
|
||||
if (callee != null && baseClause.isCallingInvokeFunction(context)) {
|
||||
factory.createExpressionByPattern("$0?.invoke()", callee)
|
||||
} else {
|
||||
baseClause.replaceCallWithLet(checkedExpression, checkedExpression, factory)
|
||||
}
|
||||
val argExpression = arg.getArgumentExpression()
|
||||
if (argExpression?.evaluatesTo(receiverExpression) == true)
|
||||
appendFixedText(parameterName)
|
||||
else
|
||||
appendExpression(argExpression)
|
||||
}
|
||||
appendFixedText(") }")
|
||||
|
||||
else -> {
|
||||
var replaced = baseClause.insertSafeCalls(factory)
|
||||
if (replaced is KtQualifiedExpression) {
|
||||
val call = replaced.callExpression
|
||||
val callee = call?.calleeExpression
|
||||
if (callee != null && callee.text != "invoke" && call.isCallingInvokeFunction(context)) {
|
||||
replaced = factory.createExpressionByPattern("$0?.$1?.invoke()", replaced.receiverExpression, callee)
|
||||
}
|
||||
}
|
||||
replaced
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KtIfExpression.buildSelectTransformationData(): IfThenToSelectData? {
|
||||
val context = safeAnalyzeNonSourceRootCode()
|
||||
|
||||
val condition = condition?.unwrapBlockOrParenthesis() as? KtOperationExpression ?: return null
|
||||
val thenClause = then?.unwrapBlockOrParenthesis()
|
||||
val elseClause = `else`?.unwrapBlockOrParenthesis()
|
||||
val receiverExpression = condition.checkedExpression()?.unwrapBlockOrParenthesis() ?: return null
|
||||
|
||||
val (baseClause, negatedClause) = when (condition) {
|
||||
is KtBinaryExpression -> when (condition.operationToken) {
|
||||
KtTokens.EQEQ -> elseClause to thenClause
|
||||
KtTokens.EXCLEQ -> thenClause to elseClause
|
||||
else -> return null
|
||||
}
|
||||
is KtIsExpression -> {
|
||||
when (condition.isNegated) {
|
||||
true -> elseClause to thenClause
|
||||
false -> thenClause to elseClause
|
||||
}
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
|
||||
if (baseClause == null) return null
|
||||
|
||||
return IfThenToSelectData(context, condition, receiverExpression, baseClause, negatedClause)
|
||||
internal fun KtExpression.isCallingInvokeFunction(context: BindingContext): Boolean {
|
||||
if (this !is KtCallExpression) return false
|
||||
val resolvedCall = getResolvedCall(context) ?: resolveToCall() ?: return false
|
||||
val descriptor = resolvedCall.resultingDescriptor as? SimpleFunctionDescriptor ?: return false
|
||||
return descriptor is FunctionInvokeDescriptor || descriptor.isOperator && descriptor.name.asString() == "invoke"
|
||||
}
|
||||
|
||||
internal fun IfThenToSelectData.conditionHasIncompatibleTypes(): Boolean {
|
||||
if (condition !is KtIsExpression) return false
|
||||
internal fun IfThenTransformationData.getImplicitReceiver(context: BindingContext): ImplicitReceiver? {
|
||||
val resolvedCall = baseClause.getResolvedCall(context) ?: return null
|
||||
if (resolvedCall.getExplicitReceiverValue() != null) return null
|
||||
return resolvedCall.getImplicitReceiverValue()
|
||||
}
|
||||
|
||||
val targetType = context[BindingContext.TYPE, condition.typeReference] ?: return true
|
||||
internal fun IfThenTransformationData.hasImplicitReceiverReplaceableBySafeCall(context: BindingContext): Boolean =
|
||||
checkedExpression is KtThisExpression && getImplicitReceiver(context) != null
|
||||
|
||||
private fun KtCallExpression.replaceCallWithLet(
|
||||
newReceiver: KtExpression,
|
||||
oldReceiver: KtExpression,
|
||||
factory: KtPsiFactory,
|
||||
): KtExpression {
|
||||
val needExplicitParameter =
|
||||
valueArguments.any { it.getArgumentExpression()?.text == StandardNames.IMPLICIT_LAMBDA_PARAMETER_NAME.identifier }
|
||||
val parameterName = if (needExplicitParameter) {
|
||||
val scope = getResolutionScope()
|
||||
KotlinNameSuggester.suggestNameByName("it") { scope.findVariable(Name.identifier(it), NoLookupLocation.FROM_IDE) == null }
|
||||
} else {
|
||||
"it"
|
||||
}
|
||||
return factory.buildExpression {
|
||||
appendExpression(newReceiver)
|
||||
appendFixedText("?.let {")
|
||||
if (needExplicitParameter) appendFixedText(" $parameterName ->")
|
||||
appendExpression(calleeExpression)
|
||||
appendFixedText("(")
|
||||
valueArguments.forEachIndexed { index, arg ->
|
||||
if (index != 0) appendFixedText(", ")
|
||||
val argName = arg.getArgumentName()?.asName
|
||||
if (argName != null) {
|
||||
appendName(argName)
|
||||
appendFixedText(" = ")
|
||||
}
|
||||
val argExpression = arg.getArgumentExpression()
|
||||
if (argExpression?.isSimplifiableTo(oldReceiver) == true)
|
||||
appendFixedText(parameterName)
|
||||
else
|
||||
appendExpression(argExpression)
|
||||
}
|
||||
appendFixedText(") }")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun IfThenTransformationData.conditionHasIncompatibleTypes(context: BindingContext): Boolean {
|
||||
val isExpression = condition as? KtIsExpression ?: return false
|
||||
|
||||
val targetType = context[BindingContext.TYPE, isExpression.typeReference] ?: return true
|
||||
if (TypeUtils.isNullableType(targetType)) return true
|
||||
// TODO: the following check can be removed after fix of KT-14576
|
||||
val originalType = receiverExpression.getType(context) ?: return true
|
||||
val originalType = checkedExpression.getType(context) ?: return true
|
||||
|
||||
return !targetType.isSubtypeOf(originalType)
|
||||
}
|
||||
@@ -335,7 +302,7 @@ internal fun KtExpression?.isClauseTransformableToLetOnly(receiver: KtExpression
|
||||
|
||||
fun KtIfExpression.shouldBeTransformed(): Boolean = when (val condition = condition) {
|
||||
is KtBinaryExpression -> {
|
||||
val baseClause = (if (condition.operationToken == KtTokens.EQEQ) `else` else then)?.unwrapBlockOrParenthesis()
|
||||
val baseClause = (if (condition.operationToken == KtTokens.EQEQ) `else` else then)?.getSingleUnwrappedStatementOrThis()
|
||||
!baseClause.isClauseTransformableToLetOnly(condition.checkedExpression())
|
||||
}
|
||||
else -> false
|
||||
@@ -346,12 +313,6 @@ fun KtIfExpression.fromIfKeywordToRightParenthesisTextRangeInThis(): TextRange {
|
||||
return TextRange(ifKeyword.startOffset, rightOffset).shiftLeft(startOffset)
|
||||
}
|
||||
|
||||
private fun KtExpression.checkedExpression() = when (this) {
|
||||
is KtBinaryExpression -> expressionComparedToNull()
|
||||
is KtIsExpression -> leftHandSide
|
||||
else -> null
|
||||
}
|
||||
|
||||
internal fun KtExpression.hasNullableType(context: BindingContext): Boolean {
|
||||
val type = getType(context) ?: return true
|
||||
return TypeUtils.isNullableType(type)
|
||||
@@ -359,7 +320,7 @@ internal fun KtExpression.hasNullableType(context: BindingContext): Boolean {
|
||||
|
||||
internal fun KtExpression.hasFirstReceiverOf(receiver: KtExpression): Boolean {
|
||||
val actualReceiver = (this as? KtDotQualifiedExpression)?.getLeftMostReceiverExpression() ?: return false
|
||||
return actualReceiver.evaluatesTo(receiver)
|
||||
return actualReceiver.isSimplifiableTo(receiver)
|
||||
}
|
||||
|
||||
private fun KtExpression.insertSafeCalls(factory: KtPsiFactory): KtExpression {
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.base.psi.replaced
|
||||
import org.jetbrains.kotlin.idea.base.psi.unwrapBlockOrParenthesis
|
||||
import org.jetbrains.kotlin.idea.base.psi.getSingleUnwrappedStatementOrThis
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingRangeIntention
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.quickFix.AddLoopLabelFix
|
||||
@@ -117,7 +117,7 @@ class IfToWhenIntention : SelfTargetingRangeIntention<KtIfExpression>(
|
||||
|
||||
private fun BuilderByPattern<*>.appendElseBlock(block: KtExpression?, unwrapBlockOrParenthesis: Boolean = false) {
|
||||
appendFixedText("else->")
|
||||
appendExpression(if (unwrapBlockOrParenthesis) block?.unwrapBlockOrParenthesis() else block)
|
||||
appendExpression(if (unwrapBlockOrParenthesis) block?.getSingleUnwrappedStatementOrThis() else block)
|
||||
appendFixedText("\n")
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeinsight.utils.isFalseConstant
|
||||
import org.jetbrains.kotlin.idea.codeinsight.utils.isTrueConstant
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.hasUsages
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.base.psi.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.Condition
|
||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FilterTransformationBase
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ package org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence
|
||||
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.hasUsages
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.base.psi.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.result.FindTransformationMatcher
|
||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.result.MaxOrMinTransformation
|
||||
|
||||
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.diagnostics.Errors.UNSAFE_CALL
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.base.psi.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.resolve.dataFlowValueFactory
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.quickfixes.KotlinQuickFixAction
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.base.psi.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.base.psi.replaced
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.quickfixes.KotlinQuickFixAction
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||
import org.jetbrains.kotlin.idea.base.psi.isNullExpression
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
|
||||
-5
@@ -411,11 +411,6 @@ public abstract class InspectionTestGenerated extends AbstractInspectionTest {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToElvis/inspectionData/inspections.test");
|
||||
}
|
||||
|
||||
@TestMetadata("branched/ifThenToSafeAccess/inspectionData/inspections.test")
|
||||
public void testBranched_ifThenToSafeAccess_inspectionData_Inspections_test() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/inspectionData/inspections.test");
|
||||
}
|
||||
|
||||
@TestMetadata("conventionNameCalls/replaceGetOrSet/inspectionData/inspections.test")
|
||||
public void testConventionNameCalls_replaceGetOrSet_inspectionData_Inspections_test() throws Exception {
|
||||
runTest("testData/inspectionsLocal/conventionNameCalls/replaceGetOrSet/inspectionData/inspections.test");
|
||||
|
||||
+98
-3
@@ -446,6 +446,11 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("argumentInCallWithReceiver.kt")
|
||||
public void testArgumentInCallWithReceiver() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/argumentInCallWithReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("blockHasMoreThanOneStatement.kt")
|
||||
public void testBlockHasMoreThanOneStatement() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/blockHasMoreThanOneStatement.kt");
|
||||
@@ -476,6 +481,16 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/call4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callChainWithImplicitReceiver.kt")
|
||||
public void testCallChainWithImplicitReceiver() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/callChainWithImplicitReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callChainWithVariableCalls.kt")
|
||||
public void testCallChainWithVariableCalls() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/callChainWithVariableCalls.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callInvokeOperator.kt")
|
||||
public void testCallInvokeOperator() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/callInvokeOperator.kt");
|
||||
@@ -561,11 +576,21 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/ifAndElseBothInBlocks.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifAndElseBothInBlocks2.kt")
|
||||
public void testIfAndElseBothInBlocks2() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/ifAndElseBothInBlocks2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifAndElseNotInBlocks.kt")
|
||||
public void testIfAndElseNotInBlocks() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/ifAndElseNotInBlocks.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifAndElseNotInBlocks2.kt")
|
||||
public void testIfAndElseNotInBlocks2() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/ifAndElseNotInBlocks2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ifAsExpression.kt")
|
||||
public void testIfAsExpression() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/ifAsExpression.kt");
|
||||
@@ -596,6 +621,11 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/implicitReceiverIsNotLeftMostReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitReceiverVariableCall.kt")
|
||||
public void testImplicitReceiverVariableCall() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/implicitReceiverVariableCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isCheckSimple.kt")
|
||||
public void testIsCheckSimple() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/isCheckSimple.kt");
|
||||
@@ -636,6 +666,16 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/lhsNotEqualsNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lhsNotEqualsNull2.kt")
|
||||
public void testLhsNotEqualsNull2() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/lhsNotEqualsNull2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localVar.kt")
|
||||
public void testLocalVar() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/localVar.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("missingNecessaryElseClause.kt")
|
||||
public void testMissingNecessaryElseClause() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/missingNecessaryElseClause.kt");
|
||||
@@ -646,6 +686,26 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/missingNecessaryThenClause.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multipleUsagesNullCheck.kt")
|
||||
public void testMultipleUsagesNullCheck() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/multipleUsagesNullCheck.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multipleUsagesThis.kt")
|
||||
public void testMultipleUsagesThis() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/multipleUsagesThis.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multipleUsagesTypeCheck.kt")
|
||||
public void testMultipleUsagesTypeCheck() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/multipleUsagesTypeCheck.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multipleUsagesVariableCalls.kt")
|
||||
public void testMultipleUsagesVariableCalls() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/multipleUsagesVariableCalls.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noCondition.kt")
|
||||
public void testNoCondition() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/noCondition.kt");
|
||||
@@ -656,6 +716,11 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/noElseBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noElseBlock2.kt")
|
||||
public void testNoElseBlock2() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/noElseBlock2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noElseBlockAsExpression.kt")
|
||||
public void testNoElseBlockAsExpression() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/noElseBlockAsExpression.kt");
|
||||
@@ -681,14 +746,19 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/noThenBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noThenBlock2.kt")
|
||||
public void testNoThenBlock2() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/noThenBlock2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notApplicableForFunction.kt")
|
||||
public void testNotApplicableForFunction() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/notApplicableForFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notApplicableForLocalVar.kt")
|
||||
public void testNotApplicableForLocalVar() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/notApplicableForLocalVar.kt");
|
||||
@TestMetadata("notApplicableForNonLocalVar.kt")
|
||||
public void testNotApplicableForNonLocalVar() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/notApplicableForNonLocalVar.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notApplicableResultInCallChain.kt")
|
||||
@@ -721,6 +791,11 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/propertyNotNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyVariableCall.kt")
|
||||
public void testPropertyVariableCall() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/propertyVariableCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyWithProperty.kt")
|
||||
public void testPropertyWithProperty() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/propertyWithProperty.kt");
|
||||
@@ -746,11 +821,21 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/rhsEqualsNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("rhsEqualsNull2.kt")
|
||||
public void testRhsEqualsNull2() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/rhsEqualsNull2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("rhsNotEqualsNull.kt")
|
||||
public void testRhsNotEqualsNull() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/rhsNotEqualsNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("rhsNotEqualsNull2.kt")
|
||||
public void testRhsNotEqualsNull2() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/rhsNotEqualsNull2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("senselessComparison.kt")
|
||||
public void testSenselessComparison() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/senselessComparison.kt");
|
||||
@@ -786,6 +871,16 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/unacceptableNoThenBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usageInBlock.kt")
|
||||
public void testUsageInBlock() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/usageInBlock.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usageInBlock2.kt")
|
||||
public void testUsageInBlock2() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/usageInBlock2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("uselessIsCheck.kt")
|
||||
public void testUselessIsCheck() throws Exception {
|
||||
runTest("testData/inspectionsLocal/branched/ifThenToSafeAccess/uselessIsCheck.kt");
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.k2.codeinsight.inspections.IfThenToSafeAccessInspection
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
fun String.ext(s: String): String = ""
|
||||
|
||||
fun test(a: Any) {
|
||||
i<caret>f (a is String) {
|
||||
"".ext(a)
|
||||
} else null
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
fun String.ext(s: String): String = ""
|
||||
|
||||
fun test(a: Any) {
|
||||
(a as? String)?.let { "".ext(it) }
|
||||
}
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// WITH_STDLIB
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// WITH_STDLIB
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
class My(val x: Int)
|
||||
|
||||
fun Any.foo(): Int? {
|
||||
return i<caret>f (this is My) x.hashCode() else null
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
class My(val x: Int)
|
||||
|
||||
fun Any.foo(): Int? {
|
||||
return (this as? My)?.x?.hashCode()
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
class Foo(val bar: () -> Foo)
|
||||
|
||||
fun test(foo: Foo?) {
|
||||
i<caret>f (foo != null) {
|
||||
foo.bar().bar()
|
||||
} else null
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
class Foo(val bar: () -> Foo)
|
||||
|
||||
fun test(foo: Foo?) {
|
||||
foo?.bar?.invoke()?.bar?.invoke()
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
class Foo(val bar: Bar)
|
||||
|
||||
class Bar {
|
||||
operator fun invoke() {}
|
||||
}
|
||||
|
||||
fun test(foo: Foo?) {
|
||||
<caret>if (foo != null) {
|
||||
foo.bar()
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
class Foo(val bar: Bar)
|
||||
|
||||
class Bar {
|
||||
operator fun invoke() {}
|
||||
}
|
||||
|
||||
fun test(foo: Foo?) {
|
||||
foo?.bar?.invoke()
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// FIR_COMPARISON
|
||||
class Foo(val bar: Bar)
|
||||
|
||||
class Bar {
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// FIR_COMPARISON
|
||||
class Foo(val bar: Bar)
|
||||
|
||||
class Bar {
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
class Foo(val f: () -> Unit)
|
||||
|
||||
fun test(foo: Foo?) {
|
||||
<caret>if (foo != null) {
|
||||
foo.f()
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
class Foo(val f: () -> Unit)
|
||||
|
||||
fun test(foo: Foo?) {
|
||||
foo?.f?.invoke()
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// FIR_COMPARISON
|
||||
class Foo(val f: () -> Unit)
|
||||
|
||||
fun test(foo: Foo?) {
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// FIR_COMPARISON
|
||||
class Foo(val f: () -> Unit)
|
||||
|
||||
fun test(foo: Foo?) {
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
class Some {
|
||||
fun bar() {}
|
||||
}
|
||||
|
||||
fun Some?.foo() {
|
||||
<caret>if (((this) != null)) {
|
||||
bar()
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
class Some {
|
||||
fun bar() {}
|
||||
}
|
||||
|
||||
fun Some?.foo() {
|
||||
this?.bar()
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// FIR_COMPARISON
|
||||
class Some {
|
||||
fun bar() {}
|
||||
}
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// FIR_COMPARISON
|
||||
class Some {
|
||||
fun bar() {}
|
||||
}
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
fun test(): String? {
|
||||
val foo = maybeFoo()
|
||||
<caret>if (foo != null) {
|
||||
foo.length
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
return foo
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
fun test(): String? {
|
||||
val foo = maybeFoo()
|
||||
foo?.length
|
||||
return foo
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
fun test(): String? {
|
||||
val foo = maybeFoo()
|
||||
i<caret>f (foo != null)
|
||||
foo.length
|
||||
else
|
||||
null
|
||||
return foo
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
fun test(): String? {
|
||||
val foo = maybeFoo()
|
||||
foo?.length
|
||||
return foo
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+2
@@ -1,2 +1,4 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// WITH_STDLIB
|
||||
fun String?.foo() = <caret>if (this == null) null else isEmpty()
|
||||
|
||||
+2
@@ -1,2 +1,4 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// WITH_STDLIB
|
||||
fun String?.foo() = this?.isEmpty()
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K1
|
||||
class A(val f: () -> Unit)
|
||||
|
||||
fun Any.foo() = <caret>if (this !is A) null else f()
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K1
|
||||
class A(val f: () -> Unit)
|
||||
|
||||
fun Any.foo() = (this as? A)?.f?.invoke()
|
||||
-201
@@ -1,201 +0,0 @@
|
||||
<problems>
|
||||
<problem>
|
||||
<file>rhsNotEqualsNull.kt</file>
|
||||
<line>7</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/rhsNotEqualsNull.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>rhsEqualsNull.kt</file>
|
||||
<line>7</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/rhsEqualsNull.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>noThenBlock.kt</file>
|
||||
<line>7</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/noThenBlock.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>noElseBlock.kt</file>
|
||||
<line>7</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/noElseBlock.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>lhsNotEqualsNull.kt</file>
|
||||
<line>7</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/lhsNotEqualsNull.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>lhsEqualsNull.kt</file>
|
||||
<line>7</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/lhsEqualsNull.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>ifAsExpression.kt</file>
|
||||
<line>7</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/ifAsExpression.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>ifAndElseNotInBlocks.kt</file>
|
||||
<line>7</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/ifAndElseNotInBlocks.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>ifAndElseBothInBlocks.kt</file>
|
||||
<line>7</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/ifAndElseBothInBlocks.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>doesNotinlineValueOutsideOfScope.kt</file>
|
||||
<line>8</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/doesNotInlineValueOutsideOfScope.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>doesNotinlineValueIfUsedMoreThanOnce.kt</file>
|
||||
<line>10</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/doesNotInlineValueIfUsedMoreThanOnce.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>willNotInlineClassProperty.kt</file>
|
||||
<line>3</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/src/willNotInlineClassProperty.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>nullCheckWithSelectorCallChain.kt</file>
|
||||
<line>5</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/nullCheckWithSelectorCallChain.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>nullCheckSimple.kt</file>
|
||||
<line>2</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/nullCheckSimple.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>implicitReceiver.kt</file>
|
||||
<line>2</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/implicitReceiver.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>propertyNotNull.kt</file>
|
||||
<line>9</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/propertyNotNull.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>implicitReceiver2.kt</file>
|
||||
<line>6</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<package><default></package>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/implicitReceiver2.kt" />
|
||||
<problem_class severity="INFO" attribute_key="INFO_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>resultCall.kt</file>
|
||||
<line>6</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<package>resultCall</package>
|
||||
<entry_point TYPE="field" FQNAME="resultCall.ResultCallKt result" />
|
||||
<problem_class severity="INFO" attribute_key="INFO_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>noStdResultInCallChain.kt</file>
|
||||
<line>7</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<package>noStdResultInCallChain</package>
|
||||
<entry_point TYPE="field" FQNAME="noStdResultInCallChain.NoStdResultInCallChainKt result" />
|
||||
<problem_class severity="INFO" attribute_key="INFO_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>callVariable2.kt</file>
|
||||
<line>4</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<package>callVariable2</package>
|
||||
<entry_point TYPE="method" FQNAME="callVariable2.CallVariable2Kt void test(callVariable2.Foo foo)" />
|
||||
<problem_class id="IfThenToSafeAccess" severity="INFO" attribute_key="INFO_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>callInvokeOperator2.kt</file>
|
||||
<line>8</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<package>callInvokeOperator2</package>
|
||||
<entry_point TYPE="method" FQNAME="callInvokeOperator2.CallInvokeOperator2Kt void test(callInvokeOperator2.Foo foo)" />
|
||||
<problem_class id="IfThenToSafeAccess" severity="INFO" attribute_key="INFO_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>callInvokeOperator3.kt</file>
|
||||
<line>2</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<package>callInvokeOperator3</package>
|
||||
<entry_point TYPE="method" FQNAME="callInvokeOperator3.CallInvokeOperator3Kt void test(callInvokeOperator3.Foo foo)" />
|
||||
<problem_class id="IfThenToSafeAccess" severity="WEAK WARNING" attribute_key="INFO_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
<highlighted_element>if (foo != null)</highlighted_element>
|
||||
<language>kotlin</language>
|
||||
<offset>4</offset>
|
||||
<length>16</length>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>callInvokeOperator4.kt</file>
|
||||
<line>8</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<package>callInvokeOperator4</package>
|
||||
<entry_point TYPE="method" FQNAME="callInvokeOperator4.CallInvokeOperator4Kt void test(callInvokeOperator4.Foo foo)" />
|
||||
<problem_class id="IfThenToSafeAccess" severity="WEAK WARNING" attribute_key="INFO_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||
<description>Foldable if-then</description>
|
||||
<highlighted_element>if (foo != null)</highlighted_element>
|
||||
<language>kotlin</language>
|
||||
<offset>4</offset>
|
||||
<length>16</length>
|
||||
</problem>
|
||||
</problems>
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.branchedTransformations.IfThenToSafeAccessInspection
|
||||
// WITH_STDLIB
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// FIX: Replace 'if' expression with safe cast expression
|
||||
// HIGHLIGHT: WARNING
|
||||
class My(val x: Int)
|
||||
|
||||
fun foo(arg: Any?): My? {
|
||||
return <caret>if (arg is My) arg else null
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// FIX: Replace 'if' expression with safe cast expression
|
||||
// HIGHLIGHT: WARNING
|
||||
class My(val x: Int)
|
||||
|
||||
fun foo(arg: Any?): My? {
|
||||
return arg as? My
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// FIX: Replace 'if' expression with safe cast expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// FIR_COMPARISON
|
||||
class My(val x: Int)
|
||||
|
||||
fun foo(arg: Any?): My? {
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// FIX: Replace 'if' expression with safe cast expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// FIR_COMPARISON
|
||||
class My(val x: Int)
|
||||
|
||||
fun foo(arg: Any?): My? {
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
/* In K2 nullable checks are also subject to the inspection */
|
||||
fun foo(arg: Any?) = if (arg !is String?<caret>) null else arg?.length
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
/* In K2 nullable checks are also subject to the inspection */
|
||||
fun foo(arg: Any?) = (arg as? String?)?.length
|
||||
+1
@@ -1,2 +1,3 @@
|
||||
// PROBLEM: none
|
||||
// FIR_COMPARISON
|
||||
fun foo(arg: Any) = if (arg !is String?<caret>) null else arg?.length
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
/* In K2 nullable checks are also subject to the inspection */
|
||||
fun foo(arg: Any?) = if (arg is String?<caret>) arg?.length else null
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
/* In K2 nullable checks are also subject to the inspection */
|
||||
fun foo(arg: Any?) = (arg as? String?)?.length
|
||||
+1
@@ -1,2 +1,3 @@
|
||||
// PROBLEM: none
|
||||
// FIR_COMPARISON
|
||||
fun foo(arg: Any) = if (arg is String?<caret>) arg?.length else null
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
fun test(): String? {
|
||||
val foo = maybeFoo()
|
||||
<caret>if (foo != null)
|
||||
foo.length
|
||||
else
|
||||
null
|
||||
return foo
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
fun test(): String? {
|
||||
val foo = maybeFoo()
|
||||
foo?.length
|
||||
return foo
|
||||
}
|
||||
+4
-2
@@ -1,4 +1,6 @@
|
||||
// PROBLEM: none
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: WARNING
|
||||
// IGNORE_K1
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
@@ -8,5 +10,5 @@ fun main(args: Array<String>) {
|
||||
if (foo == null<caret>)
|
||||
null
|
||||
else
|
||||
foo?.length
|
||||
foo.length
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: WARNING
|
||||
// IGNORE_K1
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
var foo = maybeFoo()
|
||||
foo?.length
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
fun test(baz: Int?) {
|
||||
i<caret>f (baz != null) baz + baz else null
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
fun test(baz: Int?) {
|
||||
baz?.let { it + it }
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
fun Any.test() {
|
||||
i<caret>f (this is Int) this + this else null
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
fun Any.test() {
|
||||
(this as? Int)?.let { it + it }
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
fun test(foo: Any) {
|
||||
i<caret>f (foo is Int) foo + foo else null
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// HIGHLIGHT: INFORMATION
|
||||
// IGNORE_K1
|
||||
fun test(foo: Any) {
|
||||
(foo as? Int)?.let { it + it }
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// PROBLEM: none
|
||||
/* Currently is not supported */
|
||||
data class Foo(val a: Any)
|
||||
|
||||
class Bar {
|
||||
operator fun invoke(): Int = 1
|
||||
}
|
||||
|
||||
fun test(foo: Foo) {
|
||||
i<caret>f (foo.a is Bar) {
|
||||
foo.a() + foo.a()
|
||||
} else null
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
fun test(): String? {
|
||||
val foo = maybeFoo()
|
||||
<caret>if (foo != null) {
|
||||
foo.length
|
||||
}
|
||||
return foo
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
fun test(): String? {
|
||||
val foo = maybeFoo()
|
||||
foo?.length
|
||||
return foo
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// HIGHLIGHT: WARNING
|
||||
// FIX: Replace 'if' expression with safe access expression
|
||||
// IGNORE_K2
|
||||
fun maybeFoo(): String? {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user