Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 41 additions & 10 deletions crates/oxc_semantic/src/checker/javascript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,19 +512,50 @@ pub fn check_meta_property(prop: &MetaProperty, ctx: &SemanticBuilder<'_>) {
if ctx.source_type.is_commonjs() {
return;
}
let mut in_function_scope = false;
for scope_id in ctx.scoping.scope_ancestors(ctx.current_scope_id) {
let flags = ctx.scoping.scope_flags(scope_id);
// In arrow functions, new.target is inherited from the surrounding scope.
if flags.contains(ScopeFlags::Arrow) {
continue;

// Check if we're in a valid context for new.target:
// 1. Inside a function (including constructor)
// 2. Inside a class static block
// 3. Inside a class field initializer (new.target evaluates to undefined)
//
// Arrow functions inherit new.target from their surrounding scope,
// so we skip them and continue checking the enclosing context.

let mut in_valid_context = false;

// First, check AST ancestors for class field initializers.
// We need to do this because class fields don't have their own scope.
for node_kind in ctx.nodes.ancestor_kinds(ctx.current_node_id) {
match node_kind {
// Regular functions have their own new.target binding.
// Use scope-based check from here.
AstKind::Function(_) => break,
// Class field initializers allow new.target (evaluates to undefined).
// This includes arrow functions nested inside the initializer.
AstKind::PropertyDefinition(_) | AstKind::AccessorProperty(_) => {
in_valid_context = true;
break;
}
_ => {}
}
if flags.intersects(ScopeFlags::Function | ScopeFlags::ClassStaticBlock) {
in_function_scope = true;
break;
}

// If not in a class field, fall back to scope-based check
if !in_valid_context {
for scope_id in ctx.scoping.scope_ancestors(ctx.current_scope_id) {
let flags = ctx.scoping.scope_flags(scope_id);
// In arrow functions, new.target is inherited from the surrounding scope.
if flags.contains(ScopeFlags::Arrow) {
continue;
}
if flags.intersects(ScopeFlags::Function | ScopeFlags::ClassStaticBlock) {
in_valid_context = true;
break;
}
}
}
if !in_function_scope {

if !in_valid_context {
ctx.error(diagnostics::new_target(prop.span));
}
}
Expand Down
4 changes: 3 additions & 1 deletion crates/oxc_semantic/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ pub fn module_code(x0: &str, span1: Span) -> OxcDiagnostic {
#[cold]
pub fn new_target(span: Span) -> OxcDiagnostic {
OxcDiagnostic::error("Unexpected new.target expression")
.with_help("new.target is only allowed in constructors and functions invoked using the `new` operator")
.with_help(
"new.target is only allowed in constructors, functions, and class field initializers",
)
.with_label(span)
}

Expand Down
88 changes: 7 additions & 81 deletions tasks/coverage/snapshots/parser_babel.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ commit: fc58af40

parser_babel Summary:
AST Parsed : 2221/2227 (99.73%)
Positive Passed: 2205/2227 (99.01%)
Positive Passed: 2206/2227 (99.06%)
Negative Passed: 1649/1689 (97.63%)
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2026/explicit-resource-management/invalid-for-using-of-no-initializer/input.js

Expand Down Expand Up @@ -84,80 +84,6 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/ty

Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/types/invalid-import-type-options-with-spread-element/input.ts

Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2022/class-properties/new-target/input.js

× Unexpected new.target expression
╭─[babel/packages/babel-parser/test/fixtures/es2022/class-properties/new-target/input.js:2:14]
1 │ class X {
2 │ static a = new.target;
· ──────────
3 │ static b = (foo = 1 + bar(new.target));
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator

× Unexpected new.target expression
╭─[babel/packages/babel-parser/test/fixtures/es2022/class-properties/new-target/input.js:3:29]
2 │ static a = new.target;
3 │ static b = (foo = 1 + bar(new.target));
· ──────────
4 │ static c = () => new.target;
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator

× Unexpected new.target expression
╭─[babel/packages/babel-parser/test/fixtures/es2022/class-properties/new-target/input.js:4:20]
3 │ static b = (foo = 1 + bar(new.target));
4 │ static c = () => new.target;
· ──────────
5 │ static d = (foo = new.target) => {};
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator

× Unexpected new.target expression
╭─[babel/packages/babel-parser/test/fixtures/es2022/class-properties/new-target/input.js:5:21]
4 │ static c = () => new.target;
5 │ static d = (foo = new.target) => {};
· ──────────
6 │ e = new.target;
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator

× Unexpected new.target expression
╭─[babel/packages/babel-parser/test/fixtures/es2022/class-properties/new-target/input.js:6:7]
5 │ static d = (foo = new.target) => {};
6 │ e = new.target;
· ──────────
7 │ f = (foo = 1 + bar(new.target));
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator

× Unexpected new.target expression
╭─[babel/packages/babel-parser/test/fixtures/es2022/class-properties/new-target/input.js:7:22]
6 │ e = new.target;
7 │ f = (foo = 1 + bar(new.target));
· ──────────
8 │ g = () => new.target;
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator

× Unexpected new.target expression
╭─[babel/packages/babel-parser/test/fixtures/es2022/class-properties/new-target/input.js:8:13]
7 │ f = (foo = 1 + bar(new.target));
8 │ g = () => new.target;
· ──────────
9 │ h = (foo = new.target) => {};
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator

× Unexpected new.target expression
╭─[babel/packages/babel-parser/test/fixtures/es2022/class-properties/new-target/input.js:9:14]
8 │ g = () => new.target;
9 │ h = (foo = new.target) => {};
· ──────────
10 │ }
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator

Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2022/class-static-block/duplicate-function-var-name/input.js

× Identifier `x` has already been declared
Expand Down Expand Up @@ -956,14 +882,14 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
1 │ const x = new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator
help: new.target is only allowed in constructors, functions, and class field initializers

× Unexpected new.target expression
╭─[babel/packages/babel-parser/test/fixtures/core/opts/allowNewTargetOutsideFunction-false-2/input.js:1:17]
1 │ const y = () => new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator
help: new.target is only allowed in constructors, functions, and class field initializers

× TS(1108): A 'return' statement can only be used within a function body.
╭─[babel/packages/babel-parser/test/fixtures/core/opts/allowReturnOutsideFunction-true-invalid-in-static-block/input.js:3:5]
Expand Down Expand Up @@ -3132,7 +3058,7 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ──────────
3 │ }
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator
help: new.target is only allowed in constructors, functions, and class field initializers

× The only valid meta property for new is new.target
╭─[babel/packages/babel-parser/test/fixtures/es2015/meta-properties/new-invalid-prop/input.js:2:3]
Expand All @@ -3147,7 +3073,7 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
1 │ new.target
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator
help: new.target is only allowed in constructors, functions, and class field initializers

× Keywords cannot contain escape characters
╭─[babel/packages/babel-parser/test/fixtures/es2015/meta-properties/new-target-invalid-escaped-new/input.js:1:16]
Expand Down Expand Up @@ -8250,7 +8176,7 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
1 │ var x = new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator
help: new.target is only allowed in constructors, functions, and class field initializers

× Expected `(` but found `}`
╭─[babel/packages/babel-parser/test/fixtures/es2022/class-properties/no-ctor/input.js:3:1]
Expand Down Expand Up @@ -9953,7 +9879,7 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
1 │ var x = new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator
help: new.target is only allowed in constructors, functions, and class field initializers

× The only valid meta property for new is new.target
╭─[babel/packages/babel-parser/test/fixtures/esprima/es2015-meta-property/unknown-property/input.js:1:22]
Expand Down
6 changes: 3 additions & 3 deletions tasks/coverage/snapshots/parser_test262.snap
Original file line number Diff line number Diff line change
Expand Up @@ -20523,15 +20523,15 @@ Negative Passed: 4581/4581 (100.00%)
· ──────────
38 │ };
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator
help: new.target is only allowed in constructors, functions, and class field initializers

× Unexpected new.target expression
╭─[test262/test/language/global-code/new.target.js:21:1]
20 │
21 │ new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator
help: new.target is only allowed in constructors, functions, and class field initializers

× TS(1108): A 'return' statement can only be used within a function body.
╭─[test262/test/language/global-code/return.js:23:1]
Expand Down Expand Up @@ -24730,7 +24730,7 @@ Negative Passed: 4581/4581 (100.00%)
16 │ new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using the `new` operator
help: new.target is only allowed in constructors, functions, and class field initializers

× The keyword 'public' is reserved
╭─[test262/test/language/module-code/early-strict-mode.js:15:5]
Expand Down
Loading
Loading