"Unused receiver parameter" inspection: Fix false negative in same class (KT-23897)
#KT-23897 Fixed
This commit is contained in:
committed by
Nikolay Krasko
parent
793d34b913
commit
e7a9614fb5
+42
-11
@@ -28,14 +28,22 @@ import org.jetbrains.kotlin.idea.MainFunctionDetector
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.core.isOverridable
|
||||
import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatureConfiguration
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.runChangeSignature
|
||||
import org.jetbrains.kotlin.idea.refactoring.explicateAsTextForReceiver
|
||||
import org.jetbrains.kotlin.idea.refactoring.getThisLabelName
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.util.getThisReceiverOwner
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
@@ -54,7 +62,8 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
|
||||
|
||||
val context = receiverTypeReference.analyze()
|
||||
val receiverType = context[BindingContext.TYPE, receiverTypeReference] ?: return
|
||||
if (DescriptorUtils.isCompanionObject(receiverType.constructor.declarationDescriptor)) return
|
||||
val receiverTypeDeclaration = receiverType.constructor.declarationDescriptor
|
||||
if (DescriptorUtils.isCompanionObject(receiverTypeDeclaration)) return
|
||||
|
||||
if (callableDeclaration.isOverridable() ||
|
||||
callableDeclaration.hasModifier(KtTokens.OVERRIDE_KEYWORD) ||
|
||||
@@ -69,6 +78,15 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
|
||||
|
||||
if (callable != null && MainFunctionDetector.isMain(callable)) return
|
||||
|
||||
val containingDeclaration = callable?.containingDeclaration
|
||||
if (containingDeclaration != null && containingDeclaration == receiverTypeDeclaration) {
|
||||
val thisLabelName = containingDeclaration.getThisLabelName()
|
||||
if (!callableDeclaration.anyDescendantOfType<KtThisExpression> { it.getLabelName() == thisLabelName }) {
|
||||
registerProblem(receiverTypeReference, true)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var used = false
|
||||
callableDeclaration.acceptChildren(object : KtVisitorVoid() {
|
||||
override fun visitKtElement(element: KtElement) {
|
||||
@@ -102,14 +120,7 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
|
||||
}
|
||||
})
|
||||
|
||||
if (!used) {
|
||||
holder.registerProblem(
|
||||
receiverTypeReference,
|
||||
KotlinBundle.message("unused.receiver.parameter"),
|
||||
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
|
||||
MyQuickFix()
|
||||
)
|
||||
}
|
||||
if (!used) registerProblem(receiverTypeReference)
|
||||
}
|
||||
|
||||
override fun visitNamedFunction(function: KtNamedFunction) {
|
||||
@@ -119,10 +130,19 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
|
||||
override fun visitProperty(property: KtProperty) {
|
||||
check(property)
|
||||
}
|
||||
|
||||
private fun registerProblem(receiverTypeReference: KtTypeReference, inSameClass: Boolean = false) {
|
||||
holder.registerProblem(
|
||||
receiverTypeReference,
|
||||
KotlinBundle.message("unused.receiver.parameter"),
|
||||
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
|
||||
MyQuickFix(inSameClass)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MyQuickFix : LocalQuickFix {
|
||||
private class MyQuickFix(private val inSameClass: Boolean) : LocalQuickFix {
|
||||
override fun getName(): String = KotlinBundle.message("unused.receiver.parameter.remove")
|
||||
|
||||
private fun configureChangeSignature() = object : KotlinChangeSignatureConfiguration {
|
||||
@@ -136,7 +156,18 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
|
||||
|
||||
val function = element.parent as? KtCallableDeclaration ?: return
|
||||
val callableDescriptor = function.resolveToDescriptorIfAny(BodyResolveMode.FULL) as? CallableDescriptor ?: return
|
||||
runChangeSignature(project, callableDescriptor, configureChangeSignature(), element, name)
|
||||
|
||||
if (inSameClass) {
|
||||
runWriteAction {
|
||||
val explicateAsTextForReceiver = callableDescriptor.explicateAsTextForReceiver()
|
||||
function.forEachDescendantOfType<KtThisExpression> {
|
||||
if (it.text == explicateAsTextForReceiver) it.labelQualifier?.delete()
|
||||
}
|
||||
function.setReceiverTypeReference(null)
|
||||
}
|
||||
} else {
|
||||
runChangeSignature(project, callableDescriptor, configureChangeSignature(), element, name)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFamilyName(): String = name
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
class Test(val str: String) {
|
||||
|
||||
private fun <caret>Test.print() {
|
||||
println(str)
|
||||
println(this.str)
|
||||
println(this@print.str)
|
||||
println()
|
||||
}
|
||||
|
||||
fun test(test: Test) {
|
||||
print()
|
||||
test.print()
|
||||
Test("three").print()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
Test("one").test(Test("two"))
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
class Test(val str: String) {
|
||||
|
||||
private fun <caret>print() {
|
||||
println(str)
|
||||
println(this.str)
|
||||
println(this.str)
|
||||
println()
|
||||
}
|
||||
|
||||
fun test(test: Test) {
|
||||
print()
|
||||
test.print()
|
||||
Test("three").print()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
Test("one").test(Test("two"))
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// PROBLEM: none
|
||||
// WITH_RUNTIME
|
||||
|
||||
class Test(val str: String) {
|
||||
|
||||
private fun <caret>Test.print() {
|
||||
println(str)
|
||||
println(this.str)
|
||||
println(this@print.str)
|
||||
println(this@Test.str)
|
||||
println()
|
||||
}
|
||||
|
||||
fun test(test: Test) {
|
||||
print()
|
||||
test.print()
|
||||
Test("three").print()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
Test("one").test(Test("two"))
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
class Test(private val foo: Int) {
|
||||
|
||||
val <caret>Test.baz: Int
|
||||
get() = foo + this.foo + this@baz.foo
|
||||
|
||||
fun test(test: Test) {
|
||||
println(baz)
|
||||
println(test.baz)
|
||||
println(Test(3).baz)
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
Test(1).test(Test(2))
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
class Test(private val foo: Int) {
|
||||
|
||||
val <caret>baz: Int
|
||||
get() = foo + this.foo + this.foo
|
||||
|
||||
fun test(test: Test) {
|
||||
println(baz)
|
||||
println(test.baz)
|
||||
println(Test(3).baz)
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
Test(1).test(Test(2))
|
||||
}
|
||||
+15
@@ -4697,6 +4697,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/companionPure.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionInSameClass.kt")
|
||||
public void testFunctionInSameClass() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/functionInSameClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionInSameClass2.kt")
|
||||
public void testFunctionInSameClass2() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/functionInSameClass2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("infix.kt")
|
||||
public void testInfix() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/infix.kt");
|
||||
@@ -4711,6 +4721,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
public void testOperator() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/operator.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyInSameClass.kt")
|
||||
public void testPropertyInSameClass() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/propertyInSameClass.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/unusedSymbol")
|
||||
|
||||
Reference in New Issue
Block a user