Remove 'DataFlowValueKindUtils.isStable', use 'DataFlowValue.isStable' instead
This commit is contained in:
-26
@@ -17,32 +17,6 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|||||||
import org.jetbrains.kotlin.types.expressions.AssignedVariablesSearcher
|
import org.jetbrains.kotlin.types.expressions.AssignedVariablesSearcher
|
||||||
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor
|
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether a variable with a given descriptor is stable or not at the given usage place.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Stable means that the variable value cannot change. The simple (non-property) variable is considered stable if it's immutable (val).
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* If the variable is a property, it's considered stable if it's immutable (val) AND it's final (not open) AND
|
|
||||||
* the default getter is in use (otherwise nobody can guarantee that a getter is consistent) AND
|
|
||||||
* (it's private OR internal OR used at the same module where it's defined).
|
|
||||||
* The last check corresponds to a risk of changing property definition in another module, e.g. from "val" to "var".
|
|
||||||
|
|
||||||
* @param variableDescriptor descriptor of a considered variable
|
|
||||||
* *
|
|
||||||
* @param usageModule a module with a considered usage place, or null if it's not known (not recommended)
|
|
||||||
* *
|
|
||||||
* @return true if variable is stable, false otherwise
|
|
||||||
*/
|
|
||||||
fun isStableValue(
|
|
||||||
variableDescriptor: VariableDescriptor,
|
|
||||||
usageModule: ModuleDescriptor?
|
|
||||||
): Boolean {
|
|
||||||
if (variableDescriptor.isVar) return false
|
|
||||||
return variableDescriptor !is PropertyDescriptor || variableDescriptor.propertyKind(usageModule) === DataFlowValue.Kind.STABLE_VALUE
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun PropertyDescriptor.propertyKind(usageModule: ModuleDescriptor?): DataFlowValue.Kind {
|
internal fun PropertyDescriptor.propertyKind(usageModule: ModuleDescriptor?): DataFlowValue.Kind {
|
||||||
if (isVar) return DataFlowValue.Kind.MUTABLE_PROPERTY
|
if (isVar) return DataFlowValue.Kind.MUTABLE_PROPERTY
|
||||||
if (isOverridable) return DataFlowValue.Kind.PROPERTY_WITH_GETTER
|
if (isOverridable) return DataFlowValue.Kind.PROPERTY_WITH_GETTER
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import com.intellij.codeInspection.*
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStable
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableSimpleExpression
|
||||||
import org.jetbrains.kotlin.lexer.KtToken
|
import org.jetbrains.kotlin.lexer.KtToken
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -94,8 +94,8 @@ class NullChecksToSafeCallInspection : AbstractKotlinInspection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun KtExpression.isChainStable(context: BindingContext): Boolean = when (this) {
|
private fun KtExpression.isChainStable(context: BindingContext): Boolean = when (this) {
|
||||||
is KtReferenceExpression -> isStable(context)
|
is KtReferenceExpression -> isStableSimpleExpression(context)
|
||||||
is KtQualifiedExpression -> selectorExpression?.isStable(context) == true && receiverExpression.isChainStable(context)
|
is KtQualifiedExpression -> selectorExpression?.isStableSimpleExpression(context) == true && receiverExpression.isChainStable(context)
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ class IfThenToSafeAccessInspection : AbstractApplicabilityBasedInspection<KtIfEx
|
|||||||
|
|
||||||
override fun isApplicable(element: KtIfExpression): Boolean {
|
override fun isApplicable(element: KtIfExpression): Boolean {
|
||||||
val ifThenToSelectData = element.buildSelectTransformationData() ?: return false
|
val ifThenToSelectData = element.buildSelectTransformationData() ?: return false
|
||||||
if (!ifThenToSelectData.receiverExpression.isStable(ifThenToSelectData.context)) return false
|
if (!ifThenToSelectData.receiverExpression.isStableSimpleExpression(ifThenToSelectData.context)) return false
|
||||||
|
|
||||||
return ifThenToSelectData.clausesReplaceableBySafeCall()
|
return ifThenToSelectData.clausesReplaceableBySafeCall()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.idea.intentions
|
|||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStable
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableSimpleExpression
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||||
@@ -30,7 +30,7 @@ abstract class ReplaceSubstringIntention(text: String) : SelfTargetingRangeInten
|
|||||||
protected abstract fun applicabilityRangeInner(element: KtDotQualifiedExpression): TextRange?
|
protected abstract fun applicabilityRangeInner(element: KtDotQualifiedExpression): TextRange?
|
||||||
|
|
||||||
override fun applicabilityRange(element: KtDotQualifiedExpression): TextRange? {
|
override fun applicabilityRange(element: KtDotQualifiedExpression): TextRange? {
|
||||||
if (element.receiverExpression.isStable() && element.isMethodCall("kotlin.text.substring")) {
|
if (element.receiverExpression.isStableSimpleExpression() && element.isMethodCall("kotlin.text.substring")) {
|
||||||
return applicabilityRangeInner(element)
|
return applicabilityRangeInner(element)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
|||||||
+22
-8
@@ -21,8 +21,9 @@ import com.intellij.openapi.fileEditor.FileDocumentManager
|
|||||||
import com.intellij.psi.search.LocalSearchScope
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import org.jetbrains.kotlin.KtNodeTypes
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
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.caches.resolve.resolveToCall
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.intentions.getLeftMostReceiverExpression
|
import org.jetbrains.kotlin.idea.intentions.getLeftMostReceiverExpression
|
||||||
@@ -31,15 +32,16 @@ import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
|
|||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
|
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
|
||||||
import org.jetbrains.kotlin.idea.refactoring.isMultiLine
|
import org.jetbrains.kotlin.idea.refactoring.isMultiLine
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
|
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.BindingContextUtils
|
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||||
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverValue
|
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverValue
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.isStableValue
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||||
@@ -158,11 +160,23 @@ fun KtPostfixExpression.inlineBaseExpressionIfApplicableWithPrompt(editor: Edito
|
|||||||
(this.baseExpression as? KtNameReferenceExpression)?.inlineIfDeclaredLocallyAndOnlyUsedOnceWithPrompt(editor)
|
(this.baseExpression as? KtNameReferenceExpression)?.inlineIfDeclaredLocallyAndOnlyUsedOnceWithPrompt(editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtExpression.isStable(context: BindingContext = this.analyze()): Boolean {
|
// I.e. stable val/var/receiver
|
||||||
if (this is KtConstantExpression || this is KtThisExpression) return true
|
// We exclude stable complex expressions here, because we don't do smartcasts on them (even though they are stable)
|
||||||
val descriptor = BindingContextUtils.extractVariableDescriptorFromReference(context, this)
|
fun KtExpression.isStableSimpleExpression(context: BindingContext = this.analyze()): Boolean {
|
||||||
return descriptor is VariableDescriptor &&
|
val dataFlowValue = this.toDataFlowValue(context)
|
||||||
isStableValue(descriptor, DescriptorUtils.getContainingModule(descriptor))
|
return dataFlowValue?.isStable == true &&
|
||||||
|
dataFlowValue.kind != DataFlowValue.Kind.STABLE_COMPLEX_EXPRESSION
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtExpression.isStableVal(context: BindingContext = this.analyze()): Boolean {
|
||||||
|
return this.toDataFlowValue(context)?.kind == DataFlowValue.Kind.STABLE_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtExpression.toDataFlowValue(context: BindingContext): DataFlowValue? {
|
||||||
|
val expressionType = this.getType(context) ?: return null
|
||||||
|
val dataFlowValueFactory = this.getResolutionFacade().frontendService<DataFlowValueFactory>()
|
||||||
|
return dataFlowValueFactory.createDataFlowValue(this, expressionType, context, findModuleDescriptor())
|
||||||
}
|
}
|
||||||
|
|
||||||
data class IfThenToSelectData(
|
data class IfThenToSelectData(
|
||||||
|
|||||||
+2
-2
@@ -31,7 +31,7 @@ import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
|||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNotNullExpression
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNotNullExpression
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNullExpression
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNullExpression
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceValueForCondition
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceValueForCondition
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStable
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableSimpleExpression
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.KtPostfixExpression
|
import org.jetbrains.kotlin.psi.KtPostfixExpression
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
@@ -56,7 +56,7 @@ class DoubleBangToIfThenIntention : SelfTargetingRangeIntention<KtPostfixExpress
|
|||||||
val defaultException = KtPsiFactory(element).createExpression("throw NullPointerException()")
|
val defaultException = KtPsiFactory(element).createExpression("throw NullPointerException()")
|
||||||
|
|
||||||
val isStatement = element.isUsedAsStatement(element.analyze())
|
val isStatement = element.isUsedAsStatement(element.analyze())
|
||||||
val isStable = base.isStable()
|
val isStable = base.isStableSimpleExpression()
|
||||||
|
|
||||||
val ifStatement = if (isStatement)
|
val ifStatement = if (isStatement)
|
||||||
element.convertToIfNullExpression(base, defaultException)
|
element.convertToIfNullExpression(base, defaultException)
|
||||||
|
|||||||
+3
-3
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
|||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNotNullExpression
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNotNullExpression
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfStatement
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfStatement
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceValueForCondition
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceValueForCondition
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStable
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableSimpleExpression
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
@@ -82,14 +82,14 @@ class ElvisToIfThenIntention : SelfTargetingRangeIntention<KtBinaryExpression>(K
|
|||||||
val newReceiver = leftSafeCastReceiver.left
|
val newReceiver = leftSafeCastReceiver.left
|
||||||
val typeReference = leftSafeCastReceiver.right!!
|
val typeReference = leftSafeCastReceiver.right!!
|
||||||
val factory = KtPsiFactory(element)
|
val factory = KtPsiFactory(element)
|
||||||
newReceiver.isStable(context) to element.convertToIfStatement(
|
newReceiver.isStableSimpleExpression(context) to element.convertToIfStatement(
|
||||||
factory.createExpressionByPattern("$0 is $1", newReceiver, typeReference),
|
factory.createExpressionByPattern("$0 is $1", newReceiver, typeReference),
|
||||||
left.buildExpressionWithReplacedReceiver(factory, newReceiver),
|
left.buildExpressionWithReplacedReceiver(factory, newReceiver),
|
||||||
right
|
right
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
left.isStable(context) to element.convertToIfNotNullExpression(left, left, right)
|
left.isStableSimpleExpression(context) to element.convertToIfNotNullExpression(left, left, right)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!leftIsStable) {
|
if (!leftIsStable) {
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ class IfThenToDoubleBangIntention : SelfTargetingRangeIntention<KtIfExpression>(
|
|||||||
|
|
||||||
val matchesAsStatement = element.isUsedAsStatement(context) && (baseClause?.isNullExpressionOrEmptyBlock() ?: true)
|
val matchesAsStatement = element.isUsedAsStatement(context) && (baseClause?.isNullExpressionOrEmptyBlock() ?: true)
|
||||||
if (!matchesAsStatement &&
|
if (!matchesAsStatement &&
|
||||||
!(baseClause?.evaluatesTo(receiverExpression) ?: false && receiverExpression.isStable())) return null
|
!(baseClause?.evaluatesTo(receiverExpression) ?: false && receiverExpression.isStableSimpleExpression())) return null
|
||||||
|
|
||||||
var text = "Replace 'if' expression with '!!' expression"
|
var text = "Replace 'if' expression with '!!' expression"
|
||||||
if (!throwExpression.throwsNullPointerExceptionWithNoArguments()) {
|
if (!throwExpression.throwsNullPointerExceptionWithNoArguments()) {
|
||||||
|
|||||||
+1
-1
@@ -57,7 +57,7 @@ class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention<KtIfExpre
|
|||||||
|
|
||||||
override fun isApplicableTo(element: KtIfExpression): Boolean {
|
override fun isApplicableTo(element: KtIfExpression): Boolean {
|
||||||
val ifThenToSelectData = element.buildSelectTransformationData() ?: return false
|
val ifThenToSelectData = element.buildSelectTransformationData() ?: return false
|
||||||
if (!ifThenToSelectData.receiverExpression.isStable(ifThenToSelectData.context)) return false
|
if (!ifThenToSelectData.receiverExpression.isStableSimpleExpression(ifThenToSelectData.context)) return false
|
||||||
|
|
||||||
val type = element.getType(ifThenToSelectData.context) ?: return false
|
val type = element.getType(ifThenToSelectData.context) ?: return false
|
||||||
if (KotlinBuiltIns.isUnit(type)) return false
|
if (KotlinBuiltIns.isUnit(type)) return false
|
||||||
|
|||||||
+2
-2
@@ -23,7 +23,7 @@ import com.intellij.psi.util.PsiTreeUtil
|
|||||||
import org.jetbrains.kotlin.idea.core.appendElement
|
import org.jetbrains.kotlin.idea.core.appendElement
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStable
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableVal
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.matches
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.matches
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -36,7 +36,7 @@ class MergeWhenIntention : SelfTargetingRangeIntention<KtWhenExpression>(KtWhenE
|
|||||||
val subject1 = element.subjectExpression
|
val subject1 = element.subjectExpression
|
||||||
val subject2 = next.subjectExpression
|
val subject2 = next.subjectExpression
|
||||||
if (!subject1.matches(subject2)) return null
|
if (!subject1.matches(subject2)) return null
|
||||||
if (subject1 != null && !subject1.isStable()) return null
|
if (subject1 != null && !subject1.isStableVal()) return null
|
||||||
|
|
||||||
val entries1 = element.entries
|
val entries1 = element.entries
|
||||||
val entries2 = next.entries
|
val entries2 = next.entries
|
||||||
|
|||||||
+2
-2
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.idea.core.replaced
|
|||||||
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNotNullExpression
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNotNullExpression
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceValueForCondition
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceValueForCondition
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStable
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableSimpleExpression
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
|
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
|
||||||
@@ -40,7 +40,7 @@ class SafeAccessToIfThenIntention : SelfTargetingRangeIntention<KtSafeQualifiedE
|
|||||||
val receiver = KtPsiUtil.safeDeparenthesize(element.receiverExpression)
|
val receiver = KtPsiUtil.safeDeparenthesize(element.receiverExpression)
|
||||||
val selector = element.selectorExpression!!
|
val selector = element.selectorExpression!!
|
||||||
|
|
||||||
val receiverIsStable = receiver.isStable()
|
val receiverIsStable = receiver.isStableSimpleExpression()
|
||||||
|
|
||||||
val psiFactory = KtPsiFactory(element)
|
val psiFactory = KtPsiFactory(element)
|
||||||
val dotQualified = psiFactory.createExpressionByPattern("$0.$1", receiver, selector)
|
val dotQualified = psiFactory.createExpressionByPattern("$0.$1", receiver, selector)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import com.intellij.openapi.project.Project
|
|||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStable
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableSimpleExpression
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getLastParentOfTypeInRow
|
import org.jetbrains.kotlin.psi.psiUtil.getLastParentOfTypeInRow
|
||||||
@@ -72,7 +72,7 @@ class SurroundWithNullCheckFix(
|
|||||||
else -> return null
|
else -> return null
|
||||||
} as? KtReferenceExpression ?: return null
|
} as? KtReferenceExpression ?: return null
|
||||||
|
|
||||||
if (!nullableExpression.isStable(context)) return null
|
if (!nullableExpression.isStableSimpleExpression(context)) return null
|
||||||
|
|
||||||
val expressionTarget = expressionParent.getParentOfTypesAndPredicate(strict = false, parentClasses = KtExpression::class.java) {
|
val expressionTarget = expressionParent.getParentOfTypesAndPredicate(strict = false, parentClasses = KtExpression::class.java) {
|
||||||
!it.isUsedAsExpression(context) && it.hasAcceptableParent()
|
!it.isUsedAsExpression(context) && it.hasAcceptableParent()
|
||||||
@@ -91,7 +91,7 @@ class SurroundWithNullCheckFix(
|
|||||||
val forExpression = nullableExpression.parent.parent as? KtForExpression ?: return null
|
val forExpression = nullableExpression.parent.parent as? KtForExpression ?: return null
|
||||||
if (forExpression.parent !is KtBlockExpression) return null
|
if (forExpression.parent !is KtBlockExpression) return null
|
||||||
|
|
||||||
if (!nullableExpression.isStable()) return null
|
if (!nullableExpression.isStableSimpleExpression()) return null
|
||||||
|
|
||||||
return SurroundWithNullCheckFix(forExpression, nullableExpression)
|
return SurroundWithNullCheckFix(forExpression, nullableExpression)
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ class SurroundWithNullCheckFix(
|
|||||||
|
|
||||||
if (!isNullabilityMismatch(expected = typeMismatch.a, actual = typeMismatch.b)) return null
|
if (!isNullabilityMismatch(expected = typeMismatch.a, actual = typeMismatch.b)) return null
|
||||||
|
|
||||||
if (!nullableExpression.isStable()) return null
|
if (!nullableExpression.isStableSimpleExpression()) return null
|
||||||
|
|
||||||
return SurroundWithNullCheckFix(rootCall, nullableExpression)
|
return SurroundWithNullCheckFix(rootCall, nullableExpression)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
var a: String? = "A"
|
var a: String? = "A"
|
||||||
val a1 = a
|
doSomething(if (a != null) a else throw NullPointerException("Expression 'a' must not be null"))
|
||||||
doSomething(if (a1 != null) a1 else throw NullPointerException("Expression 'a' must not be null"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun doSomething(a: Any){}
|
fun doSomething(a: Any){}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
class My(val x: Int?)
|
class My(val x: Int?)
|
||||||
fun foo(arg: Any) {
|
fun foo(arg: Any) {
|
||||||
val y = if ((arg as? My)?.x != null) (arg as? My)?.x else 42
|
val x = (arg as? My)?.x
|
||||||
|
val y = if (x != null) x else 42
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
var a: String? = "A"
|
var a: String? = "A"
|
||||||
val a1 = a
|
if (a != null) a else "bar"
|
||||||
if (a1 != null) a1 else "bar"
|
|
||||||
}
|
}
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun maybeFoo(): String? {
|
||||||
|
return "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(): String? {
|
||||||
|
var foo = maybeFoo()
|
||||||
|
val bar = if (foo == null<caret>)
|
||||||
|
throw NullPointerException()
|
||||||
|
else
|
||||||
|
foo
|
||||||
|
return foo
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun maybeFoo(): String? {
|
||||||
|
return "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(): String? {
|
||||||
|
var foo = maybeFoo()
|
||||||
|
val bar = foo!!
|
||||||
|
return foo
|
||||||
|
}
|
||||||
+8
-1
@@ -1,11 +1,18 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
//IS_APPLICABLE: false
|
// IS_APPLICABLE: false
|
||||||
fun maybeFoo(): String? {
|
fun maybeFoo(): String? {
|
||||||
return "foo"
|
return "foo"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun capture(block: () -> Unit): Unit = Unit
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
var foo = maybeFoo()
|
var foo = maybeFoo()
|
||||||
|
|
||||||
|
capture {
|
||||||
|
foo = null
|
||||||
|
}
|
||||||
|
|
||||||
if (foo == null<caret>)
|
if (foo == null<caret>)
|
||||||
throw NullPointerException()
|
throw NullPointerException()
|
||||||
else
|
else
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun maybeFoo(): String? {
|
||||||
|
return "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(): String? {
|
||||||
|
var foo = maybeFoo()
|
||||||
|
val bar = if (foo == null<caret>)
|
||||||
|
"hello"
|
||||||
|
else
|
||||||
|
foo
|
||||||
|
return foo
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun maybeFoo(): String? {
|
||||||
|
return "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(): String? {
|
||||||
|
var foo = maybeFoo()
|
||||||
|
val bar = foo ?: "hello"
|
||||||
|
return foo
|
||||||
|
}
|
||||||
@@ -111,4 +111,12 @@
|
|||||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?:'</problem_class>
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?:'</problem_class>
|
||||||
<description>Replace 'if' expression with elvis expression</description>
|
<description>Replace 'if' expression with elvis expression</description>
|
||||||
</problem>
|
</problem>
|
||||||
|
<problem>
|
||||||
|
<file>applicableForLocalStableVar.kt</file>
|
||||||
|
<line>8</line>
|
||||||
|
<module>light_idea_test_case</module>
|
||||||
|
<entry_point TYPE="file" FQNAME="temp:///src/applicableForLocalStableVar.kt" />
|
||||||
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?:'</problem_class>
|
||||||
|
<description>Replace 'if' expression with elvis expression</description>
|
||||||
|
</problem>
|
||||||
</problems>
|
</problems>
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun maybeFoo(): String? {
|
||||||
|
return "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun capture(block: () -> Unit): Unit = Unit
|
||||||
|
|
||||||
|
fun test(): String? {
|
||||||
|
var foo = maybeFoo()
|
||||||
|
|
||||||
|
capture {
|
||||||
|
foo = null
|
||||||
|
}
|
||||||
|
|
||||||
|
val bar = if (foo == null<caret>)
|
||||||
|
42
|
||||||
|
else
|
||||||
|
foo
|
||||||
|
|
||||||
|
return foo
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
//IS_APPLICABLE: false
|
|
||||||
fun maybeFoo(): String? {
|
|
||||||
return "foo"
|
|
||||||
}
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
|
||||||
var foo = maybeFoo()
|
|
||||||
if (foo == null<caret>)
|
|
||||||
"bar"
|
|
||||||
else
|
|
||||||
foo
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ fun <T> doSomething(a: T) {}
|
|||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
var a: String? = "A"
|
var a: String? = "A"
|
||||||
val a1 = a
|
doSomething(if (a != null) a.length else null)
|
||||||
doSomething(if (a1 != null) a1.length else null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-6
@@ -2135,6 +2135,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/branched/ifThenToDoubleBang"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/branched/ifThenToDoubleBang"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("applicableForLocalStableVar.kt")
|
||||||
|
public void testApplicableForLocalStableVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToDoubleBang/applicableForLocalStableVar.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("blockHasMoreThanOneStatement.kt")
|
@TestMetadata("blockHasMoreThanOneStatement.kt")
|
||||||
public void testBlockHasMoreThanOneStatement() throws Exception {
|
public void testBlockHasMoreThanOneStatement() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToDoubleBang/blockHasMoreThanOneStatement.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToDoubleBang/blockHasMoreThanOneStatement.kt");
|
||||||
@@ -2255,9 +2261,9 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("notApplicableForLocalVar.kt")
|
@TestMetadata("notApplicableForLocalUnstableVar.kt")
|
||||||
public void testNotApplicableForLocalVar() throws Exception {
|
public void testNotApplicableForLocalUnstableVar() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToDoubleBang/notApplicableForLocalVar.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToDoubleBang/notApplicableForLocalUnstableVar.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2330,6 +2336,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/branched/ifThenToElvis"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/branched/ifThenToElvis"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("applicableForLocalStableVar.kt")
|
||||||
|
public void testApplicableForLocalStableVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/applicableForLocalStableVar.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("blockHasMoreThanOneStatement.kt")
|
@TestMetadata("blockHasMoreThanOneStatement.kt")
|
||||||
public void testBlockHasMoreThanOneStatement() throws Exception {
|
public void testBlockHasMoreThanOneStatement() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/blockHasMoreThanOneStatement.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/blockHasMoreThanOneStatement.kt");
|
||||||
@@ -2510,9 +2522,9 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("notApplicableForLocalVar.kt")
|
@TestMetadata("notApplicableForLocalUnstableVar.kt")
|
||||||
public void testNotApplicableForLocalVar() throws Exception {
|
public void testNotApplicableForLocalUnstableVar() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/notApplicableForLocalVar.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/notApplicableForLocalUnstableVar.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user