fix(transformer/private-method): clear inherited strict flags (#22508)

Fixes private method transforms so scopes inherited from a transformed private method are converted back to sloppy mode when the surrounding scope is sloppy.

Previously we only cleared `StrictMode` on the private method function scope itself. That left child scopes, such as async/generator wrapper scopes, marked as strict even though the transformed output is no longer inside a class method. This caused semantic rebuild mismatches across Babel and Test262 coverage.

Nested classes are still left strict, since class bodies are always strict mode.
This commit is contained in:
camc314 committed 2026-05-25 09:39:48 +00:00
1 parent a71fedd39c
commit 7baed9c4b7
5 files changed
+40 -10365

No files matched your search

@@ -1,10 +1,12 @@
//! ES2022: Class Properties
//! Transform of private method uses e.g. `this.#method()`.
use std::cell::Cell;
use oxc_allocator::TakeIn;
use oxc_ast::ast::*;
use oxc_ast_visit::{VisitMut, walk_mut};
use oxc_semantic::ScopeFlags;
use oxc_semantic::{ScopeFlags, ScopeId};
use oxc_span::SPAN;
use crate::{Helper, common::helper_loader::helper_call_expr, context::TraverseCtx};
@@ -64,16 +66,11 @@ impl<'a> ClassProperties<'a> {
let scope_id = function.scope_id();
let new_parent_id = ctx.current_scope_id();
ctx.scoping_mut().change_scope_parent_id(scope_id, Some(new_parent_id));
let is_strict_mode = ctx.current_scope_flags().is_strict_mode();
let make_sloppy_mode = !ctx.current_scope_flags().is_strict_mode();
let flags = ctx.scoping_mut().scope_flags_mut(scope_id);
*flags -= ScopeFlags::GetAccessor | ScopeFlags::SetAccessor;
if !is_strict_mode {
// TODO: Needs to remove all child scopes' strict mode flag if child scope
// is inherited from this scope.
*flags -= ScopeFlags::StrictMode;
}
PrivateMethodVisitor::new(*r#static, self, ctx)
PrivateMethodVisitor::new(*r#static, make_sloppy_mode, self, ctx)
.visit_function(&mut function, ScopeFlags::Function);
Some(Statement::FunctionDeclaration(function))
@@ -102,6 +99,8 @@ impl<'a> ClassProperties<'a> {
/// 2. Transform `super` expressions.
struct PrivateMethodVisitor<'a, 'v> {
super_converter: ClassPropertiesSuperConverter<'a, 'v>,
make_sloppy_mode: bool,
strict_scope_depth: u32,
/// `TransCtx` object.
ctx: &'v mut TraverseCtx<'a>,
}
@@ -109,6 +108,7 @@ struct PrivateMethodVisitor<'a, 'v> {
impl<'a, 'v> PrivateMethodVisitor<'a, 'v> {
fn new(
is_static: bool,
make_sloppy_mode: bool,
class_properties: &'v mut ClassProperties<'a>,
ctx: &'v mut TraverseCtx<'a>,
) -> Self {
@@ -117,7 +117,12 @@ impl<'a, 'v> PrivateMethodVisitor<'a, 'v> {
} else {
ClassPropertiesSuperConverterMode::PrivateMethod
};
Self { super_converter: ClassPropertiesSuperConverter::new(mode, class_properties), ctx }
Self {
super_converter: ClassPropertiesSuperConverter::new(mode, class_properties),
make_sloppy_mode,
strict_scope_depth: 0,
ctx,
}
}
}
@@ -161,6 +166,7 @@ impl<'a> VisitMut<'a> for PrivateMethodVisitor<'a, '_> {
#[inline]
fn visit_class(&mut self, _class: &mut Class<'a>) {
// Ignore because we don't need to transform `super` for other classes.
// Nested classes are strict mode, so keep their scopes' strict flags.
// TODO: Actually we do need to transform `super` in:
// 1. Class decorators
@@ -169,4 +175,23 @@ impl<'a> VisitMut<'a> for PrivateMethodVisitor<'a, '_> {
// 4. Class property/method/accessor decorators
// (or does `super` in a decorator refer to inner class?)
}
#[inline]
fn enter_scope(&mut self, flags: ScopeFlags, scope_id: &Cell<Option<ScopeId>>) {
if !self.make_sloppy_mode {
return;
}
if self.strict_scope_depth > 0 || flags.is_strict_mode() {
self.strict_scope_depth += 1;
} else {
*self.ctx.scoping_mut().scope_flags_mut(scope_id.get().unwrap()) -=
ScopeFlags::StrictMode;
}
}
#[inline]
fn leave_scope(&mut self) {
self.strict_scope_depth = self.strict_scope_depth.saturating_sub(1);
}
}
+1 -20
View File
@@ -2,7 +2,7 @@ commit: 3d34928e
semantic_babel Summary:
AST Parsed : 2236/2236 (100.00%)
Positive Passed: 2035/2236 (91.01%)
Positive Passed: 2037/2236 (91.10%)
semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/comments/decorators/decorators-after-export/input.js
Symbol span mismatch for "C":
after transform: SymbolId(0): Span { start: 65, end: 66 }
@@ -46,25 +46,6 @@ Bindings mismatch:
after transform: ScopeId(0): ["Foo"]
rebuilt : ScopeId(0): []
semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2022/class-private-methods/async/input.js
Scope flags mismatch:
after transform: ScopeId(2): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(4): ScopeFlags(Function)
semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2022/class-private-methods/async-generator/input.js
Scope flags mismatch:
after transform: ScopeId(2): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(4): ScopeFlags(Function)
Scope flags mismatch:
after transform: ScopeId(3): ScopeFlags(StrictMode)
rebuilt : ScopeId(5): ScopeFlags(0x0)
Scope flags mismatch:
after transform: ScopeId(4): ScopeFlags(StrictMode)
rebuilt : ScopeId(6): ScopeFlags(0x0)
Scope flags mismatch:
after transform: ScopeId(5): ScopeFlags(StrictMode)
rebuilt : ScopeId(7): ScopeFlags(0x0)
semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2022/class-private-properties/await-in-private-property-in-params-of-async-arrow/input.js
Bindings mismatch:
after transform: ScopeId(0): ["_asyncToGenerator", "_classPrivateFieldInitSpec"]
File diff suppressed because it is too large. Load diff
@@ -2,7 +2,7 @@ commit: f350b523
semantic_typescript Summary:
AST Parsed : 4773/4773 (100.00%)
Positive Passed: 2671/4773 (55.96%)
Positive Passed: 2677/4773 (56.09%)
semantic Error: tasks/coverage/typescript/tests/cases/compiler/2dArrays.ts
Symbol reference IDs mismatch for "Cell":
after transform: SymbolId(0): [ReferenceId(1)]
@@ -19430,14 +19430,6 @@ Unresolved references mismatch:
after transform: ["require", "undefined"]
rebuilt : ["B", "require", "undefined"]
semantic Error: tasks/coverage/typescript/tests/cases/conformance/classes/members/privateNames/privateNameAccessorsCallExpression.ts
Scope flags mismatch:
after transform: ScopeId(3): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(6): ScopeFlags(Function)
Scope flags mismatch:
after transform: ScopeId(5): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(8): ScopeFlags(Function)
semantic Error: tasks/coverage/typescript/tests/cases/conformance/classes/members/privateNames/privateNameClassExpressionLoop.ts
Bindings mismatch:
after transform: ScopeId(0): ["_C_brand", "_classPrivateFieldInitSpec", "_classPrivateMethodInitSpec", "_myField", "array"]
@@ -19481,48 +19473,11 @@ Symbol reference IDs mismatch for "Test":
after transform: SymbolId(0): [ReferenceId(0)]
rebuilt : SymbolId(5): []
semantic Error: tasks/coverage/typescript/tests/cases/conformance/classes/members/privateNames/privateNameMethodAsync.ts
Scope flags mismatch:
after transform: ScopeId(2): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(6): ScopeFlags(Function)
Scope flags mismatch:
after transform: ScopeId(5): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(9): ScopeFlags(Function)
semantic Error: tasks/coverage/typescript/tests/cases/conformance/classes/members/privateNames/privateNameStaticAccessorsCallExpression.ts
Scope flags mismatch:
after transform: ScopeId(3): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(5): ScopeFlags(Function)
Scope flags mismatch:
after transform: ScopeId(5): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(7): ScopeFlags(Function)
semantic Error: tasks/coverage/typescript/tests/cases/conformance/classes/members/privateNames/privateNameStaticFieldDestructuredBinding.ts
Symbol reference IDs mismatch for "A":
after transform: SymbolId(0): [ReferenceId(12), ReferenceId(0), ReferenceId(25), ReferenceId(28)]
rebuilt : SymbolId(2): [ReferenceId(3), ReferenceId(16), ReferenceId(19)]
semantic Error: tasks/coverage/typescript/tests/cases/conformance/classes/members/privateNames/privateNameStaticsAndStaticMethods.ts
Scope flags mismatch:
after transform: ScopeId(3): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(5): ScopeFlags(Function)
Scope flags mismatch:
after transform: ScopeId(4): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(7): ScopeFlags(Function)
semantic Error: tasks/coverage/typescript/tests/cases/conformance/classes/members/privateNames/privateNamesAndStaticMethods.ts
Scope flags mismatch:
after transform: ScopeId(3): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(5): ScopeFlags(Function)
Scope flags mismatch:
after transform: ScopeId(4): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(7): ScopeFlags(Function)
semantic Error: tasks/coverage/typescript/tests/cases/conformance/classes/members/privateNames/privateNamesAssertion.ts
Scope flags mismatch:
after transform: ScopeId(8): ScopeFlags(StrictMode)
rebuilt : ScopeId(10): ScopeFlags(0x0)
semantic Error: tasks/coverage/typescript/tests/cases/conformance/classes/mixinAbstractClasses.ts
Bindings mismatch:
after transform: ScopeId(0): ["AbstractBase", "ConcreteBase", "DerivedFromAbstract", "DerivedFromConcrete", "wasAbstract", "wasConcrete"]
@@ -1,6 +1,6 @@
commit: 3d34928e
Passed: 700/1165
Passed: 703/1165
# All Passed:
* babel-plugin-transform-logical-assignment-operators
@@ -192,7 +192,7 @@ x Output mismatch
x Output mismatch
# babel-plugin-transform-class-properties (212/269)
# babel-plugin-transform-class-properties (213/269)
* assumption-constantSuper/complex-super-class/input.js
x Output mismatch
@@ -278,14 +278,6 @@ x Output mismatch
* private/static-infer-name/input.js
x Output mismatch
* private/static-self-method/input.js
Scope flags mismatch:
after transform: ScopeId(4): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(4): ScopeFlags(Function)
Scope flags mismatch:
after transform: ScopeId(6): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(6): ScopeFlags(Function)
* private/static-shadow/input.js
x Output mismatch
@@ -415,12 +407,7 @@ x Output mismatch
x Output mismatch
# babel-plugin-transform-private-methods (46/148)
* accessors/tagged-template/input.js
Scope flags mismatch:
after transform: ScopeId(3): ScopeFlags(StrictMode | Function | Arrow)
rebuilt : ScopeId(4): ScopeFlags(Function | Arrow)
# babel-plugin-transform-private-methods (48/148)
* accessors-loose/basic/input.js
x Output mismatch
@@ -699,11 +686,6 @@ x Output mismatch
* static-accessors/set-only-getter/input.js
x Output mismatch
* static-accessors/tagged-template/input.js
Scope flags mismatch:
after transform: ScopeId(3): ScopeFlags(StrictMode | Function)
rebuilt : ScopeId(4): ScopeFlags(Function)
* static-accessors-loose/basic/input.js
x Output mismatch