From 9c0aafcd1cf8fd5a3772295edf9a3b5fb2b4951c Mon Sep 17 00:00:00 2001 From: magic-akari Date: Sun, 12 Nov 2023 18:52:02 +0800 Subject: [PATCH] fix(parser): Disallow ReservedWord in NamedExports (#1230) - fix: #1222 --------- Co-authored-by: Boshen --- crates/oxc_ast/src/ast/js.rs | 2 +- crates/oxc_parser/src/diagnostics.rs | 11 +++++++--- crates/oxc_parser/src/js/module.rs | 31 +++++++++++++++++++++------- tasks/coverage/parser_babel.snap | 14 +++++++++++++ tasks/coverage/parser_test262.snap | 2 +- 5 files changed, 48 insertions(+), 12 deletions(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 45ffa01ef..9af9a9a09 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -2063,7 +2063,7 @@ impl fmt::Display for ModuleExportName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match self { Self::Identifier(identifier) => identifier.name.to_string(), - Self::StringLiteral(literal) => literal.value.to_string(), + Self::StringLiteral(literal) => format!(r#""{}""#, literal.value), }; write!(f, "{s}") } diff --git a/crates/oxc_parser/src/diagnostics.rs b/crates/oxc_parser/src/diagnostics.rs index ee0d4ed6d..27d7c4e01 100644 --- a/crates/oxc_parser/src/diagnostics.rs +++ b/crates/oxc_parser/src/diagnostics.rs @@ -2,7 +2,7 @@ use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, }; -use oxc_span::{Atom, Span}; +use oxc_span::Span; #[derive(Debug, Error, Diagnostic)] #[error("Flow is not supported")] @@ -215,8 +215,13 @@ pub struct ExportLoneSurrogate(#[label] pub Span); #[derive(Debug, Error, Diagnostic)] #[error("A string literal cannot be used as an exported binding without `from`")] -#[diagnostic(help("Did you mean `export {{ '{0}' as '{1}' }} from 'some-module'`?"))] -pub struct ExportNamedString(pub Atom, pub Atom, #[label] pub Span); +#[diagnostic(help("Did you mean `export {{ {0} as {1} }} from 'some-module'`?"))] +pub struct ExportNamedString(pub String, pub String, #[label] pub Span); + +#[derive(Debug, Error, Diagnostic)] +#[error("A reserved word cannot be used as an exported binding without `from`")] +#[diagnostic(help("Did you mean `export {{ {0} as {1} }} from 'some-module'`?"))] +pub struct ExportReservedWord(pub String, pub String, #[label] pub Span); #[derive(Debug, Error, Diagnostic)] #[error("Bad escape sequence in untagged template literal")] diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index 6c4b95bbf..b1b076648 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -239,15 +239,32 @@ impl<'a> Parser<'a> { }; // ExportDeclaration : export NamedExports ; - // * It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals. if source.is_none() { for specifier in &specifiers { - if let ModuleExportName::StringLiteral(literal) = &specifier.local { - self.error(diagnostics::ExportNamedString( - literal.value.clone(), - specifier.local.name().clone(), - literal.span, - )); + match &specifier.local { + // It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals. + ModuleExportName::StringLiteral(literal) => { + self.error(diagnostics::ExportNamedString( + specifier.local.to_string(), + specifier.exported.to_string(), + literal.span, + )); + } + // For each IdentifierName n in ReferencedBindings of NamedExports: + // It is a Syntax Error if StringValue of n is a ReservedWord or the StringValue of n + // is one of "implements", "interface", "let", "package", "private", "protected", "public", or "static". + ModuleExportName::Identifier(id) => { + let match_result = Kind::match_keyword(&id.name); + if match_result.is_reserved_keyword() + || match_result.is_future_reserved_keyword() + { + self.error(diagnostics::ExportReservedWord( + specifier.local.to_string(), + specifier.exported.to_string(), + id.span, + )); + } + } } } } diff --git a/tasks/coverage/parser_babel.snap b/tasks/coverage/parser_babel.snap index 577b3283b..92e988e9f 100644 --- a/tasks/coverage/parser_babel.snap +++ b/tasks/coverage/parser_babel.snap @@ -6947,6 +6947,13 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" · ─ ╰──── + × A reserved word cannot be used as an exported binding without `from` + ╭─[esprima/es2015-export-declaration/invalid-export-default-token/input.js:1:1] + 1 │ export {default} + + · ─────── + ╰──── + help: Did you mean `export { default as default } from 'some-module'`? + × Expected a semicolon or an implicit semicolon after a statement, but found none ╭─[esprima/es2015-export-declaration/invalid-export-default-token/input.js:1:1] 1 │ export {default} + @@ -6954,6 +6961,13 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" ╰──── help: Try insert a semicolon here + × A reserved word cannot be used as an exported binding without `from` + ╭─[esprima/es2015-export-declaration/invalid-export-named-default/input.js:1:1] + 1 │ export {default} + · ─────── + ╰──── + help: Did you mean `export { default as default } from 'some-module'`? + × Export 'default' is not defined ╭─[esprima/es2015-export-declaration/invalid-export-named-default/input.js:1:1] 1 │ export {default} diff --git a/tasks/coverage/parser_test262.snap b/tasks/coverage/parser_test262.snap index 33eabffa2..50c084581 100644 --- a/tasks/coverage/parser_test262.snap +++ b/tasks/coverage/parser_test262.snap @@ -19729,7 +19729,7 @@ Negative Passed: 3918/3918 (100.00%) · ───── 22 │ ╰──── - help: Did you mean `export { 'foo' as 'foo' } from 'some-module'`? + help: Did you mean `export { "foo" as "bar" } from 'some-module'`? × An export name cannot include a unicode lone surrogate ╭─[language/module-code/export-expname-unpaired-surrogate.js:20:1]