"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.analyze
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||||
import org.jetbrains.kotlin.idea.core.isOverridable
|
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.KotlinChangeSignatureConfiguration
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.runChangeSignature
|
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.search.usagesSearch.descriptor
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.idea.util.getThisReceiverOwner
|
import org.jetbrains.kotlin.idea.util.getThisReceiverOwner
|
||||||
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.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.BindingContext
|
||||||
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
|
||||||
@@ -54,7 +62,8 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
|
|||||||
|
|
||||||
val context = receiverTypeReference.analyze()
|
val context = receiverTypeReference.analyze()
|
||||||
val receiverType = context[BindingContext.TYPE, receiverTypeReference] ?: return
|
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() ||
|
if (callableDeclaration.isOverridable() ||
|
||||||
callableDeclaration.hasModifier(KtTokens.OVERRIDE_KEYWORD) ||
|
callableDeclaration.hasModifier(KtTokens.OVERRIDE_KEYWORD) ||
|
||||||
@@ -69,6 +78,15 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
|
|||||||
|
|
||||||
if (callable != null && MainFunctionDetector.isMain(callable)) return
|
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
|
var used = false
|
||||||
callableDeclaration.acceptChildren(object : KtVisitorVoid() {
|
callableDeclaration.acceptChildren(object : KtVisitorVoid() {
|
||||||
override fun visitKtElement(element: KtElement) {
|
override fun visitKtElement(element: KtElement) {
|
||||||
@@ -102,14 +120,7 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!used) {
|
if (!used) registerProblem(receiverTypeReference)
|
||||||
holder.registerProblem(
|
|
||||||
receiverTypeReference,
|
|
||||||
KotlinBundle.message("unused.receiver.parameter"),
|
|
||||||
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
|
|
||||||
MyQuickFix()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitNamedFunction(function: KtNamedFunction) {
|
override fun visitNamedFunction(function: KtNamedFunction) {
|
||||||
@@ -119,10 +130,19 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
|
|||||||
override fun visitProperty(property: KtProperty) {
|
override fun visitProperty(property: KtProperty) {
|
||||||
check(property)
|
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")
|
override fun getName(): String = KotlinBundle.message("unused.receiver.parameter.remove")
|
||||||
|
|
||||||
private fun configureChangeSignature() = object : KotlinChangeSignatureConfiguration {
|
private fun configureChangeSignature() = object : KotlinChangeSignatureConfiguration {
|
||||||
@@ -136,7 +156,18 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
|
|||||||
|
|
||||||
val function = element.parent as? KtCallableDeclaration ?: return
|
val function = element.parent as? KtCallableDeclaration ?: return
|
||||||
val callableDescriptor = function.resolveToDescriptorIfAny(BodyResolveMode.FULL) as? CallableDescriptor ?: 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
|
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");
|
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")
|
@TestMetadata("infix.kt")
|
||||||
public void testInfix() throws Exception {
|
public void testInfix() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/infix.kt");
|
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/infix.kt");
|
||||||
@@ -4711,6 +4721,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
public void testOperator() throws Exception {
|
public void testOperator() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/operator.kt");
|
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")
|
@TestMetadata("idea/testData/inspectionsLocal/unusedSymbol")
|
||||||
|
|||||||
Reference in New Issue
Block a user