Support callable references in "implicit this" inspection

Second part of #KT-21510
This commit is contained in:
Kirill
2017-12-17 23:38:50 +03:00
committed by Mikhail Glukhikh
parent 74e5a9425a
commit 7842fa4796
6 changed files with 70 additions and 5 deletions
@@ -30,24 +30,37 @@ import org.jetbrains.kotlin.resolve.BindingContext
class ImplicitThisInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : KtVisitorVoid() {
override fun visitCallableReferenceExpression(expression: KtCallableReferenceExpression) {
if (expression.receiverExpression != null) return
handle(expression, expression.callableReference, ::CallableReferenceFix)
}
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
if (expression !is KtNameReferenceExpression) return
if (expression.parent is KtThisExpression) return
if (expression.parent is KtCallableReferenceExpression) return
if (expression.isSelectorOfDotQualifiedExpression()) return
val parent = expression.parent
if (parent is KtCallExpression && parent.isSelectorOfDotQualifiedExpression()) return
val context = expression.analyze()
val scope = expression.getResolutionScope(context) ?: return
handle(expression, expression, ::CallFix)
}
val descriptor = context[BindingContext.REFERENCE_TARGET, expression] as? CallableDescriptor ?: return
private fun handle(expression: KtExpression, reference: KtReferenceExpression, fixFactory: (String) -> LocalQuickFix) {
val context = reference.analyze()
val scope = reference.getResolutionScope(context) ?: return
val descriptor = context[BindingContext.REFERENCE_TARGET, reference] as? CallableDescriptor ?: return
val receiverDescriptor = descriptor.extensionReceiverParameter ?: descriptor.dispatchReceiverParameter ?: return
val receiverType = receiverDescriptor.type
val expressionFactory = scope.getFactoryForImplicitReceiverWithSubtypeOf(receiverType) ?: return
val receiverText = if (expressionFactory.isImmediate) "this" else expressionFactory.expressionText
holder.registerProblem(expression, "Add explicit '$receiverText'", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, Fix(receiverText))
val fix = fixFactory(receiverText)
holder.registerProblem(expression, "Add explicit '$receiverText'", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, fix)
}
private fun KtExpression.isSelectorOfDotQualifiedExpression(): Boolean {
@@ -56,7 +69,7 @@ class ImplicitThisInspection : AbstractKotlinInspection() {
}
}
private class Fix(private val receiverText: String) : LocalQuickFix {
private class CallFix(private val receiverText: String) : LocalQuickFix {
override fun getFamilyName() = "Add explicit '$receiverText'"
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
@@ -67,4 +80,16 @@ class ImplicitThisInspection : AbstractKotlinInspection() {
call.replace(factory.createExpressionByPattern("$0.$1", receiverText, call))
}
}
private class CallableReferenceFix(private val receiverText: String) : LocalQuickFix {
override fun getFamilyName() = "Add explicit '$receiverText'"
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val expression = descriptor.psiElement as? KtCallableReferenceExpression ?: return
val factory = KtPsiFactory(project)
val reference = expression.callableReference
expression.replace(factory.createExpressionByPattern("$0::$1", receiverText, reference))
}
}
}
@@ -0,0 +1,7 @@
class Foo {
fun s() = ""
fun test() {
<caret>::s
}
}
@@ -0,0 +1,7 @@
class Foo {
fun s() = ""
fun test() {
<caret>this::s
}
}
@@ -0,0 +1,7 @@
class Foo {
val s = ""
fun test() {
<caret>::s
}
}
@@ -0,0 +1,7 @@
class Foo {
val s = ""
fun test() {
<caret>this::s
}
}
@@ -767,6 +767,12 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
doTest(fileName);
}
@TestMetadata("functionReference.kt")
public void testFunctionReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/implicitThis/functionReference.kt");
doTest(fileName);
}
@TestMetadata("multipleReceivers.kt")
public void testMultipleReceivers() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/implicitThis/multipleReceivers.kt");
@@ -797,6 +803,12 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
doTest(fileName);
}
@TestMetadata("propertyReference.kt")
public void testPropertyReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/implicitThis/propertyReference.kt");
doTest(fileName);
}
@TestMetadata("topLevel.kt")
public void testTopLevel() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/implicitThis/topLevel.kt");