Rome 静态检查规则 noUnsafeFinally 完全指南:禁用 finally 块中的危险控制流语句
发布时间:2026/9/20 13:09:48 锦皓数字建站

开发工具CLILint格式化静态分析代码质量构建工具【免费下载链接】toolsUnified developer tools for JavaScript, TypeScript, and the web项目地址https://gitcode.com/gh_mirrors/to/tools点击查看免费下载noUnsafeFinally是 RomeUnified developer tools for JavaScript, TypeScript, and the web内置的 correctness正确性类别 lint 规则自 v11.0.0 起默认推荐启用。它用于拦截finally块中出现的return、throw、break、continue语句——这些语句会静默覆盖try/catch中尚未完成的控制流行为是 JavaScript 中极易引发隐蔽 bug 的经典陷阱。读完本文你将完整掌握该规则的触发场景、边界行为、源码实现原理以及如何在rome.json或 CLI 中配置和关闭它。规则文档原文见 website/src/pages/lint/rules/noUnsafeFinally.md规则源码位于 crates/rome_js_analyze/src/analyzers/correctness/no_unsafe_finally.rs。规则背景为什么 finally 中的控制流语句是危险的JavaScript 规范规定try块和catch块中的控制流语句return、throw、break、continue会被挂起suspend直到finally块执行完毕后才真正生效。这意味着如果finally块中存在return它会抢先返回覆盖try/catch中本应返回的值如果finally块中存在throw它会覆盖try块抛出的异常以及catch块的重抛逻辑如果finally块中存在break/continue它会中断本应由try完成的跳转。这种“覆盖”行为往往与开发者的直觉相悖属于 unexpected behavior意外行为。noUnsafeFinally规则正是为了在静态分析阶段提前暴露这些隐患。在 Rome 中该规则被归类到 correctness 类别诊断代码为lint/correctness/noUnsafeFinally对应类别定义见 crates/rome_diagnostics_categories/src/categories.rs并标记为recommended: true即默认推荐开启。触发条件与规则概览noUnsafeFinally的触发对象是以下四种控制流语句只要它们直接或间接位于finally块内就会被标记语句覆盖的行为诊断消息return覆盖try/catch中挂起的返回值Unsafe usage of return.throw覆盖try/catch中挂起的异常Unsafe usage of throw.break覆盖try中挂起的跳转Unsafe usage of break.continue覆盖try中挂起的循环跳转Unsafe usage of continue.规则输出两级信息主诊断error/warn 级别的✖Unsafe usage of keyword.同时用^精确标出违规语句位置附注ℹkeyword in finally overwrites the control flow statements inside try and catch.解释覆盖语义。Invalid 示例规则实际拦截的五类场景以下示例全部来自规则文档与官方测试套件 crates/rome_js_analyze/tests/specs/correctness/noUnsafeFinally/invalid.js展示了该规则能够捕获的完整场景矩阵。场景一finally 中的 return 覆盖 try/catch 的 return(() { try { return 1; // 1 被挂起直到 finally 块结束才生效 } catch(err) { return 2; } finally { return 3; // 3 先于 1 被返回这不是我们期望的行为 } })();诊断输出correctness/noUnsafeFinally.js:7:9 lint/correctness/noUnsafeFinally ✖ Unsafe usage of return. 5 │ return 2; 6 │ } finally { 7 │ return 3; // 3 is returned before 1, which we did not expect │ ^^^^^^^^^ 8 │ } 9 │ })(); ℹ return in finally overwrites the control flow statements inside try and catch.实际运行效果函数返回值是3而非开发者预期的1。场景二finally 中的 return 吞掉 try 抛出的异常(() { try { throw new Error(Try); // 异常被抛出但挂起直到 finally 块结束 } finally { return 3; // 在异常抛出之前就返回了 3这不是我们期望的行为 } })();这里比场景一更隐蔽即使try中抛出异常finally里的return也会让异常消失函数静默返回3异常被吞掉。场景三finally 中的 throw 覆盖 catch 的重抛(() { try { throw new Error(Try) } catch(err) { throw err; // try 抛出的异常被捕获并重新抛出 } finally { throw new Error(Finally); // 实际抛出的是 Finally(...)这不是我们期望的行为 } })();诊断消息变为Unsafe usage of throw.调用方捕获到的是Error(Finally)catch块中对err的重抛处理形同虚设。场景四finally 中的 break label 跳出 try-finally(() { label: try { return 0; // 0 被挂起直到 finally 块结束 } finally { break label; // 在 0 返回之前就跳出了 try-finally 块 } return 1; })();带标签label的break同样会被捕获诊断消息为Unsafe usage of break.。实际执行时函数返回1try中的return 0被完全绕过。场景五finally 中的 break 跳出外层 switchfunction a() { switch (condition) { case a: { try { console.log(a); return; } finally { break; } } case b: { console.log(b); } } }无标签的break作用于最近的switch/循环边界同样会覆盖try中挂起的return规则照样报出Unsafe usage of break.。Valid 示例哪些写法是安全的规则并非一刀切地禁止finally中出现任何语句以下三类写法均不会被标记详见 crates/rome_js_analyze/tests/specs/correctness/noUnsafeFinally/valid.js1. finally 中只执行普通表达式无控制流语句let foo function() { try { return 1; } catch(err) { return 2; } finally { console.log(hola!); } };2. finally 中嵌套函数的 return 不影响外层控制流let foo function() { try { return 1; } catch(err) { return 2; } finally { let a function() { return hola!; } } };嵌套函数拥有自己独立的作用域与控制流边界return hola!属于内层函数不会覆盖外层的return 1。3. finally 中 switch 内的 break 只作用于该 switchlet foo function(a) { try { return 1; } catch(err) { return 2; } finally { switch(a) { case 1: { console.log(hola!) break; } } } };break只跳出 switch 语句本身不构成对外层 try 控制流的覆盖。源码实现原理finally 边界如何判定规则核心实现位于 no_unsafe_finally.rs对应生成文件 crates/rome_js_analyze/src/analyzers/correctness.rs。整条规则逻辑非常简洁值得逐层拆解。第一步把四类语句统一成一个节点联合declare_node_union! { pub(crate) ControlFlowStatement JsReturnStatement | JsThrowStatement | JsBreakStatement | JsContinueStatement }规则通过declare_node_union!宏将return、throw、break、continue四类语法节点合并为ControlFlowStatement并用它作为规则的查询类型impl Rule for NoUnsafeFinally { type Query AstControlFlowStatement; type State (); type Options (); fn run(ctx: RuleContextSelf) - Self::Signals { let query ctx.query(); if query.in_finally_block()? { Some(()) } else { None } } }只要某个控制流语句落在 finally 块内in_finally_block()返回true即产生一个信号该规则无附加状态与选项属于纯语法层面的检查。第二步向上遍历祖先节点寻找 finally 边界判定逻辑的核心是in_finally_block()方法从违规语句节点出发不断向父节点回溯一旦遇到JS_FINALLY_CLAUSE就返回“在 finally 块内”loop { let kind node.kind(); let should_stop match self { Self::JsBreakStatement(it) if it.label_token().is_none() sentinel_for_break(kind), Self::JsContinueStatement(_) sentinel_for_continue(kind), _ sentinel_for_throw_or_return(kind), }; if should_stop { break; } ... if node.kind() JsSyntaxKind::JS_FINALLY_CLAUSE { return Some(!is_label_inside_finally); } node node.parent()?; } Some(false)这里通过三组sentinel_*哨兵函数控制遍历的停止边界防止误判嵌套函数/类成员内部的语句sentinel_for_throw_or_return遇到任意函数、类成员、对象成员或文件根节点即停止——return/throw不能跨越函数边界因此嵌套函数内的return永远不会误报sentinel_for_continue在上一组基础上追加do-while、while、for、for-of、for-in等循环节点——continue不能跨越循环边界sentinel_for_break在上一组基础上追加switch节点——break不能跨越 switch 边界。这正是 valid 示例中“嵌套函数内的 return 安全”“switch 内的 break 安全”的源码级依据。第三步带标签语句的特殊处理对于带 label 的break/continue规则在向上遍历时同步追踪若在 finally 块内部遇到与语句标签同名的JsLabeledStatement则视为“标签定义在 finally 内部”此时跳出该 label 不会越过 finally 边界is_label_inside_finally true最终返回!is_label_inside_finally避免误报if let Some(label) label { if let Some(parent) node.parent().and_then(JsLabeledStatement::cast) { if parent.label_token().ok() .map_or(false, |it| it.text_trimmed() label.text_trimmed()) { is_label_inside_finally true; } } }而场景四break label指向try外部的 label中标签定义在 finally 之外is_label_inside_finally保持false因此正确报出违规。诊断消息的生成Some(RuleDiagnostic::new( rule_category!(), query.syntax().text_trimmed_range(), markup! { Unsafe usage of { query.description() }. }, ).note(markup! { { query.description() } in finally overwrites the control flow statements inside try and catch. }))description()按语句类型返回return/throw/break/continue字符串据此拼出你看到的完整诊断信息。测试套件规则行为的自动化验证Rome 为每条规则维护独立的规格测试目录noUnsafeFinally的测试位于 crates/rome_js_analyze/tests/specs/correctness/noUnsafeFinally/invalid.js15 行边界用例覆盖 finally 内的 return、条件分支内的 returnif/else包裹、finally 中嵌套 finally 的 return、label break、while/switch内的 break/continue、switch内break a跳出外层 label 等全部 invalid 形态valid.js8 行安全用例覆盖普通表达式 finally、嵌套函数内的 return/throw/break/continue、函数内 label 循环的 break/continue、while直接 break/continue 等不越界场景invalid.js.snap对应的诊断快照逐条记录了 15 个违规用例的完整诊断文本与行列位置任何规则行为变更都会引起快照比对失败从而保证规则输出稳定。测试由 crates/rome_js_analyze/tests/spec_tests.rs 驱动与文档页面示例保持一致文档源码中的注释块同时是declare_rule!宏的文档来源。配置与关闭方式noUnsafeFinally属于推荐规则默认即开启。如果需要调整可通过配置文件或 CLI 两种方式控制。方式一rome.json 配置在项目根目录的 rome.json 中于linter.rules.correctness下设置该规则字段映射定义见 crates/rome_service/src/configuration/linter/rules.rs{ linter: { rules: { correctness: { noUnsafeFinally: off } } } }每个规则的取值支持on、off、warn三档on将违规提升为 errorwarn降级为警告off完全关闭。方式二CLI 参数rules.rs中为规则生成了对应的命令行参数--no-unsafe-finally接收on|off|warn# 运行时关闭该规则 rome lint --no-unsafe-finallyoff src/ # 或降级为警告 rome lint --no-unsafe-finallywarn src/相关操作入口如需在文件中临时禁用规则或调整规则选项的通用机制可参考规则索引页 website/src/pages/lint/rules/index.mdx 以及 linter 指南中关于禁用规则与规则选项的说明。总结noUnsafeFinally是 Rome correctness 类别中实现简洁但价值极高的规则它针对finally块中return/throw/break/continue覆盖try/catch挂起控制流的语言陷阱提供精确的静态拦截。其实现通过节点联合查询 祖先遍历 哨兵边界 标签追踪四步完成判定配合覆盖 15 个 invalid 与 8 个 valid 用例的规格测试既保证了规则的查全率也严格控制了误报。建议所有项目保持其默认开启状态为 try/finally 资源清理代码提供一道可靠的安全防线。赞分享开发工具CLILint格式化静态分析代码质量构建工具【免费下载链接】toolsUnified developer tools for JavaScript, TypeScript, and the web项目地址https://gitcode.com/gh_mirrors/to/tools点击查看免费下载相关推荐ESLint no-unsafe-finally 规则详解阻止 finally 块中的控制流语句破坏异常处理ESLint no unsafe finally 规则详解阻止 finally 块中的控制流语句破坏异常处理 本文基于 ESLint 核心规则 no unsa开发工具Lint静态分析代码质量Rome 静态检查规则 noArrayIndexKey禁止使用数组索引作为 React keyRome 静态检查规则 noArrayIndexKey禁止使用数组索引作为 React key 本文基于 RomeBiome 前身开源仓库的官方规则文档编开发工具CLILint格式化静态分析代码质量构建工具Rome noDebugger 规则完全指南禁用 debugger 语句的原理、诊断输出与自动修复机制Rome noDebugger 规则完全指南禁用 debugger 语句的原理、诊断输出与自动修复机制 本文基于 Rome当前仓库 tools的官方规则文开发工具CLILint格式化静态分析代码质量构建工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。