"if-then to safe access" inspection: support call expression -> let
#KT-7675 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
2dcbf6aa34
commit
f5cfec4a91
+23
-3
@@ -19,13 +19,16 @@ package org.jetbrains.kotlin.idea.inspections.branchedTransformations
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractApplicabilityBasedInspection
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinVariableInplaceRenameHandler
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||
|
||||
@@ -73,8 +76,9 @@ class IfThenToSafeAccessInspection : AbstractApplicabilityBasedInspection<KtIfEx
|
||||
KtPsiUtil.deparenthesize(newExpr)
|
||||
}
|
||||
|
||||
if (editor != null) {
|
||||
(resultExpr as? KtSafeQualifiedExpression)?.inlineReceiverIfApplicableWithPrompt(editor)
|
||||
if (editor != null && resultExpr is KtSafeQualifiedExpression) {
|
||||
resultExpr.inlineReceiverIfApplicableWithPrompt(editor)
|
||||
resultExpr.renameLetParameter(editor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +87,22 @@ class IfThenToSafeAccessInspection : AbstractApplicabilityBasedInspection<KtIfEx
|
||||
negatedClause == null && baseClause.isUsedAsExpression(context) -> false
|
||||
negatedClause != null && !negatedClause.isNullExpression() -> false
|
||||
else -> baseClause.evaluatesTo(receiverExpression) || baseClause.hasFirstReceiverOf(receiverExpression) ||
|
||||
receiverExpression is KtThisExpression && getImplicitReceiver()?.let { it.type == receiverExpression.getType(context) } == true
|
||||
receiverExpression is KtThisExpression && getImplicitReceiver()?.let { it.type == receiverExpression.getType(context) } == true ||
|
||||
replaceableCallExpression()
|
||||
}
|
||||
|
||||
private fun IfThenToSelectData.replaceableCallExpression(): Boolean {
|
||||
val callExpression = baseClause as? KtCallExpression ?: return false
|
||||
val arguments = callExpression.valueArguments.map { it.getArgumentExpression() }
|
||||
return arguments.any { it?.evaluatesTo(receiverExpression) == true } && arguments.all { it is KtNameReferenceExpression }
|
||||
}
|
||||
|
||||
private fun KtSafeQualifiedExpression.renameLetParameter(editor: Editor) {
|
||||
val callExpression = selectorExpression as? KtCallExpression ?: return
|
||||
if (callExpression.calleeExpression?.text != "let") return
|
||||
val parameter = callExpression.lambdaArguments.singleOrNull()?.getLambdaExpression()?.valueParameters?.singleOrNull() ?: return
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
|
||||
editor.caretModel.moveToOffset(parameter.startOffset)
|
||||
KotlinVariableInplaceRenameHandler().doRename(parameter, editor, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ 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.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.intentions.getLeftMostReceiverExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.replaceFirstReceiver
|
||||
@@ -33,7 +34,10 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinI
|
||||
import org.jetbrains.kotlin.idea.refactoring.isMultiLine
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
@@ -43,6 +47,7 @@ import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverVa
|
||||
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.utils.findVariable
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.constant
|
||||
@@ -211,10 +216,12 @@ data class IfThenToSelectData(
|
||||
hasImplicitReceiver() -> factory.createExpressionByPattern("$0?.$1", newReceiver!!, baseClause).insertSafeCalls(
|
||||
factory
|
||||
)
|
||||
baseClause is KtCallExpression -> replacedBaseClause(baseClause, newReceiver!!, factory)
|
||||
else -> error("Illegal state")
|
||||
}
|
||||
}
|
||||
hasImplicitReceiver() -> factory.createExpressionByPattern("this?.$0", baseClause).insertSafeCalls(factory)
|
||||
baseClause is KtCallExpression -> replacedBaseClause(baseClause, receiverExpression, factory)
|
||||
else -> baseClause.insertSafeCalls(factory)
|
||||
}
|
||||
}
|
||||
@@ -223,6 +230,32 @@ data class IfThenToSelectData(
|
||||
internal fun getImplicitReceiver(): ImplicitReceiver? = baseClause.getResolvedCall(context)?.getImplicitReceiverValue()
|
||||
|
||||
internal fun hasImplicitReceiver(): Boolean = getImplicitReceiver() != null
|
||||
|
||||
private fun replacedBaseClause(baseClause: KtCallExpression, receiver: KtExpression, factory: KtPsiFactory): KtExpression {
|
||||
val needExplicitParameter = baseClause.valueArguments.any { it.getArgumentExpression()?.text == "it" }
|
||||
val parameterName = if (needExplicitParameter) {
|
||||
val scope = baseClause.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(baseClause.calleeExpression)
|
||||
appendFixedText("(")
|
||||
baseClause.valueArguments.forEachIndexed { index, arg ->
|
||||
if (index != 0) appendFixedText(", ")
|
||||
val argExpression = arg.getArgumentExpression()
|
||||
if (argExpression?.evaluatesTo(receiverExpression) == true)
|
||||
appendFixedText(parameterName)
|
||||
else
|
||||
appendExpression(argExpression)
|
||||
}
|
||||
appendFixedText(") }")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KtIfExpression.buildSelectTransformationData(): IfThenToSelectData? {
|
||||
|
||||
Reference in New Issue
Block a user