diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 0348aea8411..937923c9e98 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -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, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 7272b5891c9..4571522b5c5 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -806,6 +806,20 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = NestedJsModuleProhibited::class } + interface CallFromUmdMustBeJsModuleAndJsNonModule : KtFirDiagnostic { + override val diagnosticClass get() = CallFromUmdMustBeJsModuleAndJsNonModule::class + } + + interface CallToJsModuleWithoutModuleSystem : KtFirDiagnostic { + override val diagnosticClass get() = CallToJsModuleWithoutModuleSystem::class + val callee: KtSymbol + } + + interface CallToJsNonModuleWithModuleSystem : KtFirDiagnostic { + override val diagnosticClass get() = CallToJsNonModuleWithModuleSystem::class + val callee: KtSymbol + } + interface RuntimeAnnotationNotSupported : KtFirDiagnostic { override val diagnosticClass get() = RuntimeAnnotationNotSupported::class } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index d55f3f96ad7..cb342651e52 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -967,6 +967,23 @@ internal class NestedJsModuleProhibitedImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.NestedJsModuleProhibited +internal class CallFromUmdMustBeJsModuleAndJsNonModuleImpl( + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.CallFromUmdMustBeJsModuleAndJsNonModule + +internal class CallToJsModuleWithoutModuleSystemImpl( + override val callee: KtSymbol, + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.CallToJsModuleWithoutModuleSystem + +internal class CallToJsNonModuleWithModuleSystemImpl( + override val callee: KtSymbol, + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.CallToJsNonModuleWithModuleSystem + internal class RuntimeAnnotationNotSupportedImpl( firDiagnostic: KtPsiDiagnostic, token: KtLifetimeToken, diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/FirSessionConstructionUtils.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/FirSessionConstructionUtils.kt index 042fd971a9b..8f7d0d6f720 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/FirSessionConstructionUtils.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/FirSessionConstructionUtils.kt @@ -127,7 +127,7 @@ fun prepareJsSessions( sessionProvider, libraryList.moduleDataProvider, extensionRegistrars, - configuration.languageVersionSettings, + configuration, registerExtraComponents = {}, ) } @@ -136,7 +136,7 @@ fun prepareJsSessions( moduleData, sessionProvider, extensionRegistrars, - configuration.languageVersionSettings, + configuration, lookupTracker, icData = icData, registerExtraComponents = {}, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt index 1b971d4445a..dd3644b0565 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt @@ -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(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) val JS_MODULE_PROHIBITED_ON_NON_NATIVE by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) val NESTED_JS_MODULE_PROHIBITED by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) + val CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) + val CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) { + parameter>("callee") + } + val CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) { + parameter>("callee") + } val RUNTIME_ANNOTATION_NOT_SUPPORTED by warning(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) val RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) val NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) { diff --git a/compiler/fir/checkers/checkers.js/build.gradle.kts b/compiler/fir/checkers/checkers.js/build.gradle.kts index 2ba0c1ed649..afed60b76a3 100644 --- a/compiler/fir/checkers/checkers.js/build.gradle.kts +++ b/compiler/fir/checkers/checkers.js/build.gradle.kts @@ -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 diff --git a/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt b/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt index 5151e36bc8c..109f28f30a9 100644 --- a/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt +++ b/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt @@ -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(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val JS_MODULE_PROHIBITED_ON_NON_NATIVE by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val NESTED_JS_MODULE_PROHIBITED by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + val CALL_FROM_UMD_MUST_BE_JS_MODULE_AND_JS_NON_MODULE by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + val CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM by error1>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + val CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM by error1>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val RUNTIME_ANNOTATION_NOT_SUPPORTED by warning0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN by error1(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt index d11ae36369f..c0b94c2a084 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt @@ -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}", diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/FirJsHelpers.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/FirJsHelpers.kt index f18a1a3b5f3..22c18d1ab14 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/FirJsHelpers.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/FirJsHelpers.kt @@ -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 +} diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/FirJsModuleCheckUtils.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/FirJsModuleCheckUtils.kt new file mode 100644 index 00000000000..859b0bd32a3 --- /dev/null +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/FirJsModuleCheckUtils.kt @@ -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) + } + } + } +} diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsExpressionCheckers.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsExpressionCheckers.kt index 727bcb85525..6fcd9654d38 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsExpressionCheckers.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsExpressionCheckers.kt @@ -30,4 +30,14 @@ object JsExpressionCheckers : ExpressionCheckers() { get() = setOf( FirJsExternalArgumentCallChecker ) + + override val getClassCallCheckers: Set + get() = setOf( + FirJsModuleGetClassCallChecker + ) + + override val qualifiedAccessExpressionCheckers: Set + get() = setOf( + FirJsModuleQualifiedAccessChecker + ) } diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsExternalChecker.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsExternalChecker.kt index 9bab5fc85a6..95dd69b5b02 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsExternalChecker.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsExternalChecker.kt @@ -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 } -} \ No newline at end of file +} diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsModuleChecker.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsModuleChecker.kt index 3f3f56f0f65..d52be5a27f1 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsModuleChecker.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsModuleChecker.kt @@ -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) } diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsModuleGetClassCallChecker.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsModuleGetClassCallChecker.kt new file mode 100644 index 00000000000..3b5f6086fc0 --- /dev/null +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsModuleGetClassCallChecker.kt @@ -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) + } +} diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsModuleQualifiedAccessChecker.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsModuleQualifiedAccessChecker.kt new file mode 100644 index 00000000000..27952fa23b5 --- /dev/null +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsModuleQualifiedAccessChecker.kt @@ -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, 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) + } + } + } +} + + diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJsSessionFactory.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJsSessionFactory.kt index 874e4edbc57..86e45a2392d 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJsSessionFactory.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJsSessionFactory.kt @@ -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, - 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, - 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)) } } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.fir.kt deleted file mode 100644 index 421ea176c66..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.fir.kt +++ /dev/null @@ -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() -} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.kt index f42979cf740..8cece715ed8 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/dualModuleFromUmd.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // MODULE: m1 // FILE: a.kt package foo diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.fir.kt deleted file mode 100644 index 9f02d1610d3..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.fir.kt +++ /dev/null @@ -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(null) - boo(null as B?) - boo(null) - - println(B::class) - println(B.Nested::class) -} - -external class DerivedB : B - -inline fun boo(x: T) { - println("${T::class.simpleName}: $x") -} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.kt index 324089e6dad..91c5a97bc1e 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToModule.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // MODULE: m1 // FILE: a.kt package foo @@ -52,10 +53,13 @@ fun box() { println(B::class) println(B.Nested::class) + + val x: Any = 1 + println(x is B) } external class DerivedB : B inline fun boo(x: T) { println("${T::class.simpleName}: $x") -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.fir.kt deleted file mode 100644 index 51c3c9a718b..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.fir.kt +++ /dev/null @@ -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(null) - boo(null as B?) - boo(null) - - println(B::class) - println(B.Nested::class) -} - -external class DerivedB : B - -inline fun boo(x: T) { - println("${T::class.simpleName}: $x") -} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.kt index 854ccd49911..ab9b8290d6f 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/wrongCallToNonModule.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // MODULE: m1 // FILE: a.kt package foo @@ -44,10 +45,13 @@ fun box() { println(B::class) println(B.Nested::class) + + val x: Any = 1 + println(x is B) } external class DerivedB : B inline fun boo(x: T) { println("${T::class.simpleName}: $x") -} \ No newline at end of file +} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt index aac28d6a75e..f30e4a6c486 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt @@ -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, diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/TestFirJsSessionFactory.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/TestFirJsSessionFactory.kt index 5b660cf7fbf..01d65c732b9 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/TestFirJsSessionFactory.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/TestFirJsSessionFactory.kt @@ -29,7 +29,6 @@ object TestFirJsSessionFactory { testServices: TestServices, configuration: CompilerConfiguration, extensionRegistrars: List, - 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, - languageVersionSettings: LanguageVersionSettings, lookupTracker: LookupTracker?, + mainModuleData: FirModuleData, + sessionProvider: FirProjectSessionProvider, + extensionRegistrars: List, + 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,