[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:
committed by
Space Team
parent
30b012839c
commit
08d158f901
+20
@@ -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,
|
||||
|
||||
+14
@@ -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
|
||||
}
|
||||
|
||||
+17
@@ -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 = {},
|
||||
|
||||
+8
@@ -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
|
||||
|
||||
+4
@@ -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)
|
||||
|
||||
+15
@@ -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}",
|
||||
|
||||
+17
-1
@@ -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
|
||||
}
|
||||
|
||||
+66
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
+2
-5
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -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)
|
||||
}
|
||||
|
||||
+22
@@ -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)
|
||||
}
|
||||
}
|
||||
+68
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-9
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
-29
@@ -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
|
||||
|
||||
-61
@@ -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")
|
||||
}
|
||||
+5
-1
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
-53
@@ -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")
|
||||
}
|
||||
+5
-1
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -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,
|
||||
|
||||
+7
-5
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user