mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
perf(semantic): compute duplicate parameter policy once per list (#28446)
Compute parameter-list simplicity once instead of rescanning it for every parameter, and restore the duplicate-binding policy after nested default expressions. Validated with scoped Rust checks, full conformance, parser bindings. AI-assisted (Codex).
This commit is contained in:
1 parent
128c67d40d
commit
04b20c0d4b
5 files changed
+41
-50
No files matched your search
@@ -4,7 +4,7 @@ use smallvec::SmallVec;
|
||||
|
||||
use oxc_allocator::{GetAddress, UnstableAddress};
|
||||
use oxc_ast::{AstKind, ast::*};
|
||||
use oxc_ecmascript::{BoundNames, IsSimpleParameterList};
|
||||
use oxc_ecmascript::BoundNames;
|
||||
use oxc_span::GetSpan;
|
||||
use oxc_str::Ident;
|
||||
use oxc_syntax::{node::NodeId, scope::ScopeFlags, scope::ScopeId, symbol::SymbolFlags};
|
||||
@@ -250,30 +250,8 @@ impl<'a> Binder<'a> for BindingRestElement<'a> {
|
||||
impl<'a> Binder<'a> for FormalParameter<'a> {
|
||||
// Binds the FormalParameter of a function or method.
|
||||
fn bind(&self, builder: &mut SemanticBuilder<'a>) {
|
||||
let parent_kind = builder.ancestry().parent_kind();
|
||||
let AstKind::FormalParameters(parameters) = parent_kind else { unreachable!() };
|
||||
|
||||
let includes = SymbolFlags::FunctionScopedVariable;
|
||||
|
||||
let is_not_allowed_duplicate_parameters = matches!(
|
||||
parameters.kind,
|
||||
// ArrowFormalParameters: UniqueFormalParameters
|
||||
FormalParameterKind::ArrowFormalParameters |
|
||||
// UniqueFormalParameters : FormalParameters
|
||||
// * It is a Syntax Error if BoundNames of FormalParameters contains any duplicate elements.
|
||||
FormalParameterKind::UniqueFormalParameters
|
||||
) ||
|
||||
// Multiple occurrences of the same BindingIdentifier in a FormalParameterList is only allowed for functions which have simple parameter lists and which are not defined in strict mode code.
|
||||
builder.strict_mode() ||
|
||||
// FormalParameters : FormalParameterList
|
||||
// * It is a Syntax Error if IsSimpleParameterList of FormalParameterList is false and BoundNames of FormalParameterList contains any duplicate elements.
|
||||
!parameters.is_simple_parameter_list();
|
||||
|
||||
let excludes = if is_not_allowed_duplicate_parameters {
|
||||
SymbolFlags::FunctionScopedVariable | SymbolFlags::FunctionScopedVariableExcludes
|
||||
} else {
|
||||
SymbolFlags::FunctionScopedVariableExcludes
|
||||
};
|
||||
let excludes = builder.formal_parameter_excludes;
|
||||
|
||||
self.bound_names(&mut |ident| {
|
||||
let symbol_id = builder.declare_symbol(ident.span, ident.name, includes, excludes);
|
||||
@@ -285,30 +263,11 @@ impl<'a> Binder<'a> for FormalParameter<'a> {
|
||||
impl<'a> Binder<'a> for FormalParameterRest<'a> {
|
||||
// Binds the FormalParameter of a function or method.
|
||||
fn bind(&self, builder: &mut SemanticBuilder<'a>) {
|
||||
let parent_kind = builder.ancestry().parent_kind();
|
||||
let AstKind::FormalParameters(parameters) = parent_kind else { unreachable!() };
|
||||
|
||||
let includes = SymbolFlags::FunctionScopedVariable;
|
||||
|
||||
let is_not_allowed_duplicate_parameters = matches!(
|
||||
parameters.kind,
|
||||
// ArrowFormalParameters: UniqueFormalParameters
|
||||
FormalParameterKind::ArrowFormalParameters |
|
||||
// UniqueFormalParameters : FormalParameters
|
||||
// * It is a Syntax Error if BoundNames of FormalParameters contains any duplicate elements.
|
||||
FormalParameterKind::UniqueFormalParameters
|
||||
) ||
|
||||
// Multiple occurrences of the same BindingIdentifier in a FormalParameterList is only allowed for functions which have simple parameter lists and which are not defined in strict mode code.
|
||||
builder.strict_mode() ||
|
||||
// FormalParameters : FormalParameterList
|
||||
// * It is a Syntax Error if IsSimpleParameterList of FormalParameterList is false and BoundNames of FormalParameterList contains any duplicate elements.
|
||||
!parameters.is_simple_parameter_list();
|
||||
|
||||
let excludes = if is_not_allowed_duplicate_parameters {
|
||||
SymbolFlags::FunctionScopedVariable | SymbolFlags::FunctionScopedVariableExcludes
|
||||
} else {
|
||||
SymbolFlags::FunctionScopedVariableExcludes
|
||||
};
|
||||
// A rest parameter makes the parameter list non-simple in every context,
|
||||
// so duplicate bindings are always forbidden.
|
||||
let excludes =
|
||||
SymbolFlags::FunctionScopedVariable | SymbolFlags::FunctionScopedVariableExcludes;
|
||||
|
||||
self.rest.argument.bound_names(&mut |ident| {
|
||||
let symbol_id = builder.declare_symbol(ident.span, ident.name, includes, excludes);
|
||||
|
||||
@@ -17,6 +17,7 @@ use oxc_cfg::{
|
||||
IterationInstructionKind, ReturnInstructionKind,
|
||||
};
|
||||
use oxc_diagnostics::{Diagnostics, OxcDiagnostic};
|
||||
use oxc_ecmascript::IsSimpleParameterList;
|
||||
use oxc_span::{SourceType, Span};
|
||||
use oxc_str::Ident;
|
||||
use oxc_syntax::{
|
||||
@@ -81,6 +82,8 @@ pub struct SemanticBuilder<'a> {
|
||||
pub(crate) current_scope_id: ScopeId,
|
||||
pub(crate) module_instance_state_cache: FxHashMap<Address, ModuleInstanceState>,
|
||||
current_reference_flags: ReferenceFlags,
|
||||
/// Duplicate-binding exclusions for the parameter list currently being visited.
|
||||
pub(crate) formal_parameter_excludes: SymbolFlags,
|
||||
/// Nesting depth of TypeScript ambient contexts.
|
||||
ambient_depth: u32,
|
||||
/// Symbols that have been hoisted out of a scope (e.g. `var` declarations hoisted to
|
||||
@@ -149,6 +152,7 @@ impl<'a> SemanticBuilder<'a> {
|
||||
source_type: SourceType::default(),
|
||||
errors: RefCell::new(Diagnostics::new()),
|
||||
current_reference_flags: ReferenceFlags::empty(),
|
||||
formal_parameter_excludes: SymbolFlags::FunctionScopedVariableExcludes,
|
||||
ambient_depth: 0,
|
||||
current_scope_id,
|
||||
module_instance_state_cache: FxHashMap::default(),
|
||||
@@ -2283,6 +2287,25 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
||||
self.leave_node(kind);
|
||||
}
|
||||
|
||||
fn visit_formal_parameters(&mut self, it: &FormalParameters<'a>) {
|
||||
// Checking whether a list is simple scans its parameters. Do this once per
|
||||
// list, rather than once per parameter when binding each name.
|
||||
let mut excludes = SymbolFlags::FunctionScopedVariableExcludes;
|
||||
if matches!(
|
||||
it.kind,
|
||||
FormalParameterKind::ArrowFormalParameters
|
||||
| FormalParameterKind::UniqueFormalParameters
|
||||
) || self.strict_mode()
|
||||
|| !it.is_simple_parameter_list()
|
||||
{
|
||||
excludes |= SymbolFlags::FunctionScopedVariable;
|
||||
}
|
||||
let previous = mem::replace(&mut self.formal_parameter_excludes, excludes);
|
||||
oxc_ast_visit::walk::walk_formal_parameters(self, it);
|
||||
// Default values and type annotations can contain nested parameter lists.
|
||||
self.formal_parameter_excludes = previous;
|
||||
}
|
||||
|
||||
fn visit_update_expression(&mut self, it: &UpdateExpression<'a>) {
|
||||
let kind = AstKind::UpdateExpression(self.alloc(it));
|
||||
self.enter_node(kind);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
function outer(value = function (inner, inner) {}, value) {}
|
||||
@@ -1,3 +1,3 @@
|
||||
lexer_misc Summary:
|
||||
AST Parsed : 398/398 (100.00%)
|
||||
Positive Passed: 398/398 (100.00%)
|
||||
AST Parsed : 399/399 (100.00%)
|
||||
Positive Passed: 399/399 (100.00%)
|
||||
@@ -1,7 +1,7 @@
|
||||
parser_misc Summary:
|
||||
AST Parsed : 131/131 (100.00%)
|
||||
Positive Passed: 131/131 (100.00%)
|
||||
Negative Passed: 269/269 (100.00%)
|
||||
Negative Passed: 270/270 (100.00%)
|
||||
|
||||
× TS(1275): 'accessor' modifier can only appear on a property declaration.
|
||||
╭─[misc/fail/accessor-modifier-abstract-method.ts:1:29]
|
||||
@@ -508,6 +508,14 @@ Negative Passed: 269/269 (100.00%)
|
||||
If you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased.
|
||||
If you have nested conflicts, resolve the outermost conflict first.
|
||||
|
||||
× Identifier `value` has already been declared
|
||||
╭─[misc/fail/duplicate-parameter-after-nested-default.cjs:1:16]
|
||||
1 │ function outer(value = function (inner, inner) {}, value) {}
|
||||
· ──┬── ──┬──
|
||||
· │ ╰── It can not be redeclared here
|
||||
· ╰── `value` has already been declared here
|
||||
╰────
|
||||
|
||||
× Identifier `#x` has already been declared
|
||||
╭─[misc/fail/duplicate-private-declarations.js:1:24]
|
||||
1 │ class DuplicateField { #x; x; #x; }
|
||||
|
||||
Reference in new issue
Block a user