Support callable references in "implicit this" inspection
Second part of #KT-21510
This commit is contained in:
@@ -30,24 +30,37 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
|||||||
|
|
||||||
class ImplicitThisInspection : AbstractKotlinInspection() {
|
class ImplicitThisInspection : AbstractKotlinInspection() {
|
||||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : KtVisitorVoid() {
|
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) {
|
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||||
if (expression !is KtNameReferenceExpression) return
|
if (expression !is KtNameReferenceExpression) return
|
||||||
if (expression.parent is KtThisExpression) return
|
if (expression.parent is KtThisExpression) return
|
||||||
|
if (expression.parent is KtCallableReferenceExpression) return
|
||||||
if (expression.isSelectorOfDotQualifiedExpression()) return
|
if (expression.isSelectorOfDotQualifiedExpression()) return
|
||||||
val parent = expression.parent
|
val parent = expression.parent
|
||||||
if (parent is KtCallExpression && parent.isSelectorOfDotQualifiedExpression()) return
|
if (parent is KtCallExpression && parent.isSelectorOfDotQualifiedExpression()) return
|
||||||
|
|
||||||
val context = expression.analyze()
|
handle(expression, expression, ::CallFix)
|
||||||
val scope = expression.getResolutionScope(context) ?: return
|
}
|
||||||
|
|
||||||
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 receiverDescriptor = descriptor.extensionReceiverParameter ?: descriptor.dispatchReceiverParameter ?: return
|
||||||
val receiverType = receiverDescriptor.type
|
val receiverType = receiverDescriptor.type
|
||||||
|
|
||||||
val expressionFactory = scope.getFactoryForImplicitReceiverWithSubtypeOf(receiverType) ?: return
|
val expressionFactory = scope.getFactoryForImplicitReceiverWithSubtypeOf(receiverType) ?: return
|
||||||
val receiverText = if (expressionFactory.isImmediate) "this" else expressionFactory.expressionText
|
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 {
|
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 getFamilyName() = "Add explicit '$receiverText'"
|
||||||
|
|
||||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||||
@@ -67,4 +80,16 @@ class ImplicitThisInspection : AbstractKotlinInspection() {
|
|||||||
call.replace(factory.createExpressionByPattern("$0.$1", receiverText, call))
|
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);
|
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")
|
@TestMetadata("multipleReceivers.kt")
|
||||||
public void testMultipleReceivers() throws Exception {
|
public void testMultipleReceivers() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/implicitThis/multipleReceivers.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/implicitThis/multipleReceivers.kt");
|
||||||
@@ -797,6 +803,12 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("topLevel.kt")
|
||||||
public void testTopLevel() throws Exception {
|
public void testTopLevel() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/implicitThis/topLevel.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/implicitThis/topLevel.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user