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 02daa4b5d79..be34d9b26f4 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 @@ -4829,6 +4829,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJsErrors.NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE) { firDiagnostic -> + NonExternalDeclarationInInappropriateFileImpl( + firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirJsErrors.DELEGATION_BY_DYNAMIC) { firDiagnostic -> DelegationByDynamicImpl( 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 630dc8aca14..bb25a7176dc 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 @@ -3356,6 +3356,11 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = NonAbstractMemberOfExternalInterface::class } + abstract class NonExternalDeclarationInInappropriateFile : KtFirDiagnostic() { + override val diagnosticClass get() = NonExternalDeclarationInInappropriateFile::class + abstract val type: KtType + } + abstract class DelegationByDynamic : KtFirDiagnostic() { override val diagnosticClass get() = DelegationByDynamic::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 52d24b6176d..a4f50c125a3 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 @@ -4063,6 +4063,12 @@ internal class NonAbstractMemberOfExternalInterfaceImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.NonAbstractMemberOfExternalInterface(), KtAbstractFirDiagnostic +internal class NonExternalDeclarationInInappropriateFileImpl( + override val type: KtType, + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.NonExternalDeclarationInInappropriateFile(), KtAbstractFirDiagnostic + internal class DelegationByDynamicImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, 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 0713e236d41..9df5c7b4b13 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 @@ -76,6 +76,9 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") { val INLINE_CLASS_IN_EXTERNAL_DECLARATION by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) val EXTENSION_FUNCTION_IN_EXTERNAL_DECLARATION by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) val NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) + val NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) { + parameter("type") + } } val DYNAMICS by object : DiagnosticGroup("Dynamics") { 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 fa5612f360c..9cf15da9642 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 @@ -67,6 +67,7 @@ object FirJsErrors { val INLINE_CLASS_IN_EXTERNAL_DECLARATION by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val EXTENSION_FUNCTION_IN_EXTERNAL_DECLARATION by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + val NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE by error1(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) // Dynamics val DELEGATION_BY_DYNAMIC by error0() 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 0cd6d12c996..cd0af304f1c 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 @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_SETTE import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_CLASS_IN_EXTERNAL_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_EXTERNAL_DECLARATION import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE +import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.RUNTIME_ANNOTATION_NOT_SUPPORTED @@ -129,6 +130,11 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() { KtDiagnosticRenderers.TO_STRING, CommonRenderers.STRING ) + map.put( + NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE, + "Can''t put non-external declarations in file marked with {0} annotation", + FirDiagnosticRenderers.RENDER_TYPE + ) map.checkMissingMessages(FirJsErrors) } diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt index bde8c352d44..b4b47930b93 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt @@ -25,6 +25,7 @@ object JsDeclarationCheckers : DeclarationCheckers() { FirJsModuleChecker, FirJsRuntimeAnnotationChecker, FirJsExternalChecker, + FirJsExternalFileChecker, ) override val classCheckers: Set diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsExternalFileChecker.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsExternalFileChecker.kt new file mode 100644 index 00000000000..036a8daef97 --- /dev/null +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsExternalFileChecker.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.js.checkers.declaration + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.closestNonLocalWith +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker +import org.jetbrains.kotlin.fir.analysis.js.checkers.isNativeObject +import org.jetbrains.kotlin.fir.analysis.checkers.isTopLevel +import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.fir.declarations.fullyExpandedClassId +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.name.JsStandardClassIds +import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull + +object FirJsExternalFileChecker : FirBasicDeclarationChecker() { + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + val closestNonLocal = context.closestNonLocalWith(declaration)?.symbol ?: return + + if (closestNonLocal.isNativeObject(context) || !context.isTopLevel) { + return + } + + val targetAnnotations = context.containingDeclarations + .lastIsInstanceOrNull() + ?.annotations + ?.firstOrNull { it.fullyExpandedClassId(context.session) in JsStandardClassIds.Annotations.annotationsRequiringExternal } + + if (targetAnnotations != null) { + reporter.reportOn( + declaration.source, + FirJsErrors.NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE, + targetAnnotations.typeRef.coneType, + context + ) + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.fir.kt deleted file mode 100644 index f755c753b7e..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.fir.kt +++ /dev/null @@ -1,9 +0,0 @@ -@file:JsModule("lib") - -class A { - class B - - fun bar() {} -} - -fun foo() = "OK" diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.kt b/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.kt index 15ae43868bd..ef68ea41e94 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/module/jsModuleNonExternal.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL @file:JsModule("lib") class A { diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.fir.kt deleted file mode 100644 index ba10220fa13..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.fir.kt +++ /dev/null @@ -1,9 +0,0 @@ -@file:JsQualifier("a.b") - -class A { - class B - - fun bar() {} -} - -fun foo() = "OK" diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.kt b/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.kt index 2d7bf8e733d..3eac101d1ac 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/qualifier/jsQualifierNonExternal.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL @file:JsQualifier("a.b") class A { diff --git a/core/compiler.common.js/src/org/jetbrains/kotlin/name/JsStandardClassIds.kt b/core/compiler.common.js/src/org/jetbrains/kotlin/name/JsStandardClassIds.kt index 1217c3e564d..1523be99afe 100644 --- a/core/compiler.common.js/src/org/jetbrains/kotlin/name/JsStandardClassIds.kt +++ b/core/compiler.common.js/src/org/jetbrains/kotlin/name/JsStandardClassIds.kt @@ -20,6 +20,8 @@ object JsStandardClassIds { val JsNativeInvoke = "nativeInvoke".jsId() val JsNativeGetter = "nativeGetter".jsId() val JsNativeSetter = "nativeSetter".jsId() + + val annotationsRequiringExternal = setOf(JsModule, JsQualifier) } object Callables { diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsExternalFileChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsExternalFileChecker.kt index e0950ed5c77..a0630bd6c20 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsExternalFileChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsExternalFileChecker.kt @@ -18,13 +18,14 @@ package org.jetbrains.kotlin.js.resolve.diagnostics import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils +import org.jetbrains.kotlin.name.JsStandardClassIds import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker object JsExternalFileChecker : DeclarationChecker { - private val annotationFqNames = setOf(AnnotationsUtils.JS_MODULE_ANNOTATION, AnnotationsUtils.JS_QUALIFIER_ANNOTATION) + private val annotationFqNames = JsStandardClassIds.Annotations.annotationsRequiringExternal.map { it.asSingleFqName() } override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { if (!AnnotationsUtils.isNativeObject(descriptor) && DescriptorUtils.isTopLevelDeclaration(descriptor)) {