[JS FIR] Support JS module system FE diagnostics

Support CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM diagnostic
^KT-59377 Fixed

Support CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM diagnostic
^KT-59381 Fixed

Support CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE diagnostic
^KT-59417 Fixed
This commit is contained in:
Alexander Korepanov
2023-07-13 15:30:04 +02:00
committed by Space Team
parent 30b012839c
commit 08d158f901
24 changed files with 319 additions and 169 deletions
@@ -1118,6 +1118,26 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirJsErrors.CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE) { firDiagnostic ->
CallFromUmdMustBeJsModuleAndJsNonModuleImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirJsErrors.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM) { firDiagnostic ->
CallToJsModuleWithoutModuleSystemImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirJsErrors.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM) { firDiagnostic ->
CallToJsNonModuleWithModuleSystemImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirJsErrors.RUNTIME_ANNOTATION_NOT_SUPPORTED) { firDiagnostic ->
RuntimeAnnotationNotSupportedImpl(
firDiagnostic as KtPsiDiagnostic,
@@ -806,6 +806,20 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = NestedJsModuleProhibited::class
}
interface CallFromUmdMustBeJsModuleAndJsNonModule : KtFirDiagnostic<KtElement> {
override val diagnosticClass get() = CallFromUmdMustBeJsModuleAndJsNonModule::class
}
interface CallToJsModuleWithoutModuleSystem : KtFirDiagnostic<KtElement> {
override val diagnosticClass get() = CallToJsModuleWithoutModuleSystem::class
val callee: KtSymbol
}
interface CallToJsNonModuleWithModuleSystem : KtFirDiagnostic<KtElement> {
override val diagnosticClass get() = CallToJsNonModuleWithModuleSystem::class
val callee: KtSymbol
}
interface RuntimeAnnotationNotSupported : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = RuntimeAnnotationNotSupported::class
}
@@ -967,6 +967,23 @@ internal class NestedJsModuleProhibitedImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtElement>(firDiagnostic, token), KtFirDiagnostic.NestedJsModuleProhibited
internal class CallFromUmdMustBeJsModuleAndJsNonModuleImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtElement>(firDiagnostic, token), KtFirDiagnostic.CallFromUmdMustBeJsModuleAndJsNonModule
internal class CallToJsModuleWithoutModuleSystemImpl(
override val callee: KtSymbol,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtElement>(firDiagnostic, token), KtFirDiagnostic.CallToJsModuleWithoutModuleSystem
internal class CallToJsNonModuleWithModuleSystemImpl(
override val callee: KtSymbol,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtElement>(firDiagnostic, token), KtFirDiagnostic.CallToJsNonModuleWithModuleSystem
internal class RuntimeAnnotationNotSupportedImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
@@ -127,7 +127,7 @@ fun <F> prepareJsSessions(
sessionProvider,
libraryList.moduleDataProvider,
extensionRegistrars,
configuration.languageVersionSettings,
configuration,
registerExtraComponents = {},
)
}
@@ -136,7 +136,7 @@ fun <F> prepareJsSessions(
moduleData,
sessionProvider,
extensionRegistrars,
configuration.languageVersionSettings,
configuration,
lookupTracker,
icData = icData,
registerExtraComponents = {},
@@ -9,6 +9,7 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.util.PrivateForInline
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.PositioningStrategy
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
@@ -23,6 +24,13 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") {
val JS_MODULE_PROHIBITED_ON_VAR by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
val JS_MODULE_PROHIBITED_ON_NON_NATIVE by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
val NESTED_JS_MODULE_PROHIBITED by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
val CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
val CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<FirBasedSymbol<*>>("callee")
}
val CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<FirBasedSymbol<*>>("callee")
}
val RUNTIME_ANNOTATION_NOT_SUPPORTED by warning<PsiElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
val RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION by error<PsiElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
val NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
@@ -10,6 +10,10 @@ dependencies {
api(project(":js:js.ast"))
api(project(":compiler:fir:checkers"))
// FE checks for modules use ModuleKind
// This dependency can be removed when we stop supporting PLANE and UMD module systems
implementation(project(":js:js.serializer"))
/*
* We can't remove this dependency until we use
* diagnostics framework from FE 1.0
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
@@ -32,6 +33,9 @@ object FirJsErrors {
val JS_MODULE_PROHIBITED_ON_VAR by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val JS_MODULE_PROHIBITED_ON_NON_NATIVE by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NESTED_JS_MODULE_PROHIBITED by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM by error1<KtElement, FirBasedSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM by error1<KtElement, FirBasedSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val RUNTIME_ANNOTATION_NOT_SUPPORTED by warning0<PsiElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION by error0<PsiElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN by error1<KtElement, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
@@ -10,7 +10,10 @@ import org.jetbrains.kotlin.diagnostics.KtDiagnosticRenderers
import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.CANNOT_CHECK_FOR_EXTERNAL_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTENSION_FUNCTION_IN_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.EXTERNAL_ANONYMOUS_INITIALIZER
@@ -77,6 +80,18 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
NESTED_JS_MODULE_PROHIBITED,
"@JsModule and @JsNonModule can't appear on here since the file is already marked by either @JsModule or @JsNonModule"
)
map.put(
CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE,
"When accessing module declarations from UMD, they must be marked by both @JsModule and @JsNonModule"
)
map.put(
CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM,
"Can''t access {0} marked with @JsModule annotation from non-modular project", FirDiagnosticRenderers.SYMBOL
)
map.put(
CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM,
"Can''t access {0} marked with @JsNonModule annotation from modular project", FirDiagnosticRenderers.SYMBOL
)
map.put(
WRONG_MULTIPLE_INHERITANCE,
"Can''t apply multiple inheritance here, since it''s impossible to generate bridge for system function {0}",
@@ -7,6 +7,7 @@
package org.jetbrains.kotlin.fir.analysis.js.checkers
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
@@ -21,6 +22,9 @@ import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.isAny
import org.jetbrains.kotlin.fir.types.isNullableAny
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.js.PredefinedAnnotation
import org.jetbrains.kotlin.js.common.isES5IdentifierPart
import org.jetbrains.kotlin.js.common.isES5IdentifierStart
@@ -133,7 +137,7 @@ fun FirBasedSymbol<*>.isExportedObject(session: FirSession): Boolean {
}
}
private fun FirBasedSymbol<*>.getContainingFile(session: FirSession): FirFile? {
internal fun FirBasedSymbol<*>.getContainingFile(session: FirSession): FirFile? {
return when (this) {
is FirCallableSymbol<*> -> session.firProvider.getFirCallableContainerFile(this)
is FirClassLikeSymbol<*> -> session.firProvider.getFirClassifierContainerFileIfAny(this)
@@ -148,3 +152,15 @@ fun FirBasedSymbol<*>.isNativeInterface(context: CheckerContext) = isNativeInter
fun FirBasedSymbol<*>.isPredefinedObject(context: CheckerContext) = isPredefinedObject(context.session)
fun FirBasedSymbol<*>.isExportedObject(context: CheckerContext) = isExportedObject(context.session)
internal fun FirClass.superClassNotAny(session: FirSession) = superConeTypes
.filterNot { it.isAny || it.isNullableAny }
.find { it.toSymbol(session)?.classKind == ClassKind.CLASS }
internal fun getRootDeclarationSymbol(symbol: FirBasedSymbol<*>, session: FirSession): FirBasedSymbol<*> {
return generateSequence(symbol) { it.getContainingClassSymbol(session) }.last()
}
internal fun isTopLevelSymbol(symbol: FirBasedSymbol<*>, session: FirSession): Boolean {
return symbol.getContainingClassSymbol(session) == null
}
@@ -0,0 +1,66 @@
/*
* 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.AbstractKtSourceElement
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.NoMutableState
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationStringParameter
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.name.JsStandardClassIds
import org.jetbrains.kotlin.serialization.js.ModuleKind
@NoMutableState
class FirJsModuleKind(val moduleKind: ModuleKind) : FirSessionComponent
private val FirSession.jsModuleKindComponent: FirJsModuleKind by FirSession.sessionComponentAccessor()
private val FirSession.jsModuleKind: ModuleKind
get() = jsModuleKindComponent.moduleKind
internal fun checkJsModuleUsage(
callee: FirBasedSymbol<*>,
context: CheckerContext,
reporter: DiagnosticReporter,
source: AbstractKtSourceElement?
) {
val moduleKind = context.session.jsModuleKind
val calleeSession = callee.moduleData.session
val calleeRoot = getRootDeclarationSymbol(callee, calleeSession)
val calleeContainingFile = calleeRoot.getContainingFile(calleeSession)
val callToModule = calleeRoot.getAnnotationStringParameter(JsStandardClassIds.Annotations.JsModule, calleeSession) != null ||
calleeContainingFile?.symbol?.getAnnotationStringParameter(JsStandardClassIds.Annotations.JsModule, calleeSession) != null
val callToNonModule = calleeRoot.hasAnnotation(JsStandardClassIds.Annotations.JsNonModule, calleeSession) ||
calleeContainingFile?.symbol?.hasAnnotation(JsStandardClassIds.Annotations.JsNonModule, calleeSession) == true
when (moduleKind) {
ModuleKind.UMD -> {
if (!callToNonModule && callToModule || callToNonModule && !callToModule) {
reporter.reportOn(source, FirJsErrors.CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE, context)
}
}
ModuleKind.PLAIN -> {
if (!callToNonModule && callToModule) {
reporter.reportOn(source, FirJsErrors.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM, callee, context)
}
}
else -> {
if (!callToModule && callToNonModule) {
reporter.reportOn(source, FirJsErrors.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM, callee, context)
}
}
}
}
@@ -30,4 +30,14 @@ object JsExpressionCheckers : ExpressionCheckers() {
get() = setOf(
FirJsExternalArgumentCallChecker
)
override val getClassCallCheckers: Set<FirGetClassCallChecker>
get() = setOf(
FirJsModuleGetClassCallChecker
)
override val qualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker>
get() = setOf(
FirJsModuleQualifiedAccessChecker
)
}
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
import org.jetbrains.kotlin.fir.analysis.js.checkers.isNativeObject
import org.jetbrains.kotlin.fir.analysis.js.checkers.superClassNotAny
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.types.*
@@ -203,10 +204,6 @@ object FirJsExternalChecker : FirBasicDeclarationChecker() {
else -> returnTypeRef.coneType.typeArguments.firstOrNull()?.type
}
private fun FirClass.superClassNotAny(session: FirSession) = superConeTypes
.filterNot { it.isAny || it.isNullableAny }
.find { it.toSymbol(session)?.classKind == ClassKind.CLASS }
private fun FirClass.superInterfaces(session: FirSession) = superConeTypes
.filterNot { it.isAny || it.isNullableAny }
.filter { it.toSymbol(session)?.classKind == ClassKind.INTERFACE }
@@ -353,4 +350,4 @@ object FirJsExternalChecker : FirBasicDeclarationChecker() {
?.calleeReference?.toResolvedPropertySymbol() ?: return false
return declaration.callableId in JsStandardClassIds.Callables.definedExternallyPropertyNames
}
}
}
@@ -13,13 +13,19 @@ 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.isTopLevel
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.analysis.js.checkers.checkJsModuleUsage
import org.jetbrains.kotlin.fir.analysis.js.checkers.isNativeObject
import org.jetbrains.kotlin.fir.analysis.js.checkers.superClassNotAny
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.types.coneTypeOrNull
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.name.JsStandardClassIds.Annotations.JsModule
import org.jetbrains.kotlin.name.JsStandardClassIds.Annotations.JsNonModule
object FirJsModuleChecker : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
checkSuperClass(declaration, context, reporter)
if (declaration is FirFile || !declaration.isEitherModuleOrNonModule(context.session)) return
if (declaration is FirProperty && declaration.isVar) {
@@ -37,6 +43,16 @@ object FirJsModuleChecker : FirBasicDeclarationChecker() {
}
}
private fun checkSuperClass(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
val classDeclaration = declaration as? FirClass ?: return
val superClassSymbol = classDeclaration.superClassNotAny(context.session)?.toSymbol(context.session) ?: return
val superClassRef = classDeclaration.superTypeRefs.firstOrNull {
it.coneTypeOrNull?.toSymbol(context.session) == superClassSymbol
}
checkJsModuleUsage(superClassSymbol, context, reporter, superClassRef?.source ?: declaration.source)
}
private fun FirDeclaration.isEitherModuleOrNonModule(session: FirSession) =
hasAnnotation(JsModule, session) || hasAnnotation(JsNonModule, session)
}
@@ -0,0 +1,22 @@
/*
* 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.expression
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirGetClassCallChecker
import org.jetbrains.kotlin.fir.analysis.js.checkers.checkJsModuleUsage
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
import org.jetbrains.kotlin.fir.types.coneTypeOrNull
import org.jetbrains.kotlin.fir.types.toSymbol
object FirJsModuleGetClassCallChecker : FirGetClassCallChecker() {
override fun check(expression: FirGetClassCall, context: CheckerContext, reporter: DiagnosticReporter) {
val callee = expression.argument.typeRef.coneTypeOrNull?.toSymbol(context.session) ?: return
checkJsModuleUsage(callee, context, reporter, expression.argument.typeRef.source ?: expression.source)
}
}
@@ -0,0 +1,68 @@
/*
* 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.expression
import org.jetbrains.kotlin.AbstractKtSourceElement
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker
import org.jetbrains.kotlin.fir.analysis.js.checkers.checkJsModuleUsage
import org.jetbrains.kotlin.fir.analysis.js.checkers.isTopLevelSymbol
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.references.toResolvedBaseSymbol
import org.jetbrains.kotlin.fir.references.toResolvedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.types.FirTypeProjectionWithVariance
import org.jetbrains.kotlin.fir.types.coneTypeOrNull
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
object FirJsModuleQualifiedAccessChecker : FirQualifiedAccessExpressionChecker() {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
checkReifiedTypeParameters(expression, context, reporter)
val calleeSymbols = extractModuleCalleeSymbols(expression)
for ((calleeSymbol, source) in calleeSymbols) {
checkJsModuleUsage(calleeSymbol, context, reporter, source ?: expression.source)
}
}
private fun extractModuleCalleeSymbols(
expression: FirQualifiedAccessExpression
): List<Pair<FirBasedSymbol<*>, AbstractKtSourceElement?>> {
val calleeSymbol = expression.calleeReference.toResolvedBaseSymbol()
if (calleeSymbol != null && isTopLevelSymbol(calleeSymbol, calleeSymbol.moduleData.session)) {
return listOf(calleeSymbol to expression.calleeReference.source)
}
return when (val receiver = expression.dispatchReceiver) {
is FirNoReceiverExpression -> listOfNotNull(calleeSymbol?.to(expression.calleeReference.source))
is FirResolvedQualifier -> {
val classSymbol = receiver.symbol
if (expression is FirCallableReferenceAccess) {
listOfNotNull(classSymbol?.to(receiver.source), calleeSymbol?.to(expression.calleeReference.source))
} else {
listOfNotNull(classSymbol?.to(expression.calleeReference.source))
}
}
else -> emptyList()
}
}
private fun checkReifiedTypeParameters(expr: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
val functionSymbol = (expr as? FirFunctionCall)?.calleeReference?.toResolvedFunctionSymbol() ?: return
for ((typeParameterSymbol, typeArgument) in functionSymbol.typeParameterSymbols.zip(expr.typeArguments)) {
if (typeParameterSymbol.isReified) {
val type = (typeArgument as? FirTypeProjectionWithVariance)?.typeRef?.coneTypeOrNull ?: continue
val typeArgumentClass = type.toRegularClassSymbol(context.session) ?: continue
val source = typeArgument.source ?: expr.calleeReference.source ?: expr.source
checkJsModuleUsage(typeArgumentClass, context, reporter, source)
}
}
}
}
@@ -5,8 +5,8 @@
package org.jetbrains.kotlin.fir.session
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirVisibilityChecker
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.SessionConfiguration
import org.jetbrains.kotlin.fir.analysis.FirEmptyOverridesBackwardCompatibilityHelper
import org.jetbrains.kotlin.fir.analysis.FirOverridesBackwardCompatibilityHelper
import org.jetbrains.kotlin.fir.analysis.checkers.FirPlatformDiagnosticSuppressor
import org.jetbrains.kotlin.fir.analysis.js.checkers.FirJsModuleKind
import org.jetbrains.kotlin.fir.analysis.js.checkers.FirJsPlatformDiagnosticSuppressor
import org.jetbrains.kotlin.fir.checkers.registerJsCheckers
import org.jetbrains.kotlin.fir.deserialization.ModuleDataProvider
@@ -25,15 +26,17 @@ import org.jetbrains.kotlin.fir.resolve.providers.impl.FirBuiltinSyntheticFuncti
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.fir.scopes.FirPlatformClassMapper
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.js.ModuleKind
object FirJsSessionFactory : FirAbstractSessionFactory() {
fun createModuleBasedSession(
moduleData: FirModuleData,
sessionProvider: FirProjectSessionProvider,
extensionRegistrars: List<FirExtensionRegistrar>,
languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
compilerConfiguration: CompilerConfiguration,
lookupTracker: LookupTracker?,
icData: KlibIcData? = null,
registerExtraComponents: ((FirSession) -> Unit) = {},
@@ -43,12 +46,12 @@ object FirJsSessionFactory : FirAbstractSessionFactory() {
moduleData,
sessionProvider,
extensionRegistrars,
languageVersionSettings,
compilerConfiguration.languageVersionSettings,
lookupTracker,
null,
init,
registerExtraComponents = { session ->
session.registerJsSpecificComponents()
session.registerJsSpecificComponents(compilerConfiguration)
registerExtraComponents(session)
},
registerExtraCheckers = { it.registerJsCheckers() },
@@ -77,16 +80,16 @@ object FirJsSessionFactory : FirAbstractSessionFactory() {
sessionProvider: FirProjectSessionProvider,
moduleDataProvider: ModuleDataProvider,
extensionRegistrars: List<FirExtensionRegistrar>,
languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
compilerConfiguration: CompilerConfiguration,
registerExtraComponents: ((FirSession) -> Unit),
): FirSession = createLibrarySession(
mainModuleName,
sessionProvider,
moduleDataProvider,
languageVersionSettings,
compilerConfiguration.languageVersionSettings,
extensionRegistrars,
registerExtraComponents = {
it.registerJsSpecificComponents()
it.registerJsSpecificComponents(compilerConfiguration)
registerExtraComponents(it)
},
createKotlinScopeProvider = { FirKotlinScopeProvider() },
@@ -100,11 +103,14 @@ object FirJsSessionFactory : FirAbstractSessionFactory() {
)
@OptIn(SessionConfiguration::class)
fun FirSession.registerJsSpecificComponents() {
fun FirSession.registerJsSpecificComponents(compilerConfiguration: CompilerConfiguration) {
register(FirVisibilityChecker::class, FirVisibilityChecker.Default)
register(ConeCallConflictResolverFactory::class, JsCallConflictResolverFactory)
register(FirPlatformClassMapper::class, FirPlatformClassMapper.Default)
register(FirOverridesBackwardCompatibilityHelper::class, FirEmptyOverridesBackwardCompatibilityHelper)
register(FirPlatformDiagnosticSuppressor::class, FirJsPlatformDiagnosticSuppressor())
val moduleKind = compilerConfiguration.get(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
register(FirJsModuleKind::class, FirJsModuleKind(moduleKind))
}
}
@@ -1,29 +0,0 @@
// MODULE: m1
// FILE: a.kt
package foo
import kotlin.js.*
@JsModule("A")
external object A {
fun f(): Int
val g: Int
}
@JsNonModule
external open class B {
fun foo(): Int
}
// MODULE: m2(m1)
// MODULE_KIND: UMD
// FILE: c.kt
package bar
import foo.*
fun box() {
A.f()+A.g
B()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// MODULE: m1
// FILE: a.kt
package foo
@@ -1,61 +0,0 @@
// MODULE: m1
// FILE: a.kt
package foo
import kotlin.js.*
@JsModule("A")
external object A {
fun f(): Int
val g: Int
}
@JsModule("B")
external open class B {
fun foo(): Int
class Nested
}
@JsModule("bar")
external fun bar(): Unit
// MODULE: m2(m1)
// FILE: b.kt
// TODO: it's hard to test @JsNonModule on file from an external module
@file:JsModule("foo")
package foo
external fun baz(): Unit
// FILE: c.kt
package bar
import foo.*
fun box() {
A.f()+A.g
B()
bar()
baz()
println(::bar.name)
println(::baz.name)
println(A::f.name)
B.Nested()
boo<B?>(null)
boo(null as B?)
boo<B.Nested?>(null)
println(B::class)
println(B.Nested::class)
}
external class DerivedB : B
inline fun <reified T> boo(x: T) {
println("${T::class.simpleName}: $x")
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// MODULE: m1
// FILE: a.kt
package foo
@@ -52,10 +53,13 @@ fun box() {
println(<!CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM!>B::class<!>)
println(<!CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM!>B.Nested::class<!>)
val x: Any = 1
println(x is B)
}
external class DerivedB : <!CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM!>B<!>
inline fun <reified T> boo(x: T) {
println("${T::class.simpleName}: $x")
}
}
@@ -1,53 +0,0 @@
// MODULE: m1
// FILE: a.kt
package foo
import kotlin.js.*
@JsNonModule
external object A {
fun f(): Int
val g: Int
}
@JsNonModule
external open class B {
fun foo(): Int
class Nested
}
@JsNonModule
external fun bar(): Unit
// MODULE: m2(m1)
// MODULE_KIND: AMD
// TODO: it's hard to test @JsNonModule on file from an external module
// FILE: c.kt
package bar
import foo.*
fun box() {
A.f()+A.g
B()
bar()
B.Nested()
println(::bar.name)
println(A::f.name)
boo<B?>(null)
boo(null as B?)
boo<B.Nested?>(null)
println(B::class)
println(B.Nested::class)
}
external class DerivedB : B
inline fun <reified T> boo(x: T) {
println("${T::class.simpleName}: $x")
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// MODULE: m1
// FILE: a.kt
package foo
@@ -44,10 +45,13 @@ fun box() {
println(<!CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM!>B::class<!>)
println(<!CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM!>B.Nested::class<!>)
val x: Any = 1
println(x is B)
}
external class DerivedB : <!CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM!>B<!>
inline fun <reified T> boo(x: T) {
println("${T::class.simpleName}: $x")
}
}
@@ -224,7 +224,6 @@ open class FirFrontendFacade(
testServices,
configuration,
extensionRegistrars,
languageVersionSettings,
registerExtraComponents = ::registerExtraComponents,
)
}
@@ -380,7 +379,7 @@ open class FirFrontendFacade(
moduleData,
sessionProvider,
extensionRegistrars,
languageVersionSettings,
testServices.compilerConfigurationProvider.getCompilerConfiguration(module),
null,
registerExtraComponents = ::registerExtraComponents,
sessionConfigurator,
@@ -29,7 +29,6 @@ object TestFirJsSessionFactory {
testServices: TestServices,
configuration: CompilerConfiguration,
extensionRegistrars: List<FirExtensionRegistrar>,
languageVersionSettings: LanguageVersionSettings,
registerExtraComponents: ((FirSession) -> Unit),
): FirSession {
val resolvedLibraries = resolveLibraries(configuration, getAllJsDependenciesPaths(module, testServices))
@@ -40,14 +39,17 @@ object TestFirJsSessionFactory {
sessionProvider,
moduleDataProvider,
extensionRegistrars,
languageVersionSettings,
configuration,
registerExtraComponents,
)
}
fun createModuleBasedSession(
mainModuleData: FirModuleData, sessionProvider: FirProjectSessionProvider, extensionRegistrars: List<FirExtensionRegistrar>,
languageVersionSettings: LanguageVersionSettings, lookupTracker: LookupTracker?,
mainModuleData: FirModuleData,
sessionProvider: FirProjectSessionProvider,
extensionRegistrars: List<FirExtensionRegistrar>,
configuration: CompilerConfiguration,
lookupTracker: LookupTracker?,
registerExtraComponents: ((FirSession) -> Unit),
sessionConfigurator: FirSessionConfigurator.() -> Unit,
): FirSession =
@@ -55,7 +57,7 @@ object TestFirJsSessionFactory {
mainModuleData,
sessionProvider,
extensionRegistrars,
languageVersionSettings,
configuration,
lookupTracker,
icData = null,
registerExtraComponents,