mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
fix(parser): reject escaped import-phase keywords (#26534)
The parser consumes `source`, `defer`, and sometimes `from` as binding identifiers before resolving their role in an import declaration. Validate escapes once they are known to be keywords, so declarations such as `import s\u006furce wasm from "m"` and `import source fr\u006fm "m"` report an error. Escaped binding names remain valid, including `import s\u006furce from "m"` and `import source fr\u006fm from "m"`.
This commit is contained in:
@@ -174,9 +174,9 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
} else if token_after_import.kind() == Kind::Source {
|
||||
if self.cur_kind().is_binding_identifier() {
|
||||
// `import source something ...`
|
||||
let kind = self.cur_kind();
|
||||
let token = self.cur_token();
|
||||
let identifier_after_source = self.parse_binding_identifier();
|
||||
if kind == Kind::From {
|
||||
if token.kind() == Kind::From {
|
||||
// `import source from ...`
|
||||
if self.at(Kind::From) {
|
||||
// `import source from from ...`
|
||||
@@ -185,6 +185,9 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
has_default_specifier = true;
|
||||
} else if self.at(Kind::Str) {
|
||||
// `import source from 'source'`
|
||||
if token.escaped() {
|
||||
self.error(diagnostics::escaped_keyword(token.span()));
|
||||
}
|
||||
has_default_specifier = true;
|
||||
should_parse_specifiers = false;
|
||||
}
|
||||
@@ -213,6 +216,11 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
// else: `import source from 'source'` - source is the binding name, no phase
|
||||
}
|
||||
|
||||
// The first identifier is a keyword only after resolving the import phase.
|
||||
if phase.is_some() && token_after_import.escaped() {
|
||||
self.error(diagnostics::escaped_keyword(token_after_import.span()));
|
||||
}
|
||||
|
||||
let specifiers = if self.at(Kind::Str) {
|
||||
if has_default_specifier && !should_parse_specifiers {
|
||||
match identifier_after_import {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import s\u006furce wasm from "m";
|
||||
import d\u0065fer * as ns from "m";
|
||||
import source fr\u006fm "m";
|
||||
import s\u006furce from from "m";
|
||||
@@ -0,0 +1,5 @@
|
||||
import s\u006furce from "m";
|
||||
import d\u0065fer from "m";
|
||||
import source fr\u006fm from "m";
|
||||
import source w\u0061sm from "m";
|
||||
import defer * as n\u0073 from "m";
|
||||
@@ -1,3 +1,3 @@
|
||||
codegen_misc Summary:
|
||||
AST Parsed : 105/105 (100.00%)
|
||||
Positive Passed: 105/105 (100.00%)
|
||||
AST Parsed : 106/106 (100.00%)
|
||||
Positive Passed: 106/106 (100.00%)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
formatter_misc Summary:
|
||||
AST Parsed : 105/105 (100.00%)
|
||||
Positive Passed: 105/105 (100.00%)
|
||||
AST Parsed : 106/106 (100.00%)
|
||||
Positive Passed: 106/106 (100.00%)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
lexer_misc Summary:
|
||||
AST Parsed : 319/319 (100.00%)
|
||||
Positive Passed: 319/319 (100.00%)
|
||||
AST Parsed : 321/321 (100.00%)
|
||||
Positive Passed: 321/321 (100.00%)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
parser_misc Summary:
|
||||
AST Parsed : 105/105 (100.00%)
|
||||
Positive Passed: 105/105 (100.00%)
|
||||
Negative Passed: 214/214 (100.00%)
|
||||
AST Parsed : 106/106 (100.00%)
|
||||
Positive Passed: 106/106 (100.00%)
|
||||
Negative Passed: 215/215 (100.00%)
|
||||
|
||||
× Cannot assign to 'arguments' in strict mode
|
||||
╭─[misc/fail/arguments-eval-ambient.ts:2:13]
|
||||
@@ -494,6 +494,36 @@ Negative Passed: 214/214 (100.00%)
|
||||
╰────
|
||||
help: for octal literals use the '0o' prefix instead
|
||||
|
||||
× Keywords cannot contain escape characters
|
||||
╭─[misc/fail/escaped-import-phase.js:1:8]
|
||||
1 │ import s\u006furce wasm from "m";
|
||||
· ───────────
|
||||
2 │ import d\u0065fer * as ns from "m";
|
||||
╰────
|
||||
|
||||
× Keywords cannot contain escape characters
|
||||
╭─[misc/fail/escaped-import-phase.js:2:8]
|
||||
1 │ import s\u006furce wasm from "m";
|
||||
2 │ import d\u0065fer * as ns from "m";
|
||||
· ──────────
|
||||
3 │ import source fr\u006fm "m";
|
||||
╰────
|
||||
|
||||
× Keywords cannot contain escape characters
|
||||
╭─[misc/fail/escaped-import-phase.js:3:15]
|
||||
2 │ import d\u0065fer * as ns from "m";
|
||||
3 │ import source fr\u006fm "m";
|
||||
· ─────────
|
||||
4 │ import s\u006furce from from "m";
|
||||
╰────
|
||||
|
||||
× Keywords cannot contain escape characters
|
||||
╭─[misc/fail/escaped-import-phase.js:4:8]
|
||||
3 │ import source fr\u006fm "m";
|
||||
4 │ import s\u006furce from from "m";
|
||||
· ───────────
|
||||
╰────
|
||||
|
||||
× Keywords cannot contain escape characters
|
||||
╭─[misc/fail/escaped-type-import.ts:1:8]
|
||||
1 │ import t\u0079pe Foo = require("m");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
semantic_misc Summary:
|
||||
AST Parsed : 105/105 (100.00%)
|
||||
Positive Passed: 101/105 (96.19%)
|
||||
AST Parsed : 106/106 (100.00%)
|
||||
Positive Passed: 102/106 (96.23%)
|
||||
semantic Error: tasks/coverage/misc/pass/declare-let-private.ts
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["private"]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
transformer_misc Summary:
|
||||
AST Parsed : 105/105 (100.00%)
|
||||
Positive Passed: 105/105 (100.00%)
|
||||
AST Parsed : 106/106 (100.00%)
|
||||
Positive Passed: 106/106 (100.00%)
|
||||
|
||||
Reference in New Issue
Block a user