mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
refactor(semantic): correct scope in CatchClause (#4192)
close: #4186 CatchClause has two scopes. The first one is `CatchClause`, which will add a `CatchParameter` to it. The second one is `Block`, which will add binding that declares in the current block scope. The spec has a syntax error about `CatchParameter` - It is a Syntax Error if any element of the BoundNames of CatchParameter also occurs in the LexicallyDeclaredNames of Block.
This commit is contained in:
parent
aa22073736
commit
7f1adddaf0
8 changed files with 40 additions and 47 deletions
|
|
@ -1300,7 +1300,7 @@ pub struct TryStatement<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[visited_node]
|
#[visited_node]
|
||||||
#[scope(if(self.param.is_some()))]
|
#[scope]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
|
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
|
||||||
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
|
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
|
||||||
|
|
|
||||||
|
|
@ -3690,10 +3690,7 @@ pub mod walk {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn walk_catch_clause<'a, V: Visit<'a>>(visitor: &mut V, it: &CatchClause<'a>) {
|
pub fn walk_catch_clause<'a, V: Visit<'a>>(visitor: &mut V, it: &CatchClause<'a>) {
|
||||||
let scope_events_cond = it.param.is_some();
|
|
||||||
if scope_events_cond {
|
|
||||||
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
|
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
|
||||||
}
|
|
||||||
let kind = AstKind::CatchClause(visitor.alloc(it));
|
let kind = AstKind::CatchClause(visitor.alloc(it));
|
||||||
visitor.enter_node(kind);
|
visitor.enter_node(kind);
|
||||||
if let Some(param) = &it.param {
|
if let Some(param) = &it.param {
|
||||||
|
|
@ -3701,10 +3698,8 @@ pub mod walk {
|
||||||
}
|
}
|
||||||
visitor.visit_block_statement(&it.body);
|
visitor.visit_block_statement(&it.body);
|
||||||
visitor.leave_node(kind);
|
visitor.leave_node(kind);
|
||||||
if scope_events_cond {
|
|
||||||
visitor.leave_scope();
|
visitor.leave_scope();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn walk_catch_parameter<'a, V: Visit<'a>>(visitor: &mut V, it: &CatchParameter<'a>) {
|
pub fn walk_catch_parameter<'a, V: Visit<'a>>(visitor: &mut V, it: &CatchParameter<'a>) {
|
||||||
|
|
|
||||||
|
|
@ -3895,10 +3895,7 @@ pub mod walk_mut {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn walk_catch_clause<'a, V: VisitMut<'a>>(visitor: &mut V, it: &mut CatchClause<'a>) {
|
pub fn walk_catch_clause<'a, V: VisitMut<'a>>(visitor: &mut V, it: &mut CatchClause<'a>) {
|
||||||
let scope_events_cond = it.param.is_some();
|
|
||||||
if scope_events_cond {
|
|
||||||
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
|
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
|
||||||
}
|
|
||||||
let kind = AstType::CatchClause;
|
let kind = AstType::CatchClause;
|
||||||
visitor.enter_node(kind);
|
visitor.enter_node(kind);
|
||||||
if let Some(param) = &mut it.param {
|
if let Some(param) = &mut it.param {
|
||||||
|
|
@ -3906,10 +3903,8 @@ pub mod walk_mut {
|
||||||
}
|
}
|
||||||
visitor.visit_block_statement(&mut it.body);
|
visitor.visit_block_statement(&mut it.body);
|
||||||
visitor.leave_node(kind);
|
visitor.leave_node(kind);
|
||||||
if scope_events_cond {
|
|
||||||
visitor.leave_scope();
|
visitor.leave_scope();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn walk_catch_parameter<'a, V: VisitMut<'a>>(visitor: &mut V, it: &mut CatchParameter<'a>) {
|
pub fn walk_catch_parameter<'a, V: VisitMut<'a>>(visitor: &mut V, it: &mut CatchParameter<'a>) {
|
||||||
|
|
|
||||||
|
|
@ -48,21 +48,17 @@ impl Rule for NoEmpty {
|
||||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
AstKind::BlockStatement(block) if block.body.is_empty() => {
|
AstKind::BlockStatement(block) if block.body.is_empty() => {
|
||||||
|
if self.allow_empty_catch
|
||||||
|
&& matches!(ctx.nodes().parent_kind(node.id()), Some(AstKind::CatchClause(_)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.semantic().trivias().has_comments_between(block.span) {
|
if ctx.semantic().trivias().has_comments_between(block.span) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx.diagnostic(no_empty_diagnostic("block", block.span));
|
ctx.diagnostic(no_empty_diagnostic("block", block.span));
|
||||||
}
|
}
|
||||||
// The visitor does not visit the `BlockStatement` inside the `CatchClause`.
|
|
||||||
// See `Visit::visit_catch_clause`.
|
|
||||||
AstKind::CatchClause(catch_clause)
|
|
||||||
if !self.allow_empty_catch && catch_clause.body.body.is_empty() =>
|
|
||||||
{
|
|
||||||
if ctx.semantic().trivias().has_comments_between(catch_clause.body.span) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ctx.diagnostic(no_empty_diagnostic("block", catch_clause.body.span));
|
|
||||||
}
|
|
||||||
// The visitor does not visit the `BlockStatement` inside the `FinallyClause`.
|
// The visitor does not visit the `BlockStatement` inside the `FinallyClause`.
|
||||||
// See `Visit::visit_finally_clause`.
|
// See `Visit::visit_finally_clause`.
|
||||||
AstKind::FinallyClause(finally_clause) if finally_clause.body.is_empty() => {
|
AstKind::FinallyClause(finally_clause) if finally_clause.body.is_empty() => {
|
||||||
|
|
|
||||||
|
|
@ -62,13 +62,6 @@ impl Rule for EmptyBraceSpaces {
|
||||||
AstKind::BlockStatement(block_stmt) => {
|
AstKind::BlockStatement(block_stmt) => {
|
||||||
remove_empty_braces_spaces(ctx, block_stmt.body.is_empty(), block_stmt.span);
|
remove_empty_braces_spaces(ctx, block_stmt.body.is_empty(), block_stmt.span);
|
||||||
}
|
}
|
||||||
AstKind::CatchClause(catch_clause) => {
|
|
||||||
remove_empty_braces_spaces(
|
|
||||||
ctx,
|
|
||||||
catch_clause.body.body.is_empty(),
|
|
||||||
catch_clause.body.span,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
AstKind::FinallyClause(finally_clause) => {
|
AstKind::FinallyClause(finally_clause) => {
|
||||||
remove_empty_braces_spaces(
|
remove_empty_braces_spaces(
|
||||||
ctx,
|
ctx,
|
||||||
|
|
|
||||||
|
|
@ -1348,19 +1348,6 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
||||||
self.leave_node(kind);
|
self.leave_node(kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_catch_clause(&mut self, clause: &CatchClause<'a>) {
|
|
||||||
let kind = AstKind::CatchClause(self.alloc(clause));
|
|
||||||
self.enter_scope(ScopeFlags::empty(), &clause.scope_id);
|
|
||||||
clause.scope_id.set(Some(self.current_scope_id));
|
|
||||||
self.enter_node(kind);
|
|
||||||
if let Some(param) = &clause.param {
|
|
||||||
self.visit_catch_parameter(param);
|
|
||||||
}
|
|
||||||
self.visit_statements(&clause.body.body);
|
|
||||||
self.leave_node(kind);
|
|
||||||
self.leave_scope();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_finally_clause(&mut self, clause: &BlockStatement<'a>) {
|
fn visit_finally_clause(&mut self, clause: &BlockStatement<'a>) {
|
||||||
let kind = AstKind::FinallyClause(self.alloc(clause));
|
let kind = AstKind::FinallyClause(self.alloc(clause));
|
||||||
self.enter_scope(ScopeFlags::empty(), &clause.scope_id);
|
self.enter_scope(ScopeFlags::empty(), &clause.scope_id);
|
||||||
|
|
@ -1826,6 +1813,19 @@ impl<'a> SemanticBuilder<'a> {
|
||||||
AstKind::YieldExpression(_) => {
|
AstKind::YieldExpression(_) => {
|
||||||
self.set_function_node_flag(NodeFlags::HasYield);
|
self.set_function_node_flag(NodeFlags::HasYield);
|
||||||
}
|
}
|
||||||
|
AstKind::BlockStatement(_) => {
|
||||||
|
if matches!(
|
||||||
|
self.nodes.parent_kind(self.current_node_id),
|
||||||
|
Some(AstKind::CatchClause(_))
|
||||||
|
) {
|
||||||
|
// Clone the `CatchClause` bindings and add them to the current scope.
|
||||||
|
// to make it easier to check redeclare errors.
|
||||||
|
if let Some(parent_scope_id) = self.scope.get_parent_id(self.current_scope_id) {
|
||||||
|
let bindings = self.scope.get_bindings(parent_scope_id).clone();
|
||||||
|
self.scope.get_bindings_mut(self.current_scope_id).extend(bindings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ bb4: {
|
||||||
statement
|
statement
|
||||||
statement
|
statement
|
||||||
statement
|
statement
|
||||||
|
statement
|
||||||
}
|
}
|
||||||
|
|
||||||
bb5: {
|
bb5: {
|
||||||
|
|
@ -55,7 +56,7 @@ digraph {
|
||||||
1 [ label = "TryStatement" ]
|
1 [ label = "TryStatement" ]
|
||||||
2 [ label = "" ]
|
2 [ label = "" ]
|
||||||
3 [ label = "BlockStatement" ]
|
3 [ label = "BlockStatement" ]
|
||||||
4 [ label = "LabeledStatement\nBlockStatement\nIfStatement" ]
|
4 [ label = "BlockStatement\nLabeledStatement\nBlockStatement\nIfStatement" ]
|
||||||
5 [ label = "Condition(IdentifierReference(condition))" ]
|
5 [ label = "Condition(IdentifierReference(condition))" ]
|
||||||
6 [ label = "BlockStatement\nbreak <LABEL>" ]
|
6 [ label = "BlockStatement\nbreak <LABEL>" ]
|
||||||
7 [ label = "unreachable" ]
|
7 [ label = "unreachable" ]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ commit: d8086f14
|
||||||
|
|
||||||
parser_typescript Summary:
|
parser_typescript Summary:
|
||||||
AST Parsed : 5279/5283 (99.92%)
|
AST Parsed : 5279/5283 (99.92%)
|
||||||
Positive Passed: 5272/5283 (99.79%)
|
Positive Passed: 5271/5283 (99.77%)
|
||||||
Negative Passed: 1094/4875 (22.44%)
|
Negative Passed: 1094/4875 (22.44%)
|
||||||
Expect Syntax Error: "compiler/ClassDeclaration10.ts"
|
Expect Syntax Error: "compiler/ClassDeclaration10.ts"
|
||||||
Expect Syntax Error: "compiler/ClassDeclaration11.ts"
|
Expect Syntax Error: "compiler/ClassDeclaration11.ts"
|
||||||
|
|
@ -3818,6 +3818,19 @@ Expect to Parse: "compiler/withStatementInternalComments.ts"
|
||||||
2 │ /*1*/ with /*2*/ ( /*3*/ false /*4*/ ) /*5*/ {}
|
2 │ /*1*/ with /*2*/ ( /*3*/ false /*4*/ ) /*5*/ {}
|
||||||
· ────
|
· ────
|
||||||
╰────
|
╰────
|
||||||
|
Expect to Parse: "conformance/async/es6/asyncWithVarShadowing_es6.ts"
|
||||||
|
|
||||||
|
× Identifier `x` has already been declared
|
||||||
|
╭─[conformance/async/es6/asyncWithVarShadowing_es6.ts:130:14]
|
||||||
|
129 │ }
|
||||||
|
130 │ catch ({ x }) {
|
||||||
|
· ┬
|
||||||
|
· ╰── `x` has already been declared here
|
||||||
|
131 │ var x;
|
||||||
|
· ┬
|
||||||
|
· ╰── It can not be redeclared here
|
||||||
|
132 │ }
|
||||||
|
╰────
|
||||||
Expect to Parse: "conformance/classes/propertyMemberDeclarations/staticPropertyNameConflicts.ts"
|
Expect to Parse: "conformance/classes/propertyMemberDeclarations/staticPropertyNameConflicts.ts"
|
||||||
|
|
||||||
× Classes may not have a static property named prototype
|
× Classes may not have a static property named prototype
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue