Merge pull request #797 from JetBrains/rr/kt8365

KT-8365 Bogus "Receiver never used" warning when receiver is used in invoke() convention call
This commit is contained in:
Evgeny Gerashchenko
2015-12-17 17:38:24 +04:00
2 changed files with 50 additions and 13 deletions
@@ -19,9 +19,7 @@ package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.* import com.intellij.codeInspection.*
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
@@ -32,21 +30,19 @@ import org.jetbrains.kotlin.psi.psiUtil.isOverridable
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import kotlin.properties.Delegates
public class UnusedReceiverParameterInspection : AbstractKotlinInspection() { public class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() { return object : KtVisitorVoid() {
private fun check(callableDeclaration: KtCallableDeclaration) { private fun check(callableDeclaration: KtCallableDeclaration) {
val receiverTypeReference = callableDeclaration.getReceiverTypeReference() val receiverTypeReference = callableDeclaration.receiverTypeReference
if (receiverTypeReference == null || receiverTypeReference.textRange.isEmpty) return if (receiverTypeReference == null || receiverTypeReference.textRange.isEmpty) return
if (callableDeclaration.isOverridable() || callableDeclaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return if (callableDeclaration.isOverridable() || callableDeclaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
if (callableDeclaration is KtProperty && callableDeclaration.getAccessors().isEmpty()) return if (callableDeclaration is KtProperty && callableDeclaration.accessors.isEmpty()) return
if (callableDeclaration is KtNamedFunction && !callableDeclaration.hasBody()) return if (callableDeclaration is KtNamedFunction && !callableDeclaration.hasBody()) return
val callable = callableDeclaration.descriptor val callable = callableDeclaration.descriptor
@@ -60,13 +56,25 @@ public class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
val bindingContext = element.analyze() val bindingContext = element.analyze()
val resolvedCall = element.getResolvedCall(bindingContext) ?: return val resolvedCall = element.getResolvedCall(bindingContext) ?: return
if (resolvedCall.getDispatchReceiver().getThisReceiverOwner(bindingContext) == callable || if (isUsageOfReceiver(resolvedCall, bindingContext)) {
used = true
} else if (resolvedCall is VariableAsFunctionResolvedCall
&& isUsageOfReceiver(resolvedCall.variableCall, bindingContext)) {
used = true
}
}
private fun isUsageOfReceiver(resolvedCall: ResolvedCall<*>, bindingContext: BindingContext): Boolean {
// As receiver of call
if (resolvedCall.dispatchReceiver.getThisReceiverOwner(bindingContext) == callable ||
(resolvedCall.extensionReceiver as ReceiverValue?).getThisReceiverOwner(bindingContext) == callable) { (resolvedCall.extensionReceiver as ReceiverValue?).getThisReceiverOwner(bindingContext) == callable) {
used = true return true
} }
else if ((resolvedCall.getCandidateDescriptor() as? ReceiverParameterDescriptor)?.getContainingDeclaration() == callable) { // As explicit "this"
used = true if ((resolvedCall.candidateDescriptor as? ReceiverParameterDescriptor)?.containingDeclaration == callable) {
return true
} }
return false
} }
}) })
@@ -99,6 +107,6 @@ public class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
declaration.setReceiverTypeReference(null) declaration.setReceiverTypeReference(null)
} }
override fun getFamilyName(): String = getName() override fun getFamilyName(): String = name
} }
} }
@@ -0,0 +1,29 @@
// KT-8365 Bogus "Receiver never used" warning
class FunctionLike {
fun invoke() {}
}
class OwnerClass {
val functionLike = FunctionLike()
val function = { }
}
val OwnerClass.extFunctionLike = FunctionLike()
val OwnerClass.extFunction = { }
fun OwnerClass.f1() {
functionLike()
}
fun OwnerClass.f2() {
function()
}
fun OwnerClass.f3() {
extFunctionLike()
}
fun OwnerClass.f4() {
extFunction()
}