diff --git a/crates/oxc_semantic/src/checker/mod.rs b/crates/oxc_semantic/src/checker/mod.rs index a3b811782930c..9ba329ef60256 100644 --- a/crates/oxc_semantic/src/checker/mod.rs +++ b/crates/oxc_semantic/src/checker/mod.rs @@ -14,6 +14,7 @@ pub fn check<'a>(kind: AstKind<'a>, ctx: &SemanticBuilder<'a>) { AstKind::Program(program) => { js::check_duplicate_class_elements(ctx); js::check_unresolved_exports(program, ctx); + ts::check_ts_export_assignment_in_program(program, ctx); } AstKind::BindingIdentifier(ident) => { js::check_identifier(&ident.name, ident.span, ident.symbol_id.get(), ctx); diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs index 1ef689e50ffd0..7d2cbe88130f4 100644 --- a/crates/oxc_semantic/src/checker/typescript.rs +++ b/crates/oxc_semantic/src/checker/typescript.rs @@ -157,6 +157,7 @@ fn not_allowed_namespace_declaration(span: Span) -> OxcDiagnostic { pub fn check_ts_module_declaration<'a>(decl: &TSModuleDeclaration<'a>, ctx: &SemanticBuilder<'a>) { check_ts_module_or_global_declaration(decl.span, ctx); + check_ts_export_assignment_in_module_decl(decl, ctx); } pub fn check_ts_global_declaration<'a>(decl: &TSGlobalDeclaration<'a>, ctx: &SemanticBuilder<'a>) { @@ -449,3 +450,66 @@ pub fn check_jsx_expression_container( ctx.error(jsx_expressions_may_not_use_the_comma_operator(container.expression.span())); } } + +fn ts_export_assignment_cannot_be_used_with_other_exports(span: Span) -> OxcDiagnostic { + ts_error("2309", "An export assignment cannot be used in a module with other exported elements") + .with_label(span) + .with_help("If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement.") +} + +pub fn check_ts_export_assignment_in_program<'a>(program: &Program<'a>, ctx: &SemanticBuilder<'a>) { + if !ctx.source_type.is_typescript() { + return; + } + check_ts_export_assignment_in_statements(&program.body, ctx); +} + +fn check_ts_export_assignment_in_module_decl<'a>( + module_decl: &TSModuleDeclaration<'a>, + ctx: &SemanticBuilder<'a>, +) { + let Some(body) = &module_decl.body else { + return; + }; + match body { + TSModuleDeclarationBody::TSModuleDeclaration(nested) => { + check_ts_export_assignment_in_module_decl(nested, ctx); + } + TSModuleDeclarationBody::TSModuleBlock(block) => { + check_ts_export_assignment_in_statements(&block.body, ctx); + } + } +} + +fn check_ts_export_assignment_in_statements<'a>( + statements: &[Statement<'a>], + ctx: &SemanticBuilder<'a>, +) { + let mut export_assignment_spans = vec![]; + let mut has_other_exports = false; + + for stmt in statements { + match stmt { + Statement::TSExportAssignment(export_assignment) => { + export_assignment_spans.push(export_assignment.span); + } + Statement::ExportNamedDeclaration(export_decl) => { + // ignore `export {}` + if export_decl.declaration.is_none() && export_decl.specifiers.is_empty() { + continue; + } + has_other_exports = true; + } + Statement::ExportDefaultDeclaration(_) | Statement::ExportAllDeclaration(_) => { + has_other_exports = true; + } + _ => {} + } + } + + if has_other_exports { + for span in export_assignment_spans { + ctx.error(ts_export_assignment_cannot_be_used_with_other_exports(span)); + } + } +} diff --git a/tasks/coverage/misc/fail/export-equal-with-normal-export.ts b/tasks/coverage/misc/fail/export-equal-with-normal-export.ts new file mode 100644 index 0000000000000..624ad0dffe795 --- /dev/null +++ b/tasks/coverage/misc/fail/export-equal-with-normal-export.ts @@ -0,0 +1,5 @@ +export const foo = 1; +export const bar = 2; + +export = 3; +export = 4; diff --git a/tasks/coverage/snapshots/parser_misc.snap b/tasks/coverage/snapshots/parser_misc.snap index 906f2306dc0b1..6aa46687c383b 100644 --- a/tasks/coverage/snapshots/parser_misc.snap +++ b/tasks/coverage/snapshots/parser_misc.snap @@ -1,7 +1,7 @@ parser_misc Summary: AST Parsed : 52/52 (100.00%) Positive Passed: 52/52 (100.00%) -Negative Passed: 120/120 (100.00%) +Negative Passed: 121/121 (100.00%) × Cannot assign to 'arguments' in strict mode ╭─[misc/fail/arguments-eval.ts:1:10] @@ -79,6 +79,23 @@ Negative Passed: 120/120 (100.00%) ╰──── help: for octal literals use the '0o' prefix instead + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[misc/fail/export-equal-with-normal-export.ts:4:1] + 3 │ + 4 │ export = 3; + · ─────────── + 5 │ export = 4; + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[misc/fail/export-equal-with-normal-export.ts:5:1] + 4 │ export = 3; + 5 │ export = 4; + · ─────────── + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × Expected `,` or `]` but found `const` ╭─[misc/fail/imbalanced-array-expr.js:2:1] 1 │ const foo = [0, 1 diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index a03703b7c9a6a..aea4b8efc1b36 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -3,11 +3,7 @@ commit: 669c25c0 parser_typescript Summary: AST Parsed : 9821/9822 (99.99%) Positive Passed: 9812/9822 (99.90%) -Negative Passed: 1467/2547 (57.60%) -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ExportAssignment7.ts - -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ExportAssignment8.ts - +Negative Passed: 1482/2547 (58.19%) Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/FunctionDeclaration3.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/FunctionDeclaration4.ts @@ -224,8 +220,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/declarationE Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/declarationEmitRelativeModuleError.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/declarationFileNoCrashOnExtraExportModifier.ts - Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/declareModifierOnImport1.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/decoratorsOnComputedProperties.ts @@ -326,8 +320,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/enumsWithMul Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/erasableSyntaxOnlyDeclaration.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/errorForConflictingExportEqualsValue.ts - Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/errorForUsingPropertyOfTypeAsType03.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/errorMessageOnObjectLiteralType.ts @@ -336,16 +328,12 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/errorSupress Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/es5-oldStyleOctalLiteralInEnums.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/es5ExportEquals.ts - Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/es5ModuleInternalNamedImports.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/es6ClassTest.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/es6DeclOrdering.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/es6ExportEquals.ts - Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/es6ExportEqualsInterop.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/es6ImportWithoutFromClause.ts @@ -368,8 +356,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/exportAsName Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/exportAssignmentImportMergeNoCrash.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/exportAssignmentWithExports.ts - Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/exportDeclarationsInAmbientNamespaces2.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/exportDeclareClass1.ts @@ -498,18 +484,10 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/importAndVar Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/importDeclWithClassModifiers.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/importDeclWithExportModifierAndExportAssignment.ts - -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/importDeclWithExportModifierAndExportAssignmentInAmbientContext.ts - Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/importWithTrailingSlash.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/importedEnumMemberMergedWithExportedAliasIsError.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/incompatibleExports1.ts - -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/incompatibleExports2.ts - Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/incrementOnTypeParameter.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/indexedAccessWithFreshObjectLiteral.ts @@ -1490,8 +1468,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalM Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/duplicateExportAssignments.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/exportAssignmentAndDeclaration.ts - Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/importTsBeforeDTs.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/importsImplicitlyReadonly.ts @@ -1532,8 +1508,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalM Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/valuesMergingAcrossModules.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/verbatimModuleSyntaxRestrictionsCJS.ts - Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/fixSignatureCaching.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/functions/functionImplementationErrors.ts @@ -1816,10 +1790,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ec Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ExportAssignments/parserExportAssignment5.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ExportAssignments/parserExportAssignment7.ts - -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ExportAssignments/parserExportAssignment8.ts - Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ExportAssignments/parserExportAssignment9.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/FunctionDeclarations/parserFunctionDeclaration1.ts @@ -2511,6 +2481,22 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc ╰──── help: Did you mean `readonly`? + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/ExportAssignment7.ts:4:1] + 3 │ + 4 │ export = B; + · ─────────── + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/ExportAssignment8.ts:1:1] + 1 │ export = B; + · ─────────── + 2 │ + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × TS(2414): Interface name cannot be 'string' ╭─[typescript/tests/cases/compiler/InterfaceDeclaration8.ts:1:11] 1 │ interface string { @@ -4940,6 +4926,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc 2 │ export default Foo ╰──── + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/declarationFileNoCrashOnExtraExportModifier.ts:1:1] + 1 │ export = exports; + · ───────────────── + 2 │ declare class exports { + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × TS(1030): 'declare' modifier already seen. ╭─[typescript/tests/cases/compiler/declareAlreadySeen.ts:2:13] 1 │ namespace M { @@ -5736,6 +5730,15 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc 23 │ const x = ({ [foo.bar]: c }) => undefined; ╰──── + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/errorForConflictingExportEqualsValue.ts:2:1] + 1 │ export var x; + 2 │ export = x; + · ─────────── + 3 │ import("./a"); + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × Unexpected token ╭─[typescript/tests/cases/compiler/errorForUsingPropertyOfTypeAsType01.ts:44:24] 43 │ @@ -5826,6 +5829,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc 25 │ with (z) { ╰──── + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/es5ExportEquals.ts:3:1] + 2 │ + 3 │ export = f; + · ─────────── + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × Expected `{` but found `(` ╭─[typescript/tests/cases/compiler/es6ClassTest9.ts:1:18] 1 │ declare class foo(); @@ -5834,6 +5845,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc 2 │ function foo() {} ╰──── + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/es6ExportEquals.ts:3:1] + 2 │ + 3 │ export = f; + · ─────────── + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × Unexpected export. ╭─[typescript/tests/cases/compiler/es6ImportDefaultBindingFollowedWithNamedImport1WithExport.ts:1:8] 1 │ export import defaultBinding1, { } from "./server"; @@ -6219,6 +6238,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc · ────── ╰──── + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/exportAssignmentWithExports.ts:3:1] + 2 │ class D { } + 3 │ export = D; + · ─────────── + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × A class name is required. ╭─[typescript/tests/cases/compiler/exportClassWithoutName.ts:1:8] 1 │ export class { @@ -6715,6 +6742,23 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc 7 │ var b: a; ╰──── + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/importDeclWithExportModifierAndExportAssignment.ts:6:1] + 5 │ export import a = x.c; + 6 │ export = x; + · ─────────── + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/importDeclWithExportModifierAndExportAssignmentInAmbientContext.ts:7:5] + 6 │ export import a = x.c; + 7 │ export = x; + · ─────────── + 8 │ } + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × Cannot use import statement outside a module ╭─[typescript/tests/cases/compiler/importDeclarationInModuleDeclaration2.ts:2:5] 1 │ function container() { @@ -6723,6 +6767,33 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc 3 │ } ╰──── + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/incompatibleExports1.ts:4:5] + 3 │ interface y { a: Date } + 4 │ export = y; + · ─────────── + 5 │ } + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/incompatibleExports1.ts:16:5] + 15 │ + 16 │ export = c; + · ─────────── + 17 │ } + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/compiler/incompatibleExports2.ts:4:5] + 3 │ interface y { a: Date } + 4 │ export = y; + · ─────────── + 5 │ } + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × Unexpected token ╭─[typescript/tests/cases/compiler/incompleteDottedExpressionAtEOF.ts:2:18] 1 │ // used to leak __missing into error message @@ -12234,6 +12305,15 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc 21 │ ╰──── + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/conformance/ambient/ambientErrors.ts:57:5] + 56 │ export var q; + 57 │ export = n; + · ─────────── + 58 │ } + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × Identifier expected. 'debugger' is a reserved word that cannot be used here. ╭─[typescript/tests/cases/conformance/ambient/ambientModuleDeclarationWithReservedIdentifierInDottedPath.ts:3:26] 2 │ @@ -19230,6 +19310,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc · ─ ╰──── + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/conformance/externalModules/exportAssignmentAndDeclaration.ts:10:1] + 9 │ // Invalid, as there is already an exported member. + 10 │ export = C1; + · ──────────── + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × Unexpected token ╭─[typescript/tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts:1:4] 1 │ var; @@ -19609,6 +19697,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc 4 │ declare export as namespace oo2; ╰──── + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/conformance/externalModules/verbatimModuleSyntaxRestrictionsCJS.ts:2:1] + 1 │ export interface I {} + 2 │ export = { x: 1 }; + · ────────────────── + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × Identifier `fn2` has already been declared ╭─[typescript/tests/cases/conformance/functions/functionNameConflicts.ts:8:9] 7 │ @@ -21734,6 +21830,22 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc · ─ ╰──── + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ExportAssignments/parserExportAssignment7.ts:4:1] + 3 │ + 4 │ export = B; + · ─────────── + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + + × TS(2309): An export assignment cannot be used in a module with other exported elements + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ExportAssignments/parserExportAssignment8.ts:1:1] + 1 │ export = B; + · ─────────── + 2 │ + ╰──── + help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement. + × Unexpected token ╭─[typescript/tests/cases/conformance/parser/ecmascript5/Expressions/parseIncompleteBinaryExpression1.ts:1:9] 1 │ var v = || b;