Redundant companion reference: do not report 'values/valueOf' function in enum
#KT-34285 Fixed
This commit is contained in:
committed by
Yan Zhulanow
parent
2a841550d4
commit
55d55446c8
+111
-98
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.analysis.analyzeAsReplacement
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.intentions.isReferenceToBuiltInEnumFunction
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.references.resolveToDescriptors
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
@@ -50,112 +51,124 @@ class RedundantCompanionReferenceInspection : AbstractKotlinInspection() {
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun isRedundantCompanionReference(reference: KtReferenceExpression): Boolean {
|
||||
val parent = reference.parent as? KtDotQualifiedExpression ?: return false
|
||||
val grandParent = parent.parent
|
||||
val selectorExpression = parent.selectorExpression
|
||||
if (reference == selectorExpression && grandParent !is KtDotQualifiedExpression) return false
|
||||
if (parent.getStrictParentOfType<KtImportDirective>() != null) return false
|
||||
private fun isRedundantCompanionReference(reference: KtReferenceExpression): Boolean {
|
||||
val parent = reference.parent as? KtDotQualifiedExpression ?: return false
|
||||
val grandParent = parent.parent as? KtElement
|
||||
val selectorExpression = parent.selectorExpression
|
||||
if (reference == selectorExpression && grandParent !is KtDotQualifiedExpression) return false
|
||||
if (parent.getStrictParentOfType<KtImportDirective>() != null) return false
|
||||
|
||||
val objectDeclaration = reference.mainReference.resolve() as? KtObjectDeclaration ?: return false
|
||||
if (!objectDeclaration.isCompanion()) return false
|
||||
val referenceText = reference.text
|
||||
if (referenceText != objectDeclaration.name) return false
|
||||
if (reference != selectorExpression && referenceText == (selectorExpression as? KtNameReferenceExpression)?.text) return false
|
||||
val objectDeclaration = reference.mainReference.resolve() as? KtObjectDeclaration ?: return false
|
||||
if (!objectDeclaration.isCompanion()) return false
|
||||
val referenceText = reference.text
|
||||
if (referenceText != objectDeclaration.name) return false
|
||||
if (reference != selectorExpression && referenceText == (selectorExpression as? KtNameReferenceExpression)?.text) return false
|
||||
|
||||
val containingClass = objectDeclaration.containingClass() ?: return false
|
||||
if (reference.containingClass() != containingClass && reference == parent.receiverExpression) return false
|
||||
val context = reference.analyze()
|
||||
val containingClassDescriptor =
|
||||
context[BindingContext.DECLARATION_TO_DESCRIPTOR, containingClass] as? ClassDescriptor ?: return false
|
||||
when (val selectorDescriptor = selectorExpression?.getResolvedCall(context)?.resultingDescriptor) {
|
||||
is PropertyDescriptor -> {
|
||||
val name = selectorDescriptor.name
|
||||
if (containingClassDescriptor.findMemberVariable(name) != null) return false
|
||||
val containingClass = objectDeclaration.containingClass() ?: return false
|
||||
if (reference.containingClass() != containingClass && reference == parent.receiverExpression) return false
|
||||
if (parent.isReferenceToBuildInEnumFunctionInEnumClass(containingClass)) return false
|
||||
|
||||
val type = selectorDescriptor.type
|
||||
val javaGetter = containingClassDescriptor.findMemberFunction(
|
||||
Name.identifier(JvmAbi.getterName(name.asString()))
|
||||
)?.takeIf { f -> f is JavaMethodDescriptor || f.overriddenDescriptors.any { it is JavaMethodDescriptor } }
|
||||
if (javaGetter?.valueParameters?.isEmpty() == true && javaGetter.returnType?.makeNotNullable() == type) return false
|
||||
val context = reference.analyze()
|
||||
if (grandParent.isReferenceToClassOrObject(context)) return false
|
||||
|
||||
val variable = reference.getResolutionScope().findVariable(name, NoLookupLocation.FROM_IDE)
|
||||
if (variable != null && variable.isLocalOrExtension(containingClassDescriptor)) return false
|
||||
}
|
||||
is FunctionDescriptor -> {
|
||||
val name = selectorDescriptor.name
|
||||
if (containingClassDescriptor.findMemberFunction(name) != null) return false
|
||||
val function = reference.getResolutionScope().findFunction(name, NoLookupLocation.FROM_IDE)
|
||||
if (function != null && function.isLocalOrExtension(containingClassDescriptor)) return false
|
||||
}
|
||||
val containingClassDescriptor =
|
||||
context[BindingContext.DECLARATION_TO_DESCRIPTOR, containingClass] as? ClassDescriptor ?: return false
|
||||
if (containingClassDescriptor.hasSameNameMemberAs(selectorExpression, context)) return false
|
||||
|
||||
(reference as? KtSimpleNameExpression)?.getReceiverExpression()?.getQualifiedElementSelector()
|
||||
?.mainReference?.resolveToDescriptors(context)?.firstOrNull()
|
||||
?.let { if (it != containingClassDescriptor) return false }
|
||||
|
||||
if (selectorExpression is KtCallExpression && referenceText == selectorExpression.calleeExpression?.text) {
|
||||
val newExpression = KtPsiFactory(reference).createExpressionByPattern("$0", selectorExpression)
|
||||
val newContext = newExpression.analyzeAsReplacement(parent, context)
|
||||
val descriptor = newExpression.getResolvedCall(newContext)?.resultingDescriptor as? FunctionDescriptor
|
||||
if (descriptor?.isOperator == true) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun KtDotQualifiedExpression.isReferenceToBuildInEnumFunctionInEnumClass(containingClass: KtClass): Boolean {
|
||||
return containingClass.isEnum()
|
||||
&& (isReferenceToBuiltInEnumFunction() || (parent as? KtElement)?.isReferenceToBuiltInEnumFunction() == true)
|
||||
}
|
||||
|
||||
private fun KtElement?.isReferenceToClassOrObject(context: BindingContext): Boolean {
|
||||
if (this !is KtQualifiedExpression) return false
|
||||
val descriptor = getResolvedCall(context)?.resultingDescriptor
|
||||
return descriptor == null || descriptor is ConstructorDescriptor || descriptor is FakeCallableDescriptorForObject
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.hasSameNameMemberAs(expression: KtExpression?, context: BindingContext): Boolean {
|
||||
when (val descriptor = expression?.getResolvedCall(context)?.resultingDescriptor) {
|
||||
is PropertyDescriptor -> {
|
||||
val name = descriptor.name
|
||||
if (findMemberVariable(name) != null) return true
|
||||
|
||||
val type = descriptor.type
|
||||
val javaGetter = findMemberFunction(Name.identifier(JvmAbi.getterName(name.asString())))
|
||||
?.takeIf { f -> f is JavaMethodDescriptor || f.overriddenDescriptors.any { it is JavaMethodDescriptor } }
|
||||
if (javaGetter?.valueParameters?.isEmpty() == true && javaGetter.returnType?.makeNotNullable() == type) return true
|
||||
|
||||
val variable = expression.getResolutionScope().findVariable(name, NoLookupLocation.FROM_IDE)
|
||||
if (variable != null && variable.isLocalOrExtension(this)) return true
|
||||
}
|
||||
|
||||
(reference as? KtSimpleNameExpression)?.getReceiverExpression()?.getQualifiedElementSelector()
|
||||
?.mainReference?.resolveToDescriptors(context)?.firstOrNull()
|
||||
?.let { if (it != containingClassDescriptor) return false }
|
||||
|
||||
if (grandParent is KtQualifiedExpression) {
|
||||
val grandParentDescriptor = grandParent.getResolvedCall(context)?.resultingDescriptor ?: return false
|
||||
if (grandParentDescriptor is ConstructorDescriptor || grandParentDescriptor is FakeCallableDescriptorForObject) return false
|
||||
is FunctionDescriptor -> {
|
||||
val name = descriptor.name
|
||||
if (findMemberFunction(name) != null) return true
|
||||
val function = expression.getResolutionScope().findFunction(name, NoLookupLocation.FROM_IDE)
|
||||
if (function != null && function.isLocalOrExtension(this)) return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if (selectorExpression is KtCallExpression && referenceText == selectorExpression.calleeExpression?.text) {
|
||||
val newExpression = KtPsiFactory(reference).createExpressionByPattern("$0", selectorExpression)
|
||||
val newContext = newExpression.analyzeAsReplacement(parent, context)
|
||||
val descriptor = newExpression.getResolvedCall(newContext)?.resultingDescriptor as? FunctionDescriptor
|
||||
if (descriptor?.isOperator == true) return false
|
||||
private fun <D : MemberDescriptor> ClassDescriptor.findMemberByName(name: Name, find: ClassDescriptor.(Name) -> D?): D? {
|
||||
val member = find(name)
|
||||
if (member != null) return member
|
||||
|
||||
val memberInSuperClass = getSuperClassNotAny()?.findMemberByName(name, find)
|
||||
if (memberInSuperClass != null) return memberInSuperClass
|
||||
|
||||
getSuperInterfaces().forEach {
|
||||
val memberInInterface = it.findMemberByName(name, find)
|
||||
if (memberInInterface != null) return memberInInterface
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.findMemberVariable(name: Name): PropertyDescriptor? = findMemberByName(name) {
|
||||
unsubstitutedMemberScope.getContributedVariables(it, NoLookupLocation.FROM_IDE).firstOrNull()
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.findMemberFunction(name: Name): FunctionDescriptor? = findMemberByName(name) {
|
||||
unsubstitutedMemberScope.getContributedFunctions(it, NoLookupLocation.FROM_IDE).firstOrNull()
|
||||
}
|
||||
|
||||
private fun CallableDescriptor.isLocalOrExtension(extensionClassDescriptor: ClassDescriptor): Boolean {
|
||||
return visibility == DescriptorVisibilities.LOCAL ||
|
||||
extensionReceiverParameter?.type?.constructor?.declarationDescriptor == extensionClassDescriptor
|
||||
}
|
||||
|
||||
private class RemoveRedundantCompanionReferenceFix : LocalQuickFix {
|
||||
override fun getName() = KotlinBundle.message("remove.redundant.companion.reference.fix.text")
|
||||
|
||||
override fun getFamilyName() = name
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val expression = descriptor.psiElement as? KtReferenceExpression ?: return
|
||||
removeRedundantCompanionReference(expression)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun removeRedundantCompanionReference(expression: KtReferenceExpression) {
|
||||
val parent = expression.parent as? KtDotQualifiedExpression ?: return
|
||||
val selector = parent.selectorExpression ?: return
|
||||
val receiver = parent.receiverExpression
|
||||
if (expression == receiver) parent.replace(selector) else parent.replace(receiver)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <D : MemberDescriptor> ClassDescriptor.findMemberByName(name: Name, find: ClassDescriptor.(Name) -> D?): D? {
|
||||
val member = find(name)
|
||||
if (member != null) return member
|
||||
|
||||
val memberInSuperClass = getSuperClassNotAny()?.findMemberByName(name, find)
|
||||
if (memberInSuperClass != null) return memberInSuperClass
|
||||
|
||||
getSuperInterfaces().forEach {
|
||||
val memberInInterface = it.findMemberByName(name, find)
|
||||
if (memberInInterface != null) return memberInInterface
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.findMemberVariable(name: Name): PropertyDescriptor? = findMemberByName(name) {
|
||||
unsubstitutedMemberScope.getContributedVariables(it, NoLookupLocation.FROM_IDE).firstOrNull()
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.findMemberFunction(name: Name): FunctionDescriptor? = findMemberByName(name) {
|
||||
unsubstitutedMemberScope.getContributedFunctions(it, NoLookupLocation.FROM_IDE).firstOrNull()
|
||||
}
|
||||
|
||||
private fun CallableDescriptor.isLocalOrExtension(extensionClassDescriptor: ClassDescriptor): Boolean {
|
||||
return visibility == DescriptorVisibilities.LOCAL ||
|
||||
extensionReceiverParameter?.type?.constructor?.declarationDescriptor == extensionClassDescriptor
|
||||
}
|
||||
|
||||
class RemoveRedundantCompanionReferenceFix : LocalQuickFix {
|
||||
override fun getName() = KotlinBundle.message("remove.redundant.companion.reference.fix.text")
|
||||
|
||||
override fun getFamilyName() = name
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val expression = descriptor.psiElement as? KtReferenceExpression ?: return
|
||||
removeRedundantCompanionReference(expression)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun removeRedundantCompanionReference(expression: KtReferenceExpression) {
|
||||
val parent = expression.parent as? KtDotQualifiedExpression ?: return
|
||||
val selector = parent.selectorExpression ?: return
|
||||
val receiver = parent.receiverExpression
|
||||
if (expression == receiver) parent.replace(selector) else parent.replace(receiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-8
@@ -19,7 +19,7 @@ import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.core.compareDescriptors
|
||||
import org.jetbrains.kotlin.idea.core.unwrapIfFakeOverride
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.isReferenceToBuiltInEnumFunction
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.references.resolveToDescriptors
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
@@ -36,10 +36,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import javax.swing.JComponent
|
||||
|
||||
class RemoveRedundantQualifierNameInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
|
||||
companion object {
|
||||
private val ENUM_STATIC_METHODS = listOf("values", "valueOf")
|
||||
}
|
||||
|
||||
/**
|
||||
* In order to detect that `foo()` and `GrandBase.foo()` point to the same method,
|
||||
* we need to unwrap fake overrides from descriptors. If we don't do that, they will
|
||||
@@ -77,7 +73,7 @@ class RemoveRedundantQualifierNameInspection : AbstractKotlinInspection(), Clean
|
||||
|
||||
if (receiverReference?.safeAs<KtClass>()?.isEnum() == true
|
||||
&& expressionForAnalyze.getParentOfTypesAndPredicate(true, KtClass::class.java) { it.isEnum() } != receiverReference
|
||||
&& (expressionForAnalyze.isEnumStaticMethodCall() || expressionForAnalyze.isCompanionObjectReference())
|
||||
&& (expressionForAnalyze.isReferenceToBuiltInEnumFunction() || expressionForAnalyze.isCompanionObjectReference())
|
||||
) return
|
||||
|
||||
val context = originalExpression.analyze()
|
||||
@@ -107,8 +103,6 @@ class RemoveRedundantQualifierNameInspection : AbstractKotlinInspection(), Clean
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtDotQualifiedExpression.isEnumStaticMethodCall() = callExpression?.calleeExpression?.text in ENUM_STATIC_METHODS
|
||||
|
||||
private fun KtDotQualifiedExpression.isCompanionObjectReference(): Boolean {
|
||||
val selector = receiverExpression.safeAs<KtDotQualifiedExpression>()?.selectorExpression ?: selectorExpression
|
||||
return selector?.referenceExpression()?.mainReference?.resolve()?.safeAs<KtObjectDeclaration>()?.isCompanion() == true
|
||||
|
||||
@@ -48,8 +48,7 @@ import org.jetbrains.kotlin.idea.core.script.configuration.DefaultScriptingSuppo
|
||||
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
|
||||
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
|
||||
import org.jetbrains.kotlin.idea.inspections.collections.isCalling
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.isReferenceToBuiltInEnumFunction
|
||||
import org.jetbrains.kotlin.idea.intentions.isFinalizeMethod
|
||||
import org.jetbrains.kotlin.idea.isMainFunction
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
@@ -69,15 +68,12 @@ import org.jetbrains.kotlin.idea.util.hasActualsFor
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.isInlineClassType
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import java.awt.GridBagConstraints
|
||||
import java.awt.GridBagLayout
|
||||
@@ -92,10 +88,6 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
||||
|
||||
private val KOTLIN_ADDITIONAL_ANNOTATIONS = listOf("kotlin.test.*", "kotlin.js.JsExport")
|
||||
|
||||
private val KOTLIN_BUILTIN_ENUM_FUNCTIONS = listOf(FqName("kotlin.enumValues"), FqName("kotlin.enumValueOf"))
|
||||
|
||||
private val ENUM_STATIC_METHODS = listOf("values", "valueOf")
|
||||
|
||||
private fun KtDeclaration.hasKotlinAdditionalAnnotation() =
|
||||
this is KtNamedDeclaration && checkAnnotatedUsingPatterns(this, KOTLIN_ADDITIONAL_ANNOTATIONS)
|
||||
|
||||
@@ -470,30 +462,13 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
||||
}
|
||||
|
||||
private fun hasBuiltInEnumFunctionReference(reference: PsiReference): Boolean {
|
||||
return when (val parent = reference.element.getParentOfTypes(
|
||||
val parent = reference.element.getParentOfTypes(
|
||||
true,
|
||||
KtTypeReference::class.java,
|
||||
KtQualifiedExpression::class.java,
|
||||
KtCallableReferenceExpression::class.java
|
||||
)) {
|
||||
is KtTypeReference -> {
|
||||
val target = (parent.getStrictParentOfType<KtTypeArgumentList>() ?: parent)
|
||||
.getParentOfTypes(true, KtCallExpression::class.java, KtCallableDeclaration::class.java)
|
||||
when (target) {
|
||||
is KtCallExpression -> target.isCalling(KOTLIN_BUILTIN_ENUM_FUNCTIONS)
|
||||
is KtCallableDeclaration -> {
|
||||
target.anyDescendantOfType<KtCallExpression> {
|
||||
val context = it.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
|
||||
it.isCalling(KOTLIN_BUILTIN_ENUM_FUNCTIONS, context) && it.isUsedAsExpression(context)
|
||||
}
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
is KtQualifiedExpression -> parent.callExpression?.calleeExpression?.text in ENUM_STATIC_METHODS
|
||||
is KtCallableReferenceExpression -> parent.callableReference.text in ENUM_STATIC_METHODS
|
||||
else -> false
|
||||
}
|
||||
) ?: return false
|
||||
return parent.isReferenceToBuiltInEnumFunction()
|
||||
}
|
||||
|
||||
private fun checkPrivateDeclaration(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor?): Boolean {
|
||||
|
||||
@@ -17,18 +17,19 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.core.setType
|
||||
import org.jetbrains.kotlin.idea.inspections.collections.isCalling
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.references.resolveToDescriptors
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.ArrayFqNames
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
@@ -368,3 +369,30 @@ fun KotlinType.reflectToRegularFunctionType(): KotlinType {
|
||||
|
||||
val CallableDescriptor.isInvokeOperator: Boolean
|
||||
get() = this is FunctionDescriptor && isOperator && name == OperatorNameConventions.INVOKE
|
||||
|
||||
private val KOTLIN_BUILTIN_ENUM_FUNCTIONS = listOf(FqName("kotlin.enumValues"), FqName("kotlin.enumValueOf"))
|
||||
|
||||
private val ENUM_STATIC_METHODS = listOf("values", "valueOf")
|
||||
|
||||
fun KtElement.isReferenceToBuiltInEnumFunction(): Boolean {
|
||||
return when (this) {
|
||||
is KtTypeReference -> {
|
||||
val target = (parent.getStrictParentOfType<KtTypeArgumentList>() ?: this)
|
||||
.getParentOfTypes(true, KtCallExpression::class.java, KtCallableDeclaration::class.java)
|
||||
when (target) {
|
||||
is KtCallExpression -> target.isCalling(KOTLIN_BUILTIN_ENUM_FUNCTIONS)
|
||||
is KtCallableDeclaration -> {
|
||||
target.anyDescendantOfType<KtCallExpression> {
|
||||
val context = it.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
|
||||
it.isCalling(KOTLIN_BUILTIN_ENUM_FUNCTIONS, context) && it.isUsedAsExpression(context)
|
||||
}
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
is KtQualifiedExpression -> this.callExpression?.calleeExpression?.text in ENUM_STATIC_METHODS
|
||||
is KtCallExpression -> this.calleeExpression?.text in ENUM_STATIC_METHODS
|
||||
is KtCallableReferenceExpression -> this.callableReference.text in ENUM_STATIC_METHODS
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// PROBLEM: none
|
||||
|
||||
enum class A {
|
||||
TEST;
|
||||
|
||||
companion object {
|
||||
fun valueOf(name: String) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
A.<caret>Companion.valueOf("TEST")
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// PROBLEM: none
|
||||
|
||||
enum class A {
|
||||
TEST;
|
||||
|
||||
companion object {
|
||||
fun valueOf(name: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
<caret>Companion.valueOf("TEST")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// PROBLEM: none
|
||||
|
||||
enum class A {
|
||||
TEST;
|
||||
|
||||
companion object {
|
||||
fun values() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
A.<caret>Companion.values()
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// PROBLEM: none
|
||||
|
||||
enum class A {
|
||||
TEST;
|
||||
|
||||
companion object {
|
||||
fun values() {
|
||||
}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
<caret>Companion.values()
|
||||
}
|
||||
}
|
||||
+20
@@ -7417,6 +7417,26 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/directCompanion.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumValueOf.kt")
|
||||
public void testEnumValueOf() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/enumValueOf.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumValueOf2.kt")
|
||||
public void testEnumValueOf2() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/enumValueOf2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumValues.kt")
|
||||
public void testEnumValues() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/enumValues.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumValues2.kt")
|
||||
public void testEnumValues2() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/enumValues2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionReference.kt")
|
||||
public void testFunctionReference() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/functionReference.kt");
|
||||
|
||||
Reference in New Issue
Block a user