Extract common part in inspections that checks usage of descriptor in some call

This commit is contained in:
Natalia Selezneva
2018-08-01 16:10:15 +03:00
parent 4c34ced1fa
commit 24579c27a2
2 changed files with 30 additions and 46 deletions
@@ -14,14 +14,11 @@ import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.moveCaret
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.util.getThisReceiverOwner
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -42,7 +39,7 @@ class RedundantWithInspection : AbstractKotlinInspection() {
if (callExpression.getResolvedCall(context)?.resultingDescriptor?.fqNameSafe != FqName("kotlin.with")) return
val lambdaDescriptor = context[BindingContext.FUNCTION, lambda.functionLiteral] ?: return
val lambdaExtensionReceiver = lambdaDescriptor.extensionReceiverParameter
var used = false
lambda.functionLiteral.acceptChildren(object : KtVisitorVoid() {
override fun visitKtElement(element: KtElement) {
@@ -55,27 +52,13 @@ class RedundantWithInspection : AbstractKotlinInspection() {
}
val resolvedCall = element.getResolvedCall(context) ?: return
if (isUsageOfReceiver(resolvedCall, context)) {
used = true
} else if (resolvedCall is VariableAsFunctionResolvedCall && isUsageOfReceiver(resolvedCall.variableCall, context)) {
used = true
}
}
private fun isUsageOfReceiver(resolvedCall: ResolvedCall<*>, bindingContext: BindingContext): Boolean {
// As receiver of call
if (resolvedCall.dispatchReceiver.getThisReceiverOwner(bindingContext) == lambdaDescriptor ||
resolvedCall.extensionReceiver.getThisReceiverOwner(bindingContext) == lambdaDescriptor
) {
return true
if (isUsageOfDescriptor(lambdaDescriptor, resolvedCall, context)) {
used = true
}
// As explicit "this"
if (resolvedCall.candidateDescriptor == lambdaExtensionReceiver) {
return true
}
return false
}
})
if (!used) {
val quickfix = when (receiver) {
is KtSimpleNameExpression, is KtStringTemplateExpression, is KtConstantExpression -> RemoveRedundantWithFix()
@@ -22,6 +22,7 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.MainFunctionDetector
@@ -69,16 +70,16 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
callableDeclaration.isOverridable()
) return
val context = receiverTypeReference.analyze()
val context = callableDeclaration.analyze()
val receiverType = context[BindingContext.TYPE, receiverTypeReference] ?: return
val receiverTypeDeclaration = receiverType.constructor.declarationDescriptor
if (DescriptorUtils.isCompanionObject(receiverTypeDeclaration)) return
val callable = callableDeclaration.descriptor
val callable = callableDeclaration.descriptor ?: return
if (callable != null && MainFunctionDetector.isMain(callable)) return
if (MainFunctionDetector.isMain(callable)) return
val containingDeclaration = callable?.containingDeclaration
val containingDeclaration = callable.containingDeclaration
if (containingDeclaration != null && containingDeclaration == receiverTypeDeclaration) {
val thisLabelName = containingDeclaration.getThisLabelName()
if (!callableDeclaration.anyDescendantOfType<KtThisExpression> { it.getLabelName() == thisLabelName }) {
@@ -93,31 +94,12 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
if (used) return
element.acceptChildren(this)
val bindingContext = element.analyze()
val resolvedCall = element.getResolvedCall(bindingContext) ?: return
val resolvedCall = element.getResolvedCall(context) ?: return
if (isUsageOfReceiver(resolvedCall, bindingContext)) {
used = true
} else if (resolvedCall is VariableAsFunctionResolvedCall
&& isUsageOfReceiver(resolvedCall.variableCall, bindingContext)
) {
if (isUsageOfDescriptor(callable, resolvedCall, context)) {
used = true
}
}
private fun isUsageOfReceiver(resolvedCall: ResolvedCall<*>, bindingContext: BindingContext): Boolean {
// As receiver of call
if (resolvedCall.dispatchReceiver.getThisReceiverOwner(bindingContext) == callable ||
resolvedCall.extensionReceiver.getThisReceiverOwner(bindingContext) == callable
) {
return true
}
// As explicit "this"
if ((resolvedCall.candidateDescriptor as? ReceiverParameterDescriptor)?.containingDeclaration == callable) {
return true
}
return false
}
})
if (!used) registerProblem(receiverTypeReference)
@@ -175,3 +157,22 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
override fun startInWriteAction() = false
}
}
fun isUsageOfDescriptor(descriptor: DeclarationDescriptor, resolvedCall: ResolvedCall<*>, bindingContext: BindingContext): Boolean {
// As receiver of call
if (resolvedCall.dispatchReceiver.getThisReceiverOwner(bindingContext) == descriptor ||
resolvedCall.extensionReceiver.getThisReceiverOwner(bindingContext) == descriptor
) {
return true
}
// As explicit "this"
if ((resolvedCall.candidateDescriptor as? ReceiverParameterDescriptor)?.containingDeclaration == descriptor) {
return true
}
if (resolvedCall is VariableAsFunctionResolvedCall) {
return isUsageOfDescriptor(descriptor, resolvedCall.variableCall, bindingContext)
}
return false
}