fix(parser): reject new import member access (#23459)

Reject `new import(...).prop`, `new import.source(...).prop`, and `new import.defer(...).prop` by treating member access on an unparenthesized import expression as invalid after `new`.

Fixes #23458
This commit is contained in:
camc314 committed 2026-07-15 10:38:34 +00:00
1 parent 90ae04009e
commit 7b4baff16c
5 files changed
+636 -129

No files matched your search

+19 -1
View File
@@ -23,6 +23,24 @@ use crate::{
modifiers::Modifiers,
};
fn is_import_expression_or_member_access_on_import_expression(callee: &Expression<'_>) -> bool {
let mut expr = callee;
loop {
if matches!(expr, Expression::ImportExpression(_)) {
return true;
}
let Some(member_expr) = expr.as_member_expression() else {
expr = match expr {
Expression::TaggedTemplateExpression(tagged_template) => &tagged_template.tag,
Expression::TSNonNullExpression(non_null) => &non_null.expression,
_ => return false,
};
continue;
};
expr = member_expr.object();
}
}
impl<'a, C: Config> ParserImpl<'a, C> {
pub(crate) fn parse_paren_expression(&mut self) -> Expression<'a> {
let opening_span = self.cur_token().span();
@@ -1036,7 +1054,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
ArenaVec::new_in(self)
};
if is_import && matches!(callee, Expression::ImportExpression(_)) {
if is_import && is_import_expression_or_member_access_on_import_expression(&callee) {
self.error(diagnostics::new_dynamic_import(self.end_span(rhs_span)));
}
+4
View File
@@ -0,0 +1,4 @@
new import('').prop;
new import.source('<module source>').prop;
new import.defer('./empty_FIXTURE.js').prop;
new import('x').prop``;
+3
View File
@@ -0,0 +1,3 @@
new import('x').prop!;
new import('x').prop as Foo;
new import('x').prop satisfies Foo;
+60 -1
View File
@@ -1,7 +1,7 @@
parser_misc Summary:
AST Parsed : 71/71 (100.00%)
Positive Passed: 71/71 (100.00%)
Negative Passed: 150/150 (100.00%)
Negative Passed: 152/152 (100.00%)
× Cannot assign to 'arguments' in strict mode
╭─[misc/fail/arguments-eval.ts:1:10]
@@ -3602,6 +3602,65 @@ Negative Passed: 150/150 (100.00%)
· ─────────
╰────
× Cannot use new with dynamic import
╭─[misc/fail/oxc-23458.js:1:5]
1 │ new import('').prop;
· ───────────────
2 │ new import.source('<module source>').prop;
╰────
help: Wrap this with parenthesis
× Cannot use new with dynamic import
╭─[misc/fail/oxc-23458.js:2:5]
1 │ new import('').prop;
2 │ new import.source('<module source>').prop;
· ─────────────────────────────────────
3 │ new import.defer('./empty_FIXTURE.js').prop;
╰────
help: Wrap this with parenthesis
× Cannot use new with dynamic import
╭─[misc/fail/oxc-23458.js:3:5]
2 │ new import.source('<module source>').prop;
3 │ new import.defer('./empty_FIXTURE.js').prop;
· ───────────────────────────────────────
4 │ new import('x').prop``;
╰────
help: Wrap this with parenthesis
× Cannot use new with dynamic import
╭─[misc/fail/oxc-23458.js:4:5]
3 │ new import.defer('./empty_FIXTURE.js').prop;
4 │ new import('x').prop``;
· ──────────────────
╰────
help: Wrap this with parenthesis
× Cannot use new with dynamic import
╭─[misc/fail/oxc-23458.ts:1:5]
1 │ new import('x').prop!;
· ─────────────────
2 │ new import('x').prop as Foo;
╰────
help: Wrap this with parenthesis
× Cannot use new with dynamic import
╭─[misc/fail/oxc-23458.ts:2:5]
1 │ new import('x').prop!;
2 │ new import('x').prop as Foo;
· ────────────────
3 │ new import('x').prop satisfies Foo;
╰────
help: Wrap this with parenthesis
× Cannot use new with dynamic import
╭─[misc/fail/oxc-23458.ts:3:5]
2 │ new import('x').prop as Foo;
3 │ new import('x').prop satisfies Foo;
· ────────────────
╰────
help: Wrap this with parenthesis
× Expected 'with' in import type options
╭─[misc/fail/oxc-2394.ts:20:22]
19 │ export type LocalInterface =
File diff suppressed because it is too large. Load diff