From 7550838b0b951f12ff699a9e773b0927b00bf3c5 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Fri, 8 Mar 2024 21:31:32 +0800 Subject: [PATCH] fix(semantic): Incorrect reference in parameters --- crates/oxc_semantic/src/builder.rs | 59 ++++++++++++++++++++++++++++- crates/oxc_semantic/tests/scopes.rs | 31 +++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index accef2f03c53..7bb5cfefa73b 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -10,6 +10,7 @@ use oxc_syntax::{ module_record::{ExportLocalName, ModuleRecord}, operator::AssignmentOperator, }; +use rustc_hash::FxHashSet; use crate::{ binder::Binder, @@ -55,6 +56,9 @@ pub struct SemanticBuilder<'a> { in_type_definition: bool, current_reference_flag: ReferenceFlag, + is_inside_parameters: bool, + parameters_bindings: Vec>, + // builders pub nodes: AstNodes<'a>, pub scope: ScopeTree, @@ -100,6 +104,8 @@ impl<'a> SemanticBuilder<'a> { nodes: AstNodes::default(), scope, symbols: SymbolTable::default(), + is_inside_parameters: false, + parameters_bindings: vec![], module_record: Arc::new(ModuleRecord::default()), label_builder: LabelBuilder::default(), jsdoc: JSDocBuilder::new(source_text, &trivias), @@ -278,7 +284,18 @@ impl<'a> SemanticBuilder<'a> { pub fn declare_reference(&mut self, reference: Reference) -> ReferenceId { let reference_name = reference.name().clone(); let reference_id = self.symbols.create_reference(reference); - self.scope.add_unresolved_reference(self.current_scope_id, reference_name, reference_id); + let scope_id = if self.is_inside_parameters + && self + .parameters_bindings + .last() + .is_some_and(|bindings| !bindings.contains(reference_name.as_str())) + { + self.scope.get_parent_id(self.current_scope_id).unwrap_or(self.current_scope_id) + } else { + self.current_scope_id + }; + + self.scope.add_unresolved_reference(scope_id, reference_name, reference_id); reference_id } @@ -1516,7 +1533,11 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { if let Some(ident) = &func.id { self.visit_binding_identifier(ident); } - self.visit_formal_parameters(&func.params); + + self.process_parameters(|builder| { + builder.visit_formal_parameters(&func.params); + }); + if let Some(body) = &func.body { self.visit_function_body(body); } @@ -1628,6 +1649,22 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { self.leave_node(kind); self.leave_scope(); } + + fn visit_catch_clause(&mut self, clause: &CatchClause<'a>) { + let kind = AstKind::CatchClause(self.alloc(clause)); + self.enter_scope(ScopeFlags::empty()); + self.enter_node(kind); + + self.process_parameters(|builder| { + if let Some(param) = &clause.param { + builder.visit_binding_pattern(param); + } + }); + + self.visit_statements(&clause.body.body); + self.leave_node(kind); + self.leave_scope(); + } } impl<'a> SemanticBuilder<'a> { @@ -1650,6 +1687,13 @@ impl<'a> SemanticBuilder<'a> { self.make_all_namespaces_valuelike(); } AstKind::StaticBlock(_) => self.label_builder.enter_function_or_static_block(), + AstKind::BindingIdentifier(ident) => { + if self.is_inside_parameters { + if let Some(bindings) = self.parameters_bindings.last_mut() { + bindings.insert(&ident.name); + } + } + } AstKind::Function(func) => { self.function_stack.push(self.current_node_id); func.bind(self); @@ -1878,4 +1922,15 @@ impl<'a> SemanticBuilder<'a> { } false } + + fn process_parameters(&mut self, cb: F) + where + F: FnOnce(&mut SemanticBuilder<'a>), + { + self.is_inside_parameters = true; + self.parameters_bindings.push(FxHashSet::default()); + cb(self); + self.parameters_bindings.pop(); + self.is_inside_parameters = false; + } } diff --git a/crates/oxc_semantic/tests/scopes.rs b/crates/oxc_semantic/tests/scopes.rs index d05abe66a12a..116bcd6577c9 100644 --- a/crates/oxc_semantic/tests/scopes.rs +++ b/crates/oxc_semantic/tests/scopes.rs @@ -106,3 +106,34 @@ fn test_switch_case() { .has_number_of_references(1) .test(); } + +#[test] +fn test_function_parameters() { + SemanticTester::js( + " + const foo = 2; + function func(a = foo, b = a) { + const foo = 0; + } + ", + ) + .has_root_symbol("foo") + .has_number_of_references(1) + .test(); +} + +#[test] +fn test_catch_clause_parameters() { + SemanticTester::js( + " + const a = 0; + try { + } catch ({ [a]: b }) { + const a = 1; + } + ", + ) + .has_root_symbol("a") + .has_number_of_references(1) + .test(); +}