[FIR JS] Support JS_NAME_CLASH and JS_FAKE_NAME_CLASH diagnostics
^KT-59425 Fixed ^KT-59370 Fixed
This commit is contained in:
committed by
Space Team
parent
5ac277d058
commit
6bb939c6cb
+17
-38
@@ -10,12 +10,12 @@ package org.jetbrains.kotlin.fir.analysis.js.checkers
|
|||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.descriptors.effectiveVisibility
|
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.utils.isActual
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
|
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isExternal
|
import org.jetbrains.kotlin.fir.declarations.utils.isExternal
|
||||||
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
|
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
|
||||||
@@ -101,13 +101,26 @@ fun FirBasedSymbol<*>.isNativeInterface(session: FirSession): Boolean {
|
|||||||
return isNativeObject(session) && (fir as? FirClass)?.isInterface == true
|
return isNativeObject(session) && (fir as? FirClass)?.isInterface == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private val FirBasedSymbol<*>.isExpect
|
fun FirBasedSymbol<*>.isLibraryObject(session: FirSession): Boolean {
|
||||||
|
return hasAnnotationOrInsideAnnotatedClass(JsStandardClassIds.Annotations.JsLibrary, session)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FirBasedSymbol<*>.isPresentInGeneratedCode(session: FirSession) = !isNativeObject(session) && !isLibraryObject(session)
|
||||||
|
|
||||||
|
internal val FirBasedSymbol<*>.isExpect
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
is FirCallableSymbol<*> -> isExpect
|
is FirCallableSymbol<*> -> isExpect
|
||||||
is FirClassSymbol<*> -> isExpect
|
is FirClassSymbol<*> -> isExpect
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val FirBasedSymbol<*>.isActual
|
||||||
|
get() = when (this) {
|
||||||
|
is FirCallableSymbol<*> -> isActual
|
||||||
|
is FirClassSymbol<*> -> isActual
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
fun FirBasedSymbol<*>.isPredefinedObject(session: FirSession): Boolean {
|
fun FirBasedSymbol<*>.isPredefinedObject(session: FirSession): Boolean {
|
||||||
if (fir is FirMemberDeclaration && isExpect) return true
|
if (fir is FirMemberDeclaration && isExpect) return true
|
||||||
if (isEffectivelyExternalMember(session)) return true
|
if (isEffectivelyExternalMember(session)) return true
|
||||||
@@ -154,6 +167,8 @@ fun FirBasedSymbol<*>.isPredefinedObject(context: CheckerContext) = isPredefined
|
|||||||
|
|
||||||
fun FirBasedSymbol<*>.isExportedObject(context: CheckerContext) = isExportedObject(context.session)
|
fun FirBasedSymbol<*>.isExportedObject(context: CheckerContext) = isExportedObject(context.session)
|
||||||
|
|
||||||
|
fun FirBasedSymbol<*>.isLibraryObject(context: CheckerContext) = isLibraryObject(context.session)
|
||||||
|
|
||||||
internal fun FirClass.superClassNotAny(session: FirSession) = superConeTypes
|
internal fun FirClass.superClassNotAny(session: FirSession) = superConeTypes
|
||||||
.filterNot { it.isAny || it.isNullableAny }
|
.filterNot { it.isAny || it.isNullableAny }
|
||||||
.find { it.toSymbol(session)?.classKind == ClassKind.CLASS }
|
.find { it.toSymbol(session)?.classKind == ClassKind.CLASS }
|
||||||
@@ -161,39 +176,3 @@ internal fun FirClass.superClassNotAny(session: FirSession) = superConeTypes
|
|||||||
internal fun getRootClassLikeSymbolOrSelf(symbol: FirBasedSymbol<*>, session: FirSession): FirBasedSymbol<*> {
|
internal fun getRootClassLikeSymbolOrSelf(symbol: FirBasedSymbol<*>, session: FirSession): FirBasedSymbol<*> {
|
||||||
return symbol.getContainingClassSymbol(session)?.let { getRootClassLikeSymbolOrSelf(it, session) } ?: symbol
|
return symbol.getContainingClassSymbol(session)?.let { getRootClassLikeSymbolOrSelf(it, session) } ?: symbol
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun FirBasedSymbol<*>.getStableNameInJavaScript(session: FirSession): String? {
|
|
||||||
val jsName = getJsName(session)
|
|
||||||
if (jsName != null) {
|
|
||||||
return jsName
|
|
||||||
}
|
|
||||||
val hasStableNameInJavaScript = when {
|
|
||||||
isEffectivelyExternal(session) -> true
|
|
||||||
isExportedObject(session) -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: rethink in KT-60554
|
|
||||||
val hasPublicName = when (this) {
|
|
||||||
is FirClassLikeSymbol -> !isLocal
|
|
||||||
is FirCallableSymbol -> {
|
|
||||||
val parentClass = getContainingClassSymbol(session)
|
|
||||||
if (parentClass != null) {
|
|
||||||
when (visibility) {
|
|
||||||
is Visibilities.Public -> true
|
|
||||||
is Visibilities.Protected -> !parentClass.isFinal && parentClass.visibility.isPublicAPI
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
!callableId.isLocal && effectiveVisibility.publicApi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasStableNameInJavaScript || hasPublicName) {
|
|
||||||
return (fir as? FirMemberDeclaration)?.nameOrSpecialName?.identifierOrNullIfSpecial
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.fir.analysis.js.checkers
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||||
|
|
||||||
|
internal data class FirJsStableName(
|
||||||
|
val name: String,
|
||||||
|
val symbol: FirBasedSymbol<*>,
|
||||||
|
val canBeMangled: Boolean,
|
||||||
|
val isPresentInGeneratedCode: Boolean,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private fun hasPublicName(symbol: FirBasedSymbol<*>, session: FirSession): Boolean {
|
||||||
|
return when (symbol) {
|
||||||
|
is FirClassLikeSymbol -> !symbol.isLocal
|
||||||
|
is FirCallableSymbol -> {
|
||||||
|
val parentClass = symbol.getContainingClassSymbol(session)
|
||||||
|
if (parentClass != null) {
|
||||||
|
when (symbol.visibility) {
|
||||||
|
is Visibilities.Public -> true
|
||||||
|
is Visibilities.Protected -> !parentClass.isFinal && parentClass.visibility.isPublicAPI
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
!symbol.callableId.isLocal && symbol.effectiveVisibility.publicApi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(SymbolInternals::class)
|
||||||
|
fun createStableNameOrNull(symbol: FirBasedSymbol<*>, session: FirSession): FirJsStableName? {
|
||||||
|
val jsName = symbol.getJsName(session)
|
||||||
|
if (jsName != null) {
|
||||||
|
return FirJsStableName(jsName, symbol, false, symbol.isPresentInGeneratedCode(session))
|
||||||
|
}
|
||||||
|
|
||||||
|
when (symbol) {
|
||||||
|
is FirConstructorSymbol -> return null
|
||||||
|
is FirPropertyAccessorSymbol -> return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val hasStableNameInJavaScript = when {
|
||||||
|
symbol.isEffectivelyExternal(session) -> true
|
||||||
|
symbol.isExportedObject(session) -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasStableNameInJavaScript ||
|
||||||
|
// TODO: The behavior (using hasPublicName()) is inherited from K1 frontend checks for the Legacy backend.
|
||||||
|
// However, it is entirely unnecessary in the IR backend.
|
||||||
|
// This is a trying to replicate the K1 Legacy logic for the sake of consistency,
|
||||||
|
// but we should redesign and overhaul it later: KT-60554
|
||||||
|
// TODO: After that, FirJsStableName.canBeMangled should always be false,
|
||||||
|
// and all related code can be removed, such as methods:
|
||||||
|
// - FirJsStableName.hasPublicName();
|
||||||
|
// - FirBasedSymbol<*>.doesJSManglingChangeName();
|
||||||
|
// - FirJsStableName.shouldClashBeCaughtByCommonFrontendCheck().
|
||||||
|
hasPublicName(symbol, session)
|
||||||
|
) {
|
||||||
|
val name = (symbol.fir as? FirMemberDeclaration)?.nameOrSpecialName?.identifierOrNullIfSpecial
|
||||||
|
if (name != null) {
|
||||||
|
return FirJsStableName(name, symbol, !hasStableNameInJavaScript, symbol.isPresentInGeneratedCode(session))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirBasedSymbol<*>.doesJSManglingChangeName(): Boolean {
|
||||||
|
return when (this) {
|
||||||
|
is FirFunctionSymbol<*> -> isExtension || valueParameterSymbols.isNotEmpty() || typeParameterSymbols.isNotEmpty()
|
||||||
|
is FirPropertySymbol -> isExtension
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shouldClashBeCaughtByCommonFrontendCheck(lhs: FirBasedSymbol<*>, rhs: FirBasedSymbol<*>): Boolean {
|
||||||
|
return (lhs is FirFunctionSymbol<*> && rhs is FirFunctionSymbol<*>) ||
|
||||||
|
(lhs is FirPropertySymbol && rhs is FirPropertySymbol) ||
|
||||||
|
(lhs is FirClassLikeSymbol<*> && rhs is FirClassLikeSymbol<*>)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isExternalRedeclarable(): Boolean {
|
||||||
|
return when {
|
||||||
|
isPresentInGeneratedCode -> false
|
||||||
|
(symbol as? FirCallableSymbol<*>)?.isFinal == true -> true
|
||||||
|
else -> symbol is FirClassLikeSymbol<*>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clashesWith(other: FirJsStableName): Boolean {
|
||||||
|
return when {
|
||||||
|
symbol === other.symbol -> false
|
||||||
|
name != other.name -> false
|
||||||
|
!isPresentInGeneratedCode && !other.isPresentInGeneratedCode -> false
|
||||||
|
isExternalRedeclarable() || other.isExternalRedeclarable() -> false
|
||||||
|
symbol.isActual != other.symbol.isActual -> false
|
||||||
|
symbol.isExpect != other.symbol.isExpect -> false
|
||||||
|
canBeMangled && symbol.doesJSManglingChangeName() -> false
|
||||||
|
other.canBeMangled && other.symbol.doesJSManglingChangeName() -> false
|
||||||
|
canBeMangled && other.canBeMangled && shouldClashBeCaughtByCommonFrontendCheck(symbol, other.symbol) -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Collection<FirJsStableName>.collectNameClashesWith(name: FirJsStableName) = mapNotNull { next ->
|
||||||
|
next.takeIf {
|
||||||
|
next.clashesWith(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-1
@@ -32,7 +32,8 @@ object JsDeclarationCheckers : DeclarationCheckers() {
|
|||||||
FirJsMultipleInheritanceChecker,
|
FirJsMultipleInheritanceChecker,
|
||||||
FirJsDynamicDeclarationChecker,
|
FirJsDynamicDeclarationChecker,
|
||||||
FirJsInheritanceClassChecker,
|
FirJsInheritanceClassChecker,
|
||||||
FirJsExternalInheritorOnlyChecker
|
FirJsExternalInheritorOnlyChecker,
|
||||||
|
FirJsNameClashClassMembersChecker
|
||||||
)
|
)
|
||||||
|
|
||||||
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
|
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
|
||||||
@@ -46,4 +47,9 @@ object JsDeclarationCheckers : DeclarationCheckers() {
|
|||||||
get() = setOf(
|
get() = setOf(
|
||||||
FirJsPropertyDelegationByDynamicChecker
|
FirJsPropertyDelegationByDynamicChecker
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override val fileCheckers: Set<FirFileChecker>
|
||||||
|
get() = setOf(
|
||||||
|
FirJsNameClashFileTopLevelDeclarationsChecker
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-7
@@ -11,14 +11,11 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
|||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
|
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
|
||||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.getStableNameInJavaScript
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.FirJsStableName
|
||||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.isNativeObject
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.isNativeObject
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.effectiveVisibility
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.nameOrSpecialName
|
|
||||||
|
|
||||||
object FirJsBuiltinNameClashChecker : FirBasicDeclarationChecker() {
|
object FirJsBuiltinNameClashChecker : FirBasicDeclarationChecker() {
|
||||||
private val PROHIBITED_STATIC_NAMES = setOf("prototype", "length", "\$metadata\$")
|
private val PROHIBITED_STATIC_NAMES = setOf("prototype", "length", "\$metadata\$")
|
||||||
@@ -32,12 +29,12 @@ object FirJsBuiltinNameClashChecker : FirBasicDeclarationChecker() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val stableName = declaration.symbol.getStableNameInJavaScript(context.session) ?: return
|
val stableName = FirJsStableName.createStableNameOrNull(declaration.symbol, context.session) ?: return
|
||||||
|
|
||||||
if (declaration is FirClassLikeDeclaration && stableName in PROHIBITED_STATIC_NAMES) {
|
if (declaration is FirClassLikeDeclaration && stableName.name in PROHIBITED_STATIC_NAMES) {
|
||||||
reporter.reportOn(declaration.source, FirJsErrors.JS_BUILTIN_NAME_CLASH, "Function.$stableName", context)
|
reporter.reportOn(declaration.source, FirJsErrors.JS_BUILTIN_NAME_CLASH, "Function.$stableName", context)
|
||||||
}
|
}
|
||||||
if (declaration is FirCallableDeclaration && stableName in PROHIBITED_MEMBER_NAMES) {
|
if (declaration is FirCallableDeclaration && stableName.name in PROHIBITED_MEMBER_NAMES) {
|
||||||
reporter.reportOn(declaration.source, FirJsErrors.JS_BUILTIN_NAME_CLASH, "Object.prototype.$stableName", context)
|
reporter.reportOn(declaration.source, FirJsErrors.JS_BUILTIN_NAME_CLASH, "Object.prototype.$stableName", context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -11,8 +11,8 @@ import org.jetbrains.kotlin.diagnostics.reportOn
|
|||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.FirJsStableName
|
||||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.getJsName
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.getJsName
|
||||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.getStableNameInJavaScript
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.isExportedObject
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.isExportedObject
|
||||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.sanitizeName
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.sanitizeName
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirConstructor
|
import org.jetbrains.kotlin.fir.declarations.FirConstructor
|
||||||
@@ -39,8 +39,8 @@ object FirJsNameCharsChecker : FirBasicDeclarationChecker() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val stableName = declaration.symbol.getStableNameInJavaScript(context.session) ?: return
|
val stableName = FirJsStableName.createStableNameOrNull(declaration.symbol, context.session) ?: return
|
||||||
if ((sanitizeName(stableName) != stableName)) {
|
if ((sanitizeName(stableName.name) != stableName.name)) {
|
||||||
reporter.reportOn(declaration.source, FirJsErrors.NAME_CONTAINS_ILLEGAL_CHARS, context)
|
reporter.reportOn(declaration.source, FirJsErrors.NAME_CONTAINS_ILLEGAL_CHARS, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+185
@@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.fir.analysis.js.checkers.declaration
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.*
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.FirJsStableName
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.*
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.popLast
|
||||||
|
|
||||||
|
object FirJsNameClashClassMembersChecker : FirClassChecker() {
|
||||||
|
private class StableNamesCollector {
|
||||||
|
val jsStableNames = mutableSetOf<FirJsStableName>()
|
||||||
|
val overrideIntersections = hashMapOf<FirCallableSymbol<*>, HashSet<FirCallableSymbol<*>>>()
|
||||||
|
|
||||||
|
private val allSymbols = mutableSetOf<FirCallableSymbol<*>>()
|
||||||
|
|
||||||
|
private fun FirTypeScope.collectOverriddenLeaves(classMemberSymbol: FirCallableSymbol<*>): Set<FirCallableSymbol<*>> {
|
||||||
|
val visitedSymbols = hashSetOf(classMemberSymbol)
|
||||||
|
val symbolsToProcess = mutableListOf(classMemberSymbol)
|
||||||
|
val leaves = mutableSetOf<FirCallableSymbol<*>>()
|
||||||
|
while (symbolsToProcess.isNotEmpty()) {
|
||||||
|
val processingSymbol = symbolsToProcess.popLast()
|
||||||
|
val overriddenMembers = getDirectOverriddenMembers(processingSymbol, true)
|
||||||
|
for (overriddenMember in overriddenMembers) {
|
||||||
|
if (visitedSymbols.add(overriddenMember)) {
|
||||||
|
symbolsToProcess.add(overriddenMember)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (overriddenMembers.isEmpty()) {
|
||||||
|
leaves.add(processingSymbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return leaves
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableSet<FirJsStableName>.addStableJavaScriptName(
|
||||||
|
targetSymbol: FirCallableSymbol<*>?,
|
||||||
|
overriddenSymbol: FirCallableSymbol<*>?,
|
||||||
|
context: CheckerContext,
|
||||||
|
) {
|
||||||
|
val stableName = when {
|
||||||
|
targetSymbol == null || overriddenSymbol == null -> return
|
||||||
|
(targetSymbol as? FirConstructorSymbol)?.isPrimary == true -> return
|
||||||
|
!overriddenSymbol.isPresentInGeneratedCode(context.session) && overriddenSymbol.isFinal -> return
|
||||||
|
else -> FirJsStableName.createStableNameOrNull(overriddenSymbol, context.session) ?: return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stableName.isPresentInGeneratedCode) {
|
||||||
|
add(stableName.copy(symbol = targetSymbol))
|
||||||
|
} else {
|
||||||
|
val inheritedExternalName = stableName.copy(
|
||||||
|
symbol = targetSymbol,
|
||||||
|
canBeMangled = false,
|
||||||
|
isPresentInGeneratedCode = targetSymbol.isPresentInGeneratedCode(context.session)
|
||||||
|
)
|
||||||
|
add(inheritedExternalName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableSet<FirJsStableName>.addAllStableJavaScriptNames(
|
||||||
|
targetSymbol: FirCallableSymbol<*>,
|
||||||
|
overriddenSymbol: FirCallableSymbol<*>,
|
||||||
|
context: CheckerContext,
|
||||||
|
) {
|
||||||
|
addStableJavaScriptName(targetSymbol, overriddenSymbol, context)
|
||||||
|
if (targetSymbol is FirPropertySymbol && overriddenSymbol is FirPropertySymbol) {
|
||||||
|
addStableJavaScriptName(targetSymbol.getterSymbol, overriddenSymbol.getterSymbol, context)
|
||||||
|
addStableJavaScriptName(targetSymbol.setterSymbol, overriddenSymbol.setterSymbol, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addAllSymbolsFrom(symbols: Collection<FirCallableSymbol<*>>) {
|
||||||
|
for (symbol in symbols) {
|
||||||
|
when (symbol) {
|
||||||
|
is FirIntersectionCallableSymbol -> {
|
||||||
|
addAllSymbolsFrom(symbol.intersections)
|
||||||
|
for (intersectedSymbol in symbol.intersections) {
|
||||||
|
overrideIntersections.getOrPut(intersectedSymbol) { hashSetOf() }.addAll(symbol.intersections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> allSymbols.add(symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processStableJavaScriptNamesForMembers(declaration: FirClass, context: CheckerContext) {
|
||||||
|
for (classMember in declaration.declarations) {
|
||||||
|
if (classMember is FirClassLikeDeclaration) {
|
||||||
|
jsStableNames.addIfNotNull(FirJsStableName.createStableNameOrNull(classMember.symbol, context.session))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val scope = declaration.symbol.unsubstitutedScope(context)
|
||||||
|
|
||||||
|
addAllSymbolsFrom(scope.collectAllFunctions())
|
||||||
|
addAllSymbolsFrom(scope.collectAllProperties())
|
||||||
|
|
||||||
|
for (callableMemberSymbol in allSymbols) {
|
||||||
|
val overriddenLeaves = scope.collectOverriddenLeaves(callableMemberSymbol)
|
||||||
|
for (symbol in overriddenLeaves) {
|
||||||
|
jsStableNames.addAllStableJavaScriptNames(callableMemberSymbol, symbol, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<FirJsStableName>.filterFakeOverrideNames(declaration: FirClass, context: CheckerContext) = filterTo(mutableSetOf()) {
|
||||||
|
it.symbol.getContainingClassSymbol(context.session) != declaration.symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class ClashedSymbol(val symbol: FirBasedSymbol<*>, val clashedWith: List<FirBasedSymbol<*>>)
|
||||||
|
|
||||||
|
private fun List<FirJsStableName>.collectNonFakeOverrideClashes(isFakeOverrideName: (FirJsStableName) -> Boolean): List<ClashedSymbol> {
|
||||||
|
return buildList {
|
||||||
|
for (stableName in this@collectNonFakeOverrideClashes) {
|
||||||
|
var clashed = collectNameClashesWith(stableName)
|
||||||
|
if (isFakeOverrideName(stableName)) {
|
||||||
|
// Do not check for clashes between fake overrides here.
|
||||||
|
// Such clashes will be checked with JS_FAKE_NAME_CLASH.
|
||||||
|
clashed = clashed.filter { !isFakeOverrideName(it) }
|
||||||
|
}
|
||||||
|
if (clashed.isNotEmpty()) {
|
||||||
|
add(ClashedSymbol(stableName.symbol, clashed.map { it.symbol }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Set<FirJsStableName>.findFirstFakeOverrideClash(stableNameCollector: StableNamesCollector): ClashedSymbol? {
|
||||||
|
for (stableName in this) {
|
||||||
|
val intersectedSymbols = stableNameCollector.overrideIntersections[stableName.symbol] ?: emptySet()
|
||||||
|
val clashed = collectNameClashesWith(stableName).filter {
|
||||||
|
// intersected fake override symbols are not clashed
|
||||||
|
it.symbol !in intersectedSymbols
|
||||||
|
}
|
||||||
|
if (clashed.isNotEmpty()) {
|
||||||
|
return ClashedSymbol(stableName.symbol, clashed.map { it.symbol })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
|
if (!declaration.symbol.isPresentInGeneratedCode(context.session)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val stableNameCollector = StableNamesCollector()
|
||||||
|
stableNameCollector.processStableJavaScriptNamesForMembers(declaration, context)
|
||||||
|
|
||||||
|
val membersGroupedByName = stableNameCollector.jsStableNames.groupBy { it.name }
|
||||||
|
|
||||||
|
for ((name, stableNames) in membersGroupedByName.entries) {
|
||||||
|
val fakeOverrideStableNames = stableNames.filterFakeOverrideNames(declaration, context)
|
||||||
|
|
||||||
|
val nonFakeOverrideClashes = stableNames.collectNonFakeOverrideClashes { it in fakeOverrideStableNames }
|
||||||
|
for ((symbol, clashedWith) in nonFakeOverrideClashes) {
|
||||||
|
reporter.reportOn(symbol.source ?: declaration.source, FirJsErrors.JS_NAME_CLASH, name, clashedWith, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeOverrideStableNames.findFirstFakeOverrideClash(stableNameCollector)?.let { (fakeOverrideSymbol, clashedWith) ->
|
||||||
|
reporter.reportOn(declaration.source, FirJsErrors.JS_FAKE_NAME_CLASH, name, fakeOverrideSymbol, clashedWith, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.fir.analysis.js.checkers.declaration
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFileChecker
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.FirJsStableName
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.collectNameClashesWith
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
|
|
||||||
|
object FirJsNameClashFileTopLevelDeclarationsChecker : FirFileChecker() {
|
||||||
|
private fun MutableMap<String, MutableList<FirJsStableName>>.addStableName(
|
||||||
|
symbol: FirBasedSymbol<*>,
|
||||||
|
context: CheckerContext
|
||||||
|
) {
|
||||||
|
val stableName = FirJsStableName.createStableNameOrNull(symbol, context.session)
|
||||||
|
if (stableName != null) {
|
||||||
|
getOrPut(stableName.name) { mutableListOf() }.add(stableName)
|
||||||
|
}
|
||||||
|
if (symbol is FirPropertySymbol) {
|
||||||
|
symbol.getterSymbol?.let { getter -> addStableName(getter, context) }
|
||||||
|
symbol.setterSymbol?.let { setter -> addStableName(setter, context) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun check(declaration: FirFile, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
|
val topLevelDeclarationsWithStableName = mutableMapOf<String, MutableList<FirJsStableName>>()
|
||||||
|
for (topLevelDeclaration in declaration.declarations) {
|
||||||
|
if (topLevelDeclaration is FirTypeAlias) {
|
||||||
|
// Skip type aliases since they cannot be external, cannot be exported to JavaScript, and cannot be marked with @JsName.
|
||||||
|
// Furthermore, in the generated JavaScript code, all type alias declarations are removed,
|
||||||
|
// and their usages are replaced with the aliased types.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
topLevelDeclarationsWithStableName.addStableName(topLevelDeclaration.symbol, context)
|
||||||
|
if (topLevelDeclaration is FirClass) {
|
||||||
|
for (classConstructor in topLevelDeclaration.constructors(context.session)) {
|
||||||
|
topLevelDeclarationsWithStableName.addStableName(classConstructor, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ((name, stableNames) in topLevelDeclarationsWithStableName.entries) {
|
||||||
|
for (symbol in stableNames) {
|
||||||
|
val clashed = stableNames.collectNameClashesWith(symbol).takeIf { it.isNotEmpty() } ?: continue
|
||||||
|
val source = symbol.symbol.source ?: declaration.source
|
||||||
|
reporter.reportOn(source, FirJsErrors.JS_NAME_CLASH, name, clashed.map { it.symbol }, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -1,5 +1,6 @@
|
|||||||
// IGNORE_BACKEND_K1: WASM
|
// IGNORE_BACKEND_K1: WASM
|
||||||
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
|
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
|
||||||
|
// IGNORE_BACKEND_K2: JS_IR, JS_IR_ES6
|
||||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||||
// IGNORE_BACKEND: JS
|
// IGNORE_BACKEND: JS
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6, WASM
|
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6, WASM
|
||||||
|
// IGNORE_BACKEND_K2: JS_IR, JS_IR_ES6
|
||||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||||
// IGNORE_BACKEND: JS
|
// IGNORE_BACKEND: JS
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// IGNORE_BACKEND_K1: WASM
|
// IGNORE_BACKEND_K1: WASM
|
||||||
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
|
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
|
||||||
|
// IGNORE_BACKEND_K2: JS_IR, JS_IR_ES6
|
||||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||||
// IGNORE_BACKEND: JS
|
// IGNORE_BACKEND: JS
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// IGNORE_BACKEND_K1: WASM
|
// IGNORE_BACKEND_K1: WASM
|
||||||
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
|
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
|
||||||
|
// IGNORE_BACKEND_K2: JS_IR, JS_IR_ES6
|
||||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||||
// IGNORE_BACKEND: JS
|
// IGNORE_BACKEND: JS
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// IGNORE_BACKEND_K1: WASM
|
// IGNORE_BACKEND_K1: WASM
|
||||||
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
|
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
|
||||||
|
// IGNORE_BACKEND_K2: JS_IR, JS_IR_ES6
|
||||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||||
// IGNORE_BACKEND: JS
|
// IGNORE_BACKEND: JS
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
interface I {
|
|
||||||
fun foo()
|
|
||||||
}
|
|
||||||
|
|
||||||
interface J {
|
|
||||||
@JsName("bar")
|
|
||||||
fun foo()
|
|
||||||
}
|
|
||||||
|
|
||||||
interface K : I, J {
|
|
||||||
override fun foo()
|
|
||||||
}
|
|
||||||
|
|
||||||
interface L : K {
|
|
||||||
override fun foo()
|
|
||||||
|
|
||||||
fun bar()
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
interface I {
|
interface I {
|
||||||
fun foo()
|
fun foo()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
package foo
|
|
||||||
|
|
||||||
class A(val x: Int)
|
|
||||||
|
|
||||||
fun A() {}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
class <!JS_NAME_CLASH!>A(val x: Int)<!>
|
class <!JS_NAME_CLASH!>A(val x: Int)<!>
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>fun A()<!> {}
|
<!JS_NAME_CLASH!>fun A()<!> {}
|
||||||
|
|||||||
-7
@@ -1,7 +0,0 @@
|
|||||||
package foo
|
|
||||||
|
|
||||||
class A {
|
|
||||||
fun bar() = 23
|
|
||||||
|
|
||||||
val bar = 23
|
|
||||||
}
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
class A {
|
class A {
|
||||||
|
|||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
interface A {
|
|
||||||
@JsName("foo") fun f()
|
|
||||||
}
|
|
||||||
|
|
||||||
interface B {
|
|
||||||
@JsName("foo") fun g()
|
|
||||||
}
|
|
||||||
|
|
||||||
class C : A, B {
|
|
||||||
override fun f() {}
|
|
||||||
|
|
||||||
override fun g() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class D : A, B
|
|
||||||
|
|
||||||
open class E {
|
|
||||||
open fun f() {}
|
|
||||||
|
|
||||||
open fun g() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class F : E(), A, B
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
interface A {
|
interface A {
|
||||||
@JsName("foo") fun f()
|
@JsName("foo") fun f()
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -1,3 +1,6 @@
|
|||||||
|
// FIR_DIFFERENCE
|
||||||
|
// This case is only relevant for the JS Legacy BE and is not applicable to the JS IR backend,
|
||||||
|
// as the IR BE can resolve such name collisions.
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
class A
|
class A
|
||||||
|
|||||||
+4
-1
@@ -1,3 +1,6 @@
|
|||||||
|
// FIR_DIFFERENCE
|
||||||
|
// This case is only relevant for the JS Legacy BE and is not applicable to the JS IR backend,
|
||||||
|
// as the IR BE can resolve such name collisions.
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
class A
|
class A
|
||||||
@@ -5,4 +8,4 @@ class A
|
|||||||
<!JS_NAME_CLASH!>fun A.get_bar()<!> = 23
|
<!JS_NAME_CLASH!>fun A.get_bar()<!> = 23
|
||||||
|
|
||||||
val A.bar: Int
|
val A.bar: Int
|
||||||
<!JS_NAME_CLASH!>get()<!> = 42
|
<!JS_NAME_CLASH!>get()<!> = 42
|
||||||
|
|||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
package foo
|
|
||||||
|
|
||||||
open class Super {
|
|
||||||
fun foo() = 23
|
|
||||||
}
|
|
||||||
|
|
||||||
class Sub : Super() {
|
|
||||||
@JsName("foo") fun bar() = 42
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
open class Super {
|
open class Super {
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
package foo
|
|
||||||
|
|
||||||
@JsName("x") fun foo(x: Int) = x
|
|
||||||
|
|
||||||
@JsName("x") fun bar() = 42
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>@JsName("x") fun foo(x: Int)<!> = x
|
<!JS_NAME_CLASH!>@JsName("x") fun foo(x: Int)<!> = x
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>@JsName("x") fun bar()<!> = 42
|
<!JS_NAME_CLASH!>@JsName("x") fun bar()<!> = 42
|
||||||
|
|||||||
-5
@@ -1,5 +0,0 @@
|
|||||||
package foo
|
|
||||||
|
|
||||||
@JsName("bar") fun foo(x: Int) = x
|
|
||||||
|
|
||||||
fun bar() = 42
|
|
||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>@JsName("bar") fun foo(x: Int)<!> = x
|
<!JS_NAME_CLASH!>@JsName("bar") fun foo(x: Int)<!> = x
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>fun bar()<!> = 42
|
<!JS_NAME_CLASH!>fun bar()<!> = 42
|
||||||
|
|||||||
+2
-2
@@ -2,6 +2,6 @@ package foo
|
|||||||
|
|
||||||
class A {
|
class A {
|
||||||
var x: Int
|
var x: Int
|
||||||
@JsName("xx") get() = 0
|
<!JS_NAME_CLASH!>@JsName("xx") get()<!> = 0
|
||||||
@JsName("xx") set(value) {}
|
<!JS_NAME_CLASH!>@JsName("xx") set(value)<!> {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// FIR_DIFFERENCE
|
||||||
|
// This case is only relevant for the JS Legacy BE and is not applicable to the JS IR backend,
|
||||||
|
// as the IR BE can resolve such name collisions. Furthermore, the IR BE mangles names differently.
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
fun bar(x: Int) = x
|
fun bar(x: Int) = x
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
// FIR_DIFFERENCE
|
||||||
|
// This case is only relevant for the JS Legacy BE and is not applicable to the JS IR backend,
|
||||||
|
// as the IR BE can resolve such name collisions. Furthermore, the IR BE mangles names differently.
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>fun bar(x: Int)<!> = x
|
<!JS_NAME_CLASH!>fun bar(x: Int)<!> = x
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>fun `bar_za3lpa$`()<!> = 42
|
<!JS_NAME_CLASH!>fun `bar_za3lpa$`()<!> = 42
|
||||||
|
|||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
interface I {
|
|
||||||
@JsName("bar")
|
|
||||||
fun foo()
|
|
||||||
|
|
||||||
@JsName("foo")
|
|
||||||
fun bar()
|
|
||||||
}
|
|
||||||
|
|
||||||
interface J {
|
|
||||||
fun foo()
|
|
||||||
|
|
||||||
fun bar()
|
|
||||||
}
|
|
||||||
|
|
||||||
class A : I, J {
|
|
||||||
// Duplicate diagnostics are expected here, since `bar()` function gets both `foo` and `bar` names and clashes with both
|
|
||||||
// names of `foo()` function.
|
|
||||||
override fun bar() {}
|
|
||||||
|
|
||||||
override fun foo() {}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
interface I {
|
interface I {
|
||||||
@JsName("bar")
|
@JsName("bar")
|
||||||
fun foo()
|
fun foo()
|
||||||
|
|||||||
Vendored
-9
@@ -1,9 +0,0 @@
|
|||||||
external open class A {
|
|
||||||
open fun f(x: Int): Unit
|
|
||||||
|
|
||||||
open fun f(x: String): Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
class InheritClass : A() {
|
|
||||||
override fun f(x: Int): Unit { }
|
|
||||||
}
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
external open class A {
|
external open class A {
|
||||||
open fun f(x: Int): Unit
|
open fun f(x: Int): Unit
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// FIR_DIFFERENCE
|
||||||
|
// This case can't be checked using FIR. It is checked later on klib serialization.
|
||||||
|
|
||||||
// FILE: foo.kt
|
// FILE: foo.kt
|
||||||
|
|
||||||
package foo
|
package foo
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// FIR_DIFFERENCE
|
||||||
|
// This case can't be checked using FIR. It is checked later on klib serialization.
|
||||||
|
|
||||||
// FILE: foo.kt
|
// FILE: foo.kt
|
||||||
|
|
||||||
package foo
|
package foo
|
||||||
|
|||||||
+3
@@ -1,3 +1,6 @@
|
|||||||
|
// FIR_DIFFERENCE
|
||||||
|
// This case can't be checked using FIR. It is checked later on klib serialization.
|
||||||
|
|
||||||
// FILE: foo.kt
|
// FILE: foo.kt
|
||||||
|
|
||||||
package foo
|
package foo
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// FIR_DIFFERENCE
|
||||||
|
// This case can't be checked using FIR. It is checked later on klib serialization.
|
||||||
|
|
||||||
// FILE: foo.kt
|
// FILE: foo.kt
|
||||||
|
|
||||||
package foo
|
package foo
|
||||||
|
|||||||
-5
@@ -1,5 +0,0 @@
|
|||||||
package foo
|
|
||||||
|
|
||||||
@JsName("bar") private fun foo(x: Int) = x
|
|
||||||
|
|
||||||
fun bar() = 42
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>@JsName("bar") private fun foo(x: Int)<!> = x
|
<!JS_NAME_CLASH!>@JsName("bar") private fun foo(x: Int)<!> = x
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>fun bar()<!> = 42
|
<!JS_NAME_CLASH!>fun bar()<!> = 42
|
||||||
|
|||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
package foo
|
|
||||||
|
|
||||||
interface I {
|
|
||||||
fun foo() = 23
|
|
||||||
}
|
|
||||||
|
|
||||||
class Sub : I {
|
|
||||||
var foo = 42
|
|
||||||
}
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
interface I {
|
interface I {
|
||||||
|
|||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
package foo
|
|
||||||
|
|
||||||
open class Super {
|
|
||||||
val foo = 23
|
|
||||||
}
|
|
||||||
|
|
||||||
class Sub : Super() {
|
|
||||||
fun foo() = 42
|
|
||||||
}
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
open class Super {
|
open class Super {
|
||||||
|
|||||||
Vendored
-7
@@ -1,7 +0,0 @@
|
|||||||
package foo
|
|
||||||
|
|
||||||
class A(val x: String) {
|
|
||||||
@JsName("aa") constructor(x: Int) : this("int $x")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun aa() {}
|
|
||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
class A(val x: String) {
|
class A(val x: String) {
|
||||||
@JsName("aa") <!JS_NAME_CLASH!>constructor(x: Int)<!> : this("int $x")
|
@JsName("aa") <!JS_NAME_CLASH!>constructor(x: Int)<!> : this("int $x")
|
||||||
}
|
}
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>fun aa()<!> {}
|
<!JS_NAME_CLASH!>fun aa()<!> {}
|
||||||
|
|||||||
-5
@@ -1,5 +0,0 @@
|
|||||||
package foo
|
|
||||||
|
|
||||||
fun bar() = 23
|
|
||||||
|
|
||||||
val bar = 32
|
|
||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>fun bar()<!> = 23
|
<!JS_NAME_CLASH!>fun bar()<!> = 23
|
||||||
|
|
||||||
<!JS_NAME_CLASH!>val bar<!> = 32
|
<!JS_NAME_CLASH!>val bar<!> = 32
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// FIR_IDENTICAL
|
// FIR_IDENTICAL
|
||||||
// SKIP_KLIB_TEST
|
// SKIP_KLIB_TEST
|
||||||
// IGNORE_BACKEND_K1: JS_IR
|
// IGNORE_BACKEND_K1: JS_IR
|
||||||
|
// IGNORE_BACKEND_K2: JS_IR
|
||||||
// Ignore reason: there is a js name clash between function `a()` and property `a`
|
// Ignore reason: there is a js name clash between function `a()` and property `a`
|
||||||
package test
|
package test
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user