fix(semantic): bind SymbolId to function name in if (foo) function id() {} (#5673)

This commit is contained in:
Boshen 2024-09-10 11:05:26 +00:00
parent 067f9b5a6f
commit f9e3a41dd2
4 changed files with 806 additions and 686 deletions

View file

@ -10,7 +10,11 @@ use oxc_ast::{
};
use oxc_span::{GetSpan, SourceType};
use crate::{scope::ScopeFlags, symbol::SymbolFlags, SemanticBuilder};
use crate::{
scope::{ScopeFlags, ScopeId},
symbol::SymbolFlags,
SemanticBuilder,
};
pub(crate) trait Binder<'a> {
#[allow(unused_variables)]
@ -132,19 +136,42 @@ fn function_as_var(flags: ScopeFlags, source_type: SourceType) -> bool {
flags.is_function() || (source_type.is_script() && flags.is_top())
}
/// Check for Annex B `if (foo) function a() {} else function b() {}`
fn is_function_part_of_if_statement(function: &Function, builder: &SemanticBuilder) -> bool {
if builder.current_scope_flags().is_strict_mode() {
return false;
}
let Some(AstKind::IfStatement(stmt)) = builder.nodes.parent_kind(builder.current_node_id)
else {
return false;
};
if let Statement::FunctionDeclaration(func) = &stmt.consequent {
if func.span == function.span {
return true;
}
}
if let Some(Statement::FunctionDeclaration(func)) = &stmt.alternate {
if func.span == function.span {
return true;
}
}
false
}
impl<'a> Binder<'a> for Function<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
let current_scope_id = builder.current_scope_id;
let scope_flags = builder.current_scope_flags();
if let Some(ident) = &self.id {
if !scope_flags.is_strict_mode()
&& matches!(
builder.nodes.parent_kind(builder.current_node_id),
Some(AstKind::IfStatement(_))
)
{
// Do not declare in if single statements,
// if (false) function f() {} else function g() { }
if is_function_part_of_if_statement(self, builder) {
let symbol_id = builder.symbols.create_symbol(
ident.span,
ident.name.clone().into(),
SymbolFlags::Function,
ScopeId::new(u32::MAX - 1), // Not bound to any scope.
builder.current_node_id,
);
ident.symbol_id.set(Some(symbol_id));
} else if self.r#type == FunctionType::FunctionDeclaration {
// The visitor is already inside the function scope,
// retrieve the parent scope for the function id to bind to.

View file

@ -648,22 +648,6 @@ impl<'a, 'e> Visit<'a> for SemanticIdsCollector<'e> {
walk::walk_declaration(self, it);
}
fn visit_if_statement(&mut self, stmt: &IfStatement<'a>) {
// skip `if (function foo() {}) {}`
if !matches!(stmt.test, Expression::FunctionExpression(_)) {
self.visit_expression(&stmt.test);
}
// skip `if (true) function foo() {} else function bar() {}`
if !stmt.consequent.is_declaration() {
self.visit_statement(&stmt.consequent);
}
if let Some(alternate) = &stmt.alternate {
if !alternate.is_declaration() {
self.visit_statement(alternate);
}
}
}
fn visit_ts_type(&mut self, _it: &TSType<'a>) {
/* noop */
}

View file

@ -2,11 +2,14 @@ commit: 3bcfee23
semantic_babel Summary:
AST Parsed : 2101/2101 (100.00%)
Positive Passed: 1737/2101 (82.67%)
Positive Passed: 1739/2101 (82.77%)
tasks/coverage/babel/packages/babel-parser/test/fixtures/annex-b/enabled/3.3-function-in-if-body/input.js
semantic error: Scope children mismatch:
after transform: ScopeId(0): [ScopeId(1), ScopeId(2)]
rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2)]
semantic error: Symbol scope ID mismatch:
after transform: SymbolId(0): ScopeId(4294967294)
rebuilt : SymbolId(0): ScopeId(4294967294)
Symbol scope ID mismatch:
after transform: SymbolId(1): ScopeId(4294967294)
rebuilt : SymbolId(1): ScopeId(4294967294)
tasks/coverage/babel/packages/babel-parser/test/fixtures/comments/interpreter-directive/interpreter-directive-import/input.js
semantic error: Bindings mismatch:
@ -23,14 +26,9 @@ semantic error: Unexpected new.target expression
Unexpected new.target expression
tasks/coverage/babel/packages/babel-parser/test/fixtures/core/scope/dupl-bind-catch-hang-func/input.js
semantic error: Scope children mismatch:
after transform: ScopeId(3): [ScopeId(4)]
rebuilt : ScopeId(3): [ScopeId(4)]
tasks/coverage/babel/packages/babel-parser/test/fixtures/core/uncategorised/230/input.js
semantic error: Binding symbols mismatch:
after transform: ScopeId(0): [SymbolId(0)]
rebuilt : ScopeId(0): [SymbolId(0)]
semantic error: Symbol scope ID mismatch:
after transform: SymbolId(1): ScopeId(4294967294)
rebuilt : SymbolId(1): ScopeId(4294967294)
tasks/coverage/babel/packages/babel-parser/test/fixtures/core/uncategorised/327/input.js
semantic error: A 'return' statement can only be used within a function body.
@ -138,15 +136,10 @@ semantic error: Bindings mismatch:
after transform: ScopeId(0): ["nil"]
rebuilt : ScopeId(0): []
tasks/coverage/babel/packages/babel-parser/test/fixtures/esprima/statement-if/migrated_0002/input.js
semantic error: Binding symbols mismatch:
after transform: ScopeId(0): [SymbolId(0)]
rebuilt : ScopeId(0): [SymbolId(0)]
tasks/coverage/babel/packages/babel-parser/test/fixtures/esprima/statement-if/migrated_0003/input.js
semantic error: Scope children mismatch:
after transform: ScopeId(0): [ScopeId(1)]
rebuilt : ScopeId(0): [ScopeId(1)]
semantic error: Symbol scope ID mismatch:
after transform: SymbolId(0): ScopeId(4294967294)
rebuilt : SymbolId(0): ScopeId(4294967294)
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-function-with-newline/input.ts
semantic error: Unresolved references mismatch:

File diff suppressed because it is too large Load diff