Explicit this inspection: fix false negative with local variable

#KT-30136 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-07-10 13:44:05 +09:00
committed by Mikhail Glukhikh
parent eb469b924f
commit 0ec18b4a33
7 changed files with 83 additions and 1 deletions
@@ -22,13 +22,18 @@ import com.intellij.codeInspection.ProblemHighlightType.LIKE_UNUSED_SYMBOL
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.config.LanguageFeature.CallableReferencesToClassMembersWithEmptyLHS
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
@@ -60,7 +65,14 @@ class ExplicitThisInspection : AbstractKotlinInspection() {
//we avoid overload-related problems by enforcing that there is only one candidate
val name = referenceExpression.getReferencedNameAsName()
val candidates = scope.getAllAccessibleVariables(name) + scope.getAllAccessibleFunctions(name)
val candidates = if (reference is KtCallExpression
|| (expression is KtCallableReferenceExpression && reference.mainReference.resolve() is KtFunction)
) {
scope.getAllAccessibleFunctions(name) +
scope.getAllAccessibleVariables(name).filter { it is LocalVariableDescriptor && it.canInvoke() }
} else {
scope.getAllAccessibleVariables(name)
}
if (referenceExpression.getCallableDescriptor() is SyntheticJavaPropertyDescriptor) {
if (candidates.map { it.containingDeclaration }.distinct().size != 1) return
} else {
@@ -76,6 +88,11 @@ class ExplicitThisInspection : AbstractKotlinInspection() {
holder.registerProblem(thisExpression, "Redundant explicit this", LIKE_UNUSED_SYMBOL, Fix(thisExpression.text))
}
private fun VariableDescriptor.canInvoke(): Boolean {
val declarationDescriptor = this.type.constructor.declarationDescriptor as? LazyClassDescriptor ?: return false
return declarationDescriptor.declaredCallableMembers.any { (it as? FunctionDescriptor)?.isOperator == true }
}
private fun ReceiverExpressionFactory.matchesLabel(label: String): Boolean {
val implicitLabel = expressionText.substringAfter("@", "")
@@ -0,0 +1,14 @@
// PROBLEM: none
class Some {
operator fun invoke() {}
}
class Other {
fun foo() {
val a = Some()
<caret>this.a()
a()
}
fun a() {}
}
@@ -0,0 +1,9 @@
class Foo {
fun foo() {
val a = 1
<caret>this.a()
}
fun a() {
}
}
@@ -0,0 +1,9 @@
class Foo {
fun foo() {
val a = 1
<caret>a()
}
fun a() {
}
}
@@ -0,0 +1,9 @@
class Foo {
fun foo() {
fun a() {
}
<caret>this.a
}
val a = 1
}
@@ -0,0 +1,9 @@
class Foo {
fun foo() {
fun a() {
}
<caret>a
}
val a = 1
}
@@ -4196,6 +4196,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/explicitThis/functionReference.kt");
}
@TestMetadata("functionWithSameNameInvocableVariable.kt")
public void testFunctionWithSameNameInvocableVariable() throws Exception {
runTest("idea/testData/inspectionsLocal/explicitThis/functionWithSameNameInvocableVariable.kt");
}
@TestMetadata("functionWithSameNameVariable.kt")
public void testFunctionWithSameNameVariable() throws Exception {
runTest("idea/testData/inspectionsLocal/explicitThis/functionWithSameNameVariable.kt");
}
@TestMetadata("multipleReceivers.kt")
public void testMultipleReceivers() throws Exception {
runTest("idea/testData/inspectionsLocal/explicitThis/multipleReceivers.kt");
@@ -4265,6 +4275,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
public void testVariableWithSameName() throws Exception {
runTest("idea/testData/inspectionsLocal/explicitThis/variableWithSameName.kt");
}
@TestMetadata("variableWithSameNameFunction.kt")
public void testVariableWithSameNameFunction() throws Exception {
runTest("idea/testData/inspectionsLocal/explicitThis/variableWithSameNameFunction.kt");
}
}
@TestMetadata("idea/testData/inspectionsLocal/foldInitializerAndIfToElvis")