fix(rome_js_analyze): improve exception control flow handling
MODIFY
crates/rome_js_analyze/src/analyzers/correctness/no_unreachable.rs
crates/rome_js_analyze/src/analyzers/correctness/no_unreachable.rs
@@ -2,7 +2,9 @@ use std::{cmp::Ordering, collections::VecDeque, num::NonZeroU32, vec::IntoIter};
use roaring::bitmap::RoaringBitmap;
use rome_analyze::{context::RuleContext, declare_rule, Rule, RuleDiagnostic};
-use rome_control_flow::{builder::BlockId, ExceptionHandler, Instruction, InstructionKind};
+use rome_control_flow::{
+ builder::BlockId, ExceptionHandler, ExceptionHandlerKind, Instruction, InstructionKind,
+};
use rome_js_syntax::{
JsBlockStatement, JsCaseClause, JsDefaultClause, JsDoWhileStatement, JsForInStatement,
JsForOfStatement, JsForStatement, JsFunctionBody, JsIfStatement, JsLabeledStatement,
@@ -294,7 +296,7 @@ fn analyze_simple(cfg: &ControlFlowGraph, signals: &mut UnreachableRanges) {
// catch or finally block
if let Some((handler, handlers)) = exception_handlers.take() {
if reachable_blocks.insert(handler.target) {
- queue.push_back((handler.target, Some(handlers)));
+ queue.push_back((handler.target, find_catch_handlers(handlers)));
}
}
}
@@ -473,7 +475,7 @@ fn traverse_cfg(
next_block: handler.target,
visited: path.visited.clone(),
terminator: path.terminator,
- exception_handlers: Some(handlers),
+ exception_handlers: find_catch_handlers(handlers),
});
}
}
@@ -542,6 +544,22 @@ fn has_side_effects(inst: &Instruction<JsLanguage>) -> bool {
}
}
+/// Returns the list of all `finally` exception handlers up to and including
+/// the first `catch` handler to be executed when an exception is thrown
+fn find_catch_handlers(handlers: &[ExceptionHandler]) -> Option<&[ExceptionHandler]> {
+ let handlers = handlers
+ .iter()
+ .position(|handler| matches!(handler.kind, ExceptionHandlerKind::Catch))
+ .map(|index| &handlers[index..])
+ .unwrap_or(handlers);
+
+ if handlers.is_empty() {
+ None
+ } else {
+ Some(handlers)
+ }
+}
+
/// Create an additional visitor path from a jump instruction and push it to the queue
fn handle_jump<'cfg>(
queue: &mut VecDeque<PathState<'cfg>>,

ADD
crates/rome_js_analyze/tests/specs/correctness/noUnreachable/issue-3654.js
+function testFAIL() {
+ try {
+ return fn();
+ } catch {
+ log();
+ } finally {
+ log();
+ }
+ return null;
+}
+
+function testOK() {
+ try {
+ return fn();
+ } catch {
+ log();
+ }
+ return null;
+}
+
+function fn() {
+ throw new Error('nope!');
+}
+
+function log() {}

ADD
crates/rome_js_analyze/tests/specs/correctness/noUnreachable/issue-3654.js.snap
+---
+source: crates/rome_js_analyze/tests/spec_tests.rs
+assertion_line: 73
+expression: issue-3654.js
+---
+# Input
+```js
+function testFAIL() {
+ try {
+ return fn();
+ } catch {
+ log();
+ } finally {
+ log();
+ }
+ return null;
+}
+
+function testOK() {
+ try {
+ return fn();
+ } catch {
+ log();
+ }
+ return null;
+}
+
+function fn() {
+ throw new Error('nope!');
+}
+
+function log() {}
+
+```
+
+

fix(rome_js_analyze): improve exception control flow handling (#3761)