mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-14 22:29:17 +00:00
fix(parser): reject module export names containing a lone surrogate (#26953)
A module export name that is not well-formed Unicode is an [early error](https://tc39.es/ecma262/#sec-module-semantics-static-semantics-early-errors) (`ModuleExportName : StringLiteral` is a Syntax Error if `IsStringWellFormedUnicode` of its value is false), and the parser reported it, but it still built the node and returned the recovered AST. Every consumer of `ModuleExportName::name` therefore had to tolerate an export name that cannot exist in a valid module. Acorn rejects the input outright, and Babel does the same unless error recovery is enabled. Make the error fatal, so no AST carries an ill-formed export name. Consumers can treat the name as well-formed by construction. This is preparation for the JSStr stack (#26242), where `ModuleExportName::name` keeps returning UTF-8 text and currently needs a fallback for exactly this case; with this change the fallback becomes dead and is removed there. ## Example ```js export { x as "\uD800" }; ``` Before, the error was reported and parsing continued with a recovered AST. After, the same diagnostic is reported and parsing stops: ``` x An export name cannot include a unicode lone surrogate ``` The test262 negative test `early-export-ill-formed-string.js` still fails to parse; it now reports only its first error instead of three, and a duplicate-export diagnostic that printed the surrogate as marker text disappears with it. Verified with four new misc fixtures covering the export, re-export, namespace re-export, and import forms, the parser, semantic, codegen, and lexer conformance runs, and the parser, semantic, and linter test suites. Implemented with AI assistance.
This commit is contained in:
1 parent
d77d5bfe5d
commit
01ff33e9c8
8 files changed
+36
-32
No files matched your search
@@ -982,8 +982,10 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
let literal = self.parse_literal_string();
|
||||
// ModuleExportName : StringLiteral
|
||||
// It is a Syntax Error if IsStringWellFormedUnicode(the SV of StringLiteral) is false.
|
||||
// The error is fatal so that no AST ever carries an ill-formed export name.
|
||||
if !literal.is_string_well_formed_unicode() {
|
||||
self.error(diagnostics::export_lone_surrogate(literal.span));
|
||||
let error = diagnostics::export_lone_surrogate(literal.span);
|
||||
return self.fatal_error(error);
|
||||
}
|
||||
ModuleExportName::StringLiteral(literal)
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { "\uDC00" as y } from "m";
|
||||
@@ -0,0 +1 @@
|
||||
export * as "\uD800" from "m";
|
||||
@@ -0,0 +1,2 @@
|
||||
const x = 1;
|
||||
export { x as "\uD800" };
|
||||
@@ -0,0 +1 @@
|
||||
import { "\uD800" as y } from "m";
|
||||
@@ -1,6 +1,6 @@
|
||||
lexer_misc Summary:
|
||||
AST Parsed : 345/345 (100.00%)
|
||||
Positive Passed: 343/345 (99.42%)
|
||||
AST Parsed : 349/349 (100.00%)
|
||||
Positive Passed: 347/349 (99.43%)
|
||||
Tokens: tasks/coverage/misc/pass/babel-16776-m.js
|
||||
|
||||
Tokens: tasks/coverage/misc/pass/unambiguous-await-regex.js
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
parser_misc Summary:
|
||||
AST Parsed : 111/111 (100.00%)
|
||||
Positive Passed: 111/111 (100.00%)
|
||||
Negative Passed: 234/234 (100.00%)
|
||||
Negative Passed: 238/238 (100.00%)
|
||||
|
||||
× TS(1275): 'accessor' modifier can only appear on a property declaration.
|
||||
╭─[misc/fail/accessor-modifier-abstract-method.ts:1:29]
|
||||
@@ -719,6 +719,25 @@ Negative Passed: 234/234 (100.00%)
|
||||
· ──
|
||||
╰────
|
||||
|
||||
× An export name cannot include a unicode lone surrogate
|
||||
╭─[misc/fail/export-name-lone-surrogate-from.js:1:10]
|
||||
1 │ export { "\uDC00" as y } from "m";
|
||||
· ────────
|
||||
╰────
|
||||
|
||||
× An export name cannot include a unicode lone surrogate
|
||||
╭─[misc/fail/export-name-lone-surrogate-star.js:1:13]
|
||||
1 │ export * as "\uD800" from "m";
|
||||
· ────────
|
||||
╰────
|
||||
|
||||
× An export name cannot include a unicode lone surrogate
|
||||
╭─[misc/fail/export-name-lone-surrogate.js:2:15]
|
||||
1 │ const x = 1;
|
||||
2 │ export { x as "\uD800" };
|
||||
· ────────
|
||||
╰────
|
||||
|
||||
× The left-hand side of a for...in statement cannot be an await using declaration.
|
||||
╭─[misc/fail/for-await-using-in-initializer.js:2:8]
|
||||
1 │ async function f() {
|
||||
@@ -1006,6 +1025,12 @@ Negative Passed: 234/234 (100.00%)
|
||||
· ╰── `from` expected
|
||||
╰────
|
||||
|
||||
× An export name cannot include a unicode lone surrogate
|
||||
╭─[misc/fail/import-name-lone-surrogate.js:1:10]
|
||||
1 │ import { "\uD800" as y } from "m";
|
||||
· ────────
|
||||
╰────
|
||||
|
||||
× Expected `from` but found `Identifier`
|
||||
╭─[misc/fail/import-source-non-from.js:1:19]
|
||||
1 │ import source foo bar from 'module';
|
||||
|
||||
@@ -25603,34 +25603,6 @@ Negative Passed: 4653/4653 (100.00%)
|
||||
22 │ export {"\uD83C"} from "./early-export-ill-formed-string.js";
|
||||
╰────
|
||||
|
||||
× An export name cannot include a unicode lone surrogate
|
||||
╭─[test262/test/language/module-code/early-export-ill-formed-string.js:22:9]
|
||||
21 │ export {Moon as "\uD83C",} from "./early-export-ill-formed-string.js";
|
||||
22 │ export {"\uD83C"} from "./early-export-ill-formed-string.js";
|
||||
· ────────
|
||||
23 │ import {'\uD83C' as Usagi} from "./early-export-ill-formed-string.js";
|
||||
╰────
|
||||
|
||||
× An export name cannot include a unicode lone surrogate
|
||||
╭─[test262/test/language/module-code/early-export-ill-formed-string.js:23:9]
|
||||
22 │ export {"\uD83C"} from "./early-export-ill-formed-string.js";
|
||||
23 │ import {'\uD83C' as Usagi} from "./early-export-ill-formed-string.js";
|
||||
· ────────
|
||||
24 │
|
||||
╰────
|
||||
|
||||
× Duplicated export '�d83c'
|
||||
╭─[test262/test/language/module-code/early-export-ill-formed-string.js:21:17]
|
||||
20 │ // 🌙 is '\uD83C\uDF19'
|
||||
21 │ export {Moon as "\uD83C",} from "./early-export-ill-formed-string.js";
|
||||
· ────┬───
|
||||
· ╰── Export has already been declared here
|
||||
22 │ export {"\uD83C"} from "./early-export-ill-formed-string.js";
|
||||
· ────┬───
|
||||
· ╰── It cannot be redeclared here
|
||||
23 │ import {'\uD83C' as Usagi} from "./early-export-ill-formed-string.js";
|
||||
╰────
|
||||
|
||||
× Export 'unresolvable' is not defined
|
||||
╭─[test262/test/language/module-code/early-export-unresolvable.js:17:10]
|
||||
16 │
|
||||
|
||||
Reference in new issue
Block a user