Skip to content

Commit 33a7320

Browse files
committed
fix(linter/no-throw-literal): fix unconditional recursion in could_be_error (#12819)
fixes #12818
1 parent a3aec6a commit 33a7320

File tree

2 files changed

+31
-8
lines changed

2 files changed

+31
-8
lines changed

crates/oxc_linter/src/ast_util.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::borrow::Cow;
22

3+
use rustc_hash::FxHashSet;
4+
35
use oxc_ast::{
46
AstKind,
57
ast::{BindingIdentifier, *},
@@ -514,6 +516,14 @@ pub fn get_preceding_indent_str(source_text: &str, span: Span) -> Option<&str> {
514516
}
515517

516518
pub fn could_be_error(ctx: &LintContext, expr: &Expression) -> bool {
519+
could_be_error_impl(ctx, expr, &mut FxHashSet::default())
520+
}
521+
522+
fn could_be_error_impl(
523+
ctx: &LintContext,
524+
expr: &Expression,
525+
visited: &mut FxHashSet<SymbolId>,
526+
) -> bool {
517527
match expr.get_inner_expression() {
518528
Expression::NewExpression(_)
519529
| Expression::AwaitExpression(_)
@@ -527,42 +537,54 @@ pub fn could_be_error(ctx: &LintContext, expr: &Expression) -> bool {
527537
Expression::AssignmentExpression(expr) => {
528538
if matches!(expr.operator, AssignmentOperator::Assign | AssignmentOperator::LogicalAnd)
529539
{
530-
return could_be_error(ctx, &expr.right);
540+
return could_be_error_impl(ctx, &expr.right, visited);
531541
}
532542

533543
if matches!(
534544
expr.operator,
535545
AssignmentOperator::LogicalOr | AssignmentOperator::LogicalNullish
536546
) {
537-
return expr.left.get_expression().is_none_or(|expr| could_be_error(ctx, expr))
538-
|| could_be_error(ctx, &expr.right);
547+
return expr
548+
.left
549+
.get_expression()
550+
.is_none_or(|expr| could_be_error_impl(ctx, expr, visited))
551+
|| could_be_error_impl(ctx, &expr.right, visited);
539552
}
540553

541554
false
542555
}
543556
Expression::SequenceExpression(expr) => {
544-
expr.expressions.last().is_some_and(|expr| could_be_error(ctx, expr))
557+
expr.expressions.last().is_some_and(|expr| could_be_error_impl(ctx, expr, visited))
545558
}
546559
Expression::LogicalExpression(expr) => {
547560
if matches!(expr.operator, LogicalOperator::And) {
548-
return could_be_error(ctx, &expr.right);
561+
return could_be_error_impl(ctx, &expr.right, visited);
549562
}
550563

551-
could_be_error(ctx, &expr.left) || could_be_error(ctx, &expr.right)
564+
could_be_error_impl(ctx, &expr.left, visited)
565+
|| could_be_error_impl(ctx, &expr.right, visited)
552566
}
553567
Expression::ConditionalExpression(expr) => {
554-
could_be_error(ctx, &expr.consequent) || could_be_error(ctx, &expr.alternate)
568+
could_be_error_impl(ctx, &expr.consequent, visited)
569+
|| could_be_error_impl(ctx, &expr.alternate, visited)
555570
}
556571
Expression::Identifier(ident) => {
557572
let reference = ctx.scoping().get_reference(ident.reference_id());
558573
let Some(symbol_id) = reference.symbol_id() else {
559574
return true;
560575
};
576+
577+
// Check if we've already visited this symbol to prevent infinite recursion
578+
// Return true (could be error) when we encounter a circular reference since we can't determine the type
579+
if !visited.insert(symbol_id) {
580+
return true;
581+
}
582+
561583
let decl = ctx.nodes().get_node(ctx.scoping().symbol_declaration(symbol_id));
562584
match decl.kind() {
563585
AstKind::VariableDeclarator(decl) => {
564586
if let Some(init) = &decl.init {
565-
could_be_error(ctx, init)
587+
could_be_error_impl(ctx, init, visited)
566588
} else {
567589
// TODO: warn about throwing undefined
568590
false

crates/oxc_linter/src/rules/eslint/no_throw_literal.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ fn test() {
133133
"function main(x) { throw x; }", // cannot determine type of x
134134
"function main(x: any) { throw x; }",
135135
"function main(x: TypeError) { throw x; }",
136+
"async function json(stream) { try { } catch (_err) { const err = err; throw err; } }",
136137
];
137138

138139
let fail = vec![

0 commit comments

Comments
 (0)