[Invariant Fix] Use structural descriptor equivalence in JsNamesClashChecker

It's needed for situation when we have JS module with expect declaration,
so we refine member scope for that expect declaration and try to
compare to function descriptors (e.g. of `equals` function) from
different class descriptors (expect and actual) and report diagnostic
about name clash. So, since we can earn descriptors from type refinement,
they can be not identical but still equals, so with should use structural
equality to comparing them
This commit is contained in:
Dmitry Savvinov
2019-07-25 18:46:26 +03:00
parent 9add14a2a1
commit ab9ff786d7
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.js.resolve.diagnostics package org.jetbrains.kotlin.js.resolve.diagnostics
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.isTypeRefinementEnabled
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
@@ -24,14 +26,20 @@ import org.jetbrains.kotlin.js.naming.SuggestedName
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner
import org.jetbrains.kotlin.types.refinement.TypeRefinement
class JsNameClashChecker(private val nameSuggestion: NameSuggestion) : DeclarationChecker { class JsNameClashChecker(
private val nameSuggestion: NameSuggestion,
private val languageVersionSettings: LanguageVersionSettings
) : DeclarationChecker {
companion object { companion object {
private val COMMON_DIAGNOSTICS = setOf( private val COMMON_DIAGNOSTICS = setOf(
Errors.REDECLARATION, Errors.REDECLARATION,
@@ -87,7 +95,12 @@ class JsNameClashChecker(private val nameSuggestion: NameSuggestion) : Declarati
val scope = getScope(overrideFqn.scope) val scope = getScope(overrideFqn.scope)
val name = overrideFqn.names.last() val name = overrideFqn.names.last()
val existing = scope[name] as? CallableMemberDescriptor val existing = scope[name] as? CallableMemberDescriptor
if (existing != null && existing != overrideFqn.descriptor && !isFakeOverridingNative(existing)) { val overrideDescriptor = overrideFqn.descriptor as? CallableMemberDescriptor
if (existing != null &&
overrideDescriptor != null &&
!areDescriptorsEquivalent(existing, overrideDescriptor) &&
!isFakeOverridingNative(existing)
) {
diagnosticHolder.report(ErrorsJs.JS_FAKE_NAME_CLASH.on(declaration, name, override, existing)) diagnosticHolder.report(ErrorsJs.JS_FAKE_NAME_CLASH.on(declaration, name, override, existing))
break break
} }
@@ -102,6 +115,20 @@ class JsNameClashChecker(private val nameSuggestion: NameSuggestion) : Declarati
} }
} }
private fun areDescriptorsEquivalent(
existing: CallableMemberDescriptor,
overrideDescriptor: CallableMemberDescriptor
): Boolean {
return if (!languageVersionSettings.isTypeRefinementEnabled) {
// Fast-path
existing == overrideDescriptor
} else {
// If refinement is enabled, we can get duplicate descriptors for one and the same members (as refinement re-creates
// descriptors), so, in this case, we have to compare descriptors structurally
DescriptorEquivalenceForOverrides.areCallableDescriptorsEquivalent(existing, overrideDescriptor)
}
}
private fun NameSuggestion.suggestAllPossibleNames(descriptor: DeclarationDescriptor): Collection<SuggestedName> = private fun NameSuggestion.suggestAllPossibleNames(descriptor: DeclarationDescriptor): Collection<SuggestedName> =
if (descriptor is CallableMemberDescriptor) { if (descriptor is CallableMemberDescriptor) {
val primary = suggest(descriptor) val primary = suggest(descriptor)