fix(parser): Disallow ReservedWord in NamedExports (#1230)

- fix: #1222

---------

Co-authored-by: Boshen <boshenc@gmail.com>
This commit is contained in:
magic-akari 2023-11-12 18:52:02 +08:00 committed by GitHub
parent f775488102
commit 9c0aafcd1c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 48 additions and 12 deletions

View file

@ -2063,7 +2063,7 @@ impl fmt::Display for ModuleExportName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self { let s = match self {
Self::Identifier(identifier) => identifier.name.to_string(), Self::Identifier(identifier) => identifier.name.to_string(),
Self::StringLiteral(literal) => literal.value.to_string(), Self::StringLiteral(literal) => format!(r#""{}""#, literal.value),
}; };
write!(f, "{s}") write!(f, "{s}")
} }

View file

@ -2,7 +2,7 @@ use oxc_diagnostics::{
miette::{self, Diagnostic}, miette::{self, Diagnostic},
thiserror::{self, Error}, thiserror::{self, Error},
}; };
use oxc_span::{Atom, Span}; use oxc_span::Span;
#[derive(Debug, Error, Diagnostic)] #[derive(Debug, Error, Diagnostic)]
#[error("Flow is not supported")] #[error("Flow is not supported")]
@ -215,8 +215,13 @@ pub struct ExportLoneSurrogate(#[label] pub Span);
#[derive(Debug, Error, Diagnostic)] #[derive(Debug, Error, Diagnostic)]
#[error("A string literal cannot be used as an exported binding without `from`")] #[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'`?"))] #[diagnostic(help("Did you mean `export {{ {0} as {1} }} from 'some-module'`?"))]
pub struct ExportNamedString(pub Atom, pub Atom, #[label] pub Span); 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)] #[derive(Debug, Error, Diagnostic)]
#[error("Bad escape sequence in untagged template literal")] #[error("Bad escape sequence in untagged template literal")]

View file

@ -239,15 +239,32 @@ impl<'a> Parser<'a> {
}; };
// ExportDeclaration : export NamedExports ; // ExportDeclaration : export NamedExports ;
// * It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals.
if source.is_none() { if source.is_none() {
for specifier in &specifiers { for specifier in &specifiers {
if let ModuleExportName::StringLiteral(literal) = &specifier.local { match &specifier.local {
self.error(diagnostics::ExportNamedString( // It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals.
literal.value.clone(), ModuleExportName::StringLiteral(literal) => {
specifier.local.name().clone(), self.error(diagnostics::ExportNamedString(
literal.span, 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,
));
}
}
} }
} }
} }

View file

@ -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 × 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] ╭─[esprima/es2015-export-declaration/invalid-export-default-token/input.js:1:1]
1 │ export {default} + 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 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 × Export 'default' is not defined
╭─[esprima/es2015-export-declaration/invalid-export-named-default/input.js:1:1] ╭─[esprima/es2015-export-declaration/invalid-export-named-default/input.js:1:1]
1 │ export {default} 1 │ export {default}

View file

@ -19729,7 +19729,7 @@ Negative Passed: 3918/3918 (100.00%)
· ───── · ─────
22 │ 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 × An export name cannot include a unicode lone surrogate
╭─[language/module-code/export-expname-unpaired-surrogate.js:20:1] ╭─[language/module-code/export-expname-unpaired-surrogate.js:20:1]