From 74e5a9425a7627ed02865c1dcc8d18e559b89cb6 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sun, 17 Dec 2017 20:02:36 +0300 Subject: [PATCH] Support callable references in "explicit this" inspection So #KT-21510 Fixed --- .../inspections/ExplicitThisInspection.kt | 39 ++++++++++++------- .../explicitThis/functionReference.kt | 7 ++++ .../explicitThis/functionReference.kt.after | 7 ++++ .../explicitThis/propertyReference.kt | 7 ++++ .../explicitThis/propertyReference.kt.after | 7 ++++ .../LocalInspectionTestGenerated.java | 12 ++++++ 6 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 idea/testData/inspectionsLocal/explicitThis/functionReference.kt create mode 100644 idea/testData/inspectionsLocal/explicitThis/functionReference.kt.after create mode 100644 idea/testData/inspectionsLocal/explicitThis/propertyReference.kt create mode 100644 idea/testData/inspectionsLocal/explicitThis/propertyReference.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ExplicitThisInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ExplicitThisInspection.kt index ed6898fe51c..803ad36388e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ExplicitThisInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ExplicitThisInspection.kt @@ -21,35 +21,44 @@ import com.intellij.codeInspection.ProblemDescriptor 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.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.project.languageVersionSettings 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.scopes.LexicalScope class ExplicitThisInspection : AbstractKotlinInspection() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : KtVisitorVoid() { + override fun visitCallableReferenceExpression(expression: KtCallableReferenceExpression) { + if (!expression.languageVersionSettings.supportsFeature(CallableReferencesToClassMembersWithEmptyLHS)) return + + val thisExpression = expression.receiverExpression as? KtThisExpression ?: return + val selectorExpression = expression.callableReference + + handle(expression, thisExpression, selectorExpression) + } + override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) { val thisExpression = expression.receiverExpression as? KtThisExpression ?: return + val selectorExpression = expression.selectorExpression as? KtReferenceExpression ?: return + handle(expression, thisExpression, selectorExpression) + } + + private fun handle(expression: KtExpression, thisExpression: KtThisExpression, reference: KtReferenceExpression) { val context = expression.analyze() val scope = expression.getResolutionScope(context) ?: return - val selectorExpression = expression.selectorExpression as? KtReferenceExpression ?: return - val referenceExpression = selectorExpression as? KtNameReferenceExpression - ?: selectorExpression.getChildOfType() + val referenceExpression = reference as? KtNameReferenceExpression + ?: reference.getChildOfType() ?: return - val scopeFunction = when (selectorExpression) { - is KtNameReferenceExpression -> LexicalScope::getAllAccessibleVariables - is KtCallExpression -> LexicalScope::getAllAccessibleFunctions - else -> return - } - //we avoid overload-related problems by enforcing that there is only one candidate - val candidates = scopeFunction(scope, referenceExpression.getReferencedNameAsName()) + val name = referenceExpression.getReferencedNameAsName() + val candidates = scope.getAllAccessibleVariables(name) + scope.getAllAccessibleFunctions(name) if (candidates.size != 1) return @@ -73,8 +82,12 @@ class ExplicitThisInspection : AbstractKotlinInspection() { override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val thisExpression = descriptor.psiElement as? KtThisExpression ?: return - val parent = thisExpression.parent as? KtDotQualifiedExpression ?: return - parent.replace(parent.selectorExpression ?: return) + val parent = thisExpression.parent + + when (parent) { + is KtDotQualifiedExpression -> parent.replace(parent.selectorExpression ?: return) + is KtCallableReferenceExpression -> thisExpression.delete() + } } } } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/explicitThis/functionReference.kt b/idea/testData/inspectionsLocal/explicitThis/functionReference.kt new file mode 100644 index 00000000000..d5e5199f4fb --- /dev/null +++ b/idea/testData/inspectionsLocal/explicitThis/functionReference.kt @@ -0,0 +1,7 @@ +class Foo { + fun s() = "" + + fun test() { + this::s + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/explicitThis/functionReference.kt.after b/idea/testData/inspectionsLocal/explicitThis/functionReference.kt.after new file mode 100644 index 00000000000..f80cf407505 --- /dev/null +++ b/idea/testData/inspectionsLocal/explicitThis/functionReference.kt.after @@ -0,0 +1,7 @@ +class Foo { + fun s() = "" + + fun test() { + ::s + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/explicitThis/propertyReference.kt b/idea/testData/inspectionsLocal/explicitThis/propertyReference.kt new file mode 100644 index 00000000000..ff1a6acaf71 --- /dev/null +++ b/idea/testData/inspectionsLocal/explicitThis/propertyReference.kt @@ -0,0 +1,7 @@ +class Foo { + val s = "" + + fun test() { + this::s + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/explicitThis/propertyReference.kt.after b/idea/testData/inspectionsLocal/explicitThis/propertyReference.kt.after new file mode 100644 index 00000000000..b287819c655 --- /dev/null +++ b/idea/testData/inspectionsLocal/explicitThis/propertyReference.kt.after @@ -0,0 +1,7 @@ +class Foo { + val s = "" + + fun test() { + ::s + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 9072ec61861..ee5e66cc76b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -641,6 +641,12 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { doTest(fileName); } + @TestMetadata("functionReference.kt") + public void testFunctionReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/explicitThis/functionReference.kt"); + doTest(fileName); + } + @TestMetadata("multipleReceivers.kt") public void testMultipleReceivers() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/explicitThis/multipleReceivers.kt"); @@ -683,6 +689,12 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { doTest(fileName); } + @TestMetadata("propertyReference.kt") + public void testPropertyReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/explicitThis/propertyReference.kt"); + doTest(fileName); + } + @TestMetadata("variableWithSameName.kt") public void testVariableWithSameName() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/explicitThis/variableWithSameName.kt");