"Redundant Companion": Don't suggest if same callable name is in use

So #KT-24425 Fixed
This commit is contained in:
Toshiaki Kameyama
2018-06-06 16:37:27 +03:00
committed by Mikhail Glukhikh
parent a2435ff3b0
commit 5ad98a139d
15 changed files with 252 additions and 2 deletions
@@ -11,24 +11,59 @@ import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.psi.psiUtil.findPropertyByName
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.scopes.utils.findFunction
import org.jetbrains.kotlin.resolve.scopes.utils.findVariable
class RedundantCompanionReferenceInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return referenceExpressionVisitor(fun(expression) {
val parent = expression.parent as? KtDotQualifiedExpression ?: return
if (expression == parent.selectorExpression && parent.parent !is KtDotQualifiedExpression) return
val selectorExpression = parent.selectorExpression
if (expression == selectorExpression && parent.parent !is KtDotQualifiedExpression) return
if (parent.getStrictParentOfType<KtImportDirective>() != null) return
val objectDeclaration = expression.mainReference.resolve() as? KtObjectDeclaration ?: return
if (!objectDeclaration.isCompanion()) return
if (expression.text != objectDeclaration.name) return
val containingClass = objectDeclaration.containingClass() ?: return
val selectorDescriptor = selectorExpression?.getCallableDescriptor()
when (selectorDescriptor) {
is PropertyDescriptor -> {
val name = selectorDescriptor.name
if (containingClass.findPropertyByName(name.asString()) != null) return
val variable = expression.getResolutionScope().findVariable(name, NoLookupLocation.FROM_IDE)
if (variable.isLocalOrExtension(containingClass)) return
}
is FunctionDescriptor -> {
val name = selectorDescriptor.name
val function = containingClass.findFunctionByName(name.asString())?.descriptor
?: expression.getResolutionScope().findFunction(name, NoLookupLocation.FROM_IDE)?.takeIf {
it.isLocalOrExtension(containingClass)
}
if (function is FunctionDescriptor) {
val functionParams = function.valueParameters
val calleeParams =
(selectorExpression as? KtCallExpression)?.calleeExpression?.getCallableDescriptor()?.valueParameters.orEmpty()
if (functionParams.size == calleeParams.size &&
functionParams.zip(calleeParams).all { it.first.type == it.second.type }
) return
}
}
}
val grandParent = parent.parent as? KtQualifiedExpression
if (grandParent != null) {
val grandParentDescriptor = grandParent.resolveToCall()?.resultingDescriptor ?: return
@@ -45,6 +80,15 @@ class RedundantCompanionReferenceInspection : AbstractKotlinInspection() {
}
}
private fun KtClass.findFunctionByName(name: String): KtDeclaration? {
return declarations.firstOrNull { it is KtNamedFunction && it.name == name }
}
private fun CallableDescriptor?.isLocalOrExtension(extensionClass: KtClass): Boolean {
return this?.visibility == Visibilities.LOCAL
|| this?.extensionReceiverParameter?.type?.constructor?.declarationDescriptor == extensionClass.descriptor
}
private class RemoveRedundantCompanionReferenceFix : LocalQuickFix {
override fun getName() = "Remove redundant Companion reference"
@@ -0,0 +1,11 @@
class Test {
companion object {
fun f(x: Int, y: Int) = 1
}
fun f(x: Int, y: String) = 2
fun test() {
<caret>Companion.f(1, 2)
}
}
@@ -0,0 +1,11 @@
class Test {
companion object {
fun f(x: Int, y: Int) = 1
}
fun f(x: Int, y: String) = 2
fun test() {
f(1, 2)
}
}
@@ -0,0 +1,13 @@
// PROBLEM: none
class Test {
companion object {
fun extentionFun() = 1
}
fun test() {
<caret>Companion.extentionFun()
}
}
fun Test.extentionFun() = 2
@@ -0,0 +1,14 @@
// PROBLEM: none
class Test {
companion object {
val extentionVar = 1
}
fun test() {
<caret>Companion.extentionVar
}
}
val Test.extentionVar: Int
get() = 2
@@ -0,0 +1,11 @@
class Test {
companion object {
fun globalFun() = 1
}
fun test() {
<caret>Companion.globalFun()
}
}
fun globalFun() = 2
@@ -0,0 +1,11 @@
class Test {
companion object {
fun globalFun() = 1
}
fun test() {
globalFun()
}
}
fun globalFun() = 2
@@ -0,0 +1,11 @@
class Test {
companion object {
val globalVar = 1
}
fun test() {
<caret>Companion.globalVar
}
}
val globalVar = 2
@@ -0,0 +1,11 @@
class Test {
companion object {
val globalVar = 1
}
fun test() {
globalVar
}
}
val globalVar = 2
@@ -0,0 +1,13 @@
// PROBLEM: none
class Test {
companion object {
fun localFun() = 1
}
fun test() {
fun localFun() = 2
<caret>Companion.localFun()
}
}
@@ -0,0 +1,13 @@
// PROBLEM: none
class Test {
companion object {
val localVar = 1
}
fun test() {
var localVar = 2
<caret>Companion.localVar
}
}
@@ -0,0 +1,11 @@
// PROBLEM: none
class Test {
companion object {
val localVar = 1
}
fun test(localVar: Int) {
<caret>Companion.localVar
}
}
@@ -0,0 +1,13 @@
// PROBLEM: none
class Test {
companion object {
fun memberFun(x: Int) = 1
}
fun memberFun(x: Int) = 2
fun test() {
<caret>Companion.memberFun(0)
}
}
@@ -0,0 +1,13 @@
// PROBLEM: none
class Test {
companion object {
val memberVar = 1
}
val memberVar = 2
fun test() {
<caret>Companion.memberVar
}
}
@@ -3024,6 +3024,56 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
public void testOnlyCompanion() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/onlyCompanion.kt");
}
@TestMetadata("sameNameDifferentArgsFunction.kt")
public void testSameNameDifferentArgsFunction() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/sameNameDifferentArgsFunction.kt");
}
@TestMetadata("sameNameExtensionFunction.kt")
public void testSameNameExtensionFunction() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/sameNameExtensionFunction.kt");
}
@TestMetadata("sameNameExtensionVariable.kt")
public void testSameNameExtensionVariable() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/sameNameExtensionVariable.kt");
}
@TestMetadata("sameNameGlobalFunction.kt")
public void testSameNameGlobalFunction() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/sameNameGlobalFunction.kt");
}
@TestMetadata("sameNameGlobalVariable.kt")
public void testSameNameGlobalVariable() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/sameNameGlobalVariable.kt");
}
@TestMetadata("sameNameLocalFunction.kt")
public void testSameNameLocalFunction() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/sameNameLocalFunction.kt");
}
@TestMetadata("sameNameLocalVariable.kt")
public void testSameNameLocalVariable() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/sameNameLocalVariable.kt");
}
@TestMetadata("sameNameLocalVariable2.kt")
public void testSameNameLocalVariable2() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/sameNameLocalVariable2.kt");
}
@TestMetadata("sameNameMemberFunction.kt")
public void testSameNameMemberFunction() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/sameNameMemberFunction.kt");
}
@TestMetadata("sameNameMemberVariable.kt")
public void testSameNameMemberVariable() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/sameNameMemberVariable.kt");
}
}
@TestMetadata("idea/testData/inspectionsLocal/redundantExplicitType")