ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Foundry forge-lint 的 todo-comment 规则:用 TODO/FIXME 标记审计未完成代码

Foundry forge-lint 的 todo-comment 规则:用 TODO/FIXME 标记审计未完成代码 Foundry forge-lint 的 todo-comment 规则用 TODO/FIXME 标记审计未完成代码【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundryTODO与FIXME是开发过程中最常见的未完成工作标记但把它们随合约一起部署到链上往往意味着遗留了待办功能或已知缺陷。Foundry 内置的 Solidity 静态检查工具forge-lint提供了todo-comment规则严重级别Info规则 IDtodo-comment专门扫描行注释、块注释与 NatSpec 注释中的此类标记帮助开发者在代码进入生产环境前发现未完成的工作。本文将以 crates/lint/docs/todo-comment.md 为骨架结合仓库中的 规则实现、测试用例 与配置解析源码讲清该规则的触发条件、边界行为、启用方式与抑制方法读完即可在自己的 Foundry 项目里落地使用。规则概览检测什么、级别与 IDtodo-comment是forge-lint众多内置规则之一其元信息在源码中通过declare_forge_lint!宏声明见 crates/lint/src/sol/info/todo.rs规则 IDtodo-comment严重级别Info信息级属于风格/规范类提示对应终端青色高亮规则描述unresolved \TODO or FIXME comment未解决的 TODO 或 FIXME 注释检测范围行注释、块注释以及 NatSpec 注释中的TODO和FIXME标记不区分大小写todo、ToDo、FIXME、fixme等写法均会被识别。从规则注册表可以看到该规则以early早期阶段lint pass 的方式注册即在语法分析阶段对完整源文件扫描不依赖类型解析等后期信息。它由TodoComment结构体实现EarlyLintPass在check_full_source_unit中对整个编译单元的注释流进行遍历crates/lint/src/sol/info/todo.rs。由于Info级别的规则默认不参与forge lint的默认严重级别筛选详见下文如何在项目中启用它更像一个可选的待办事项提醒器默认不打扰需要时一条命令即可全局排查。为什么要在意 TODO / FIXME原文档的立意非常明确TODO和FIXME注释本质上是开发笔记。将它们留在进入生产环境的合约中等于对外宣告这块功能还没写完或这个检查是错的但仍在上线。具体风险包括未完成的功能TODO: implement access control意味着该函数当前没有权限控制可能被任意调用已知缺陷被发布FIXME: this check is wrong表明存在一条已知的错误校验逻辑一旦部署便不可轻易修改合约不可变审计噪音安全审计与代码评审时残留标记会分散注意力掩盖真正需要关注的逻辑问题。值得注意的是Foundry 的 linter 在设计上非常务实测试目录和脚本目录下的文件默认不参与除unsafe-cheatcode、environment-read-across-mutation之外的所有 lint 检查含显式选中的规则见 crates/lint/README.md。因此todo-comment主要针对的是生产合约源码——这恰好与该规则防止未完成工作流入生产的初衷一致。触发示例与修复方式原文档给出了一个典型的触发示例合约中存在未完成实现与错误校验注释里留下了标记。contract Vault { // TODO: implement access control function withdraw() public {} // FIXME: this check is wrong function deposit(uint256 amount) public { require(amount 0); } }运行forge lint --only-lint todo-comment后两处注释都会被报告为未解决的标记。正确的做法是真正完成这项工作而不是简单删除注释contract Vault { function withdraw() public onlyOwner {} function deposit(uint256 amount) public { require(amount 0, zero amount); } }即为withdraw补上真正的访问控制如onlyOwner修饰器为deposit补上带错误信息的完整校验。若无法立即完成则应保留上下文并显式抑制该 lint见下文如何抑制而非放任其流入生产。源码视角标记识别的边界规则todo-comment并非简单的注释里含 TODO 就报警其识别逻辑有精密的边界处理理解这些规则可以避免误报与漏报。核心逻辑位于 crates/lint/src/sol/info/todo.rs 的marker_at_start函数以及配套的is_control_comment、strip_comment_prefix辅助函数。标记集合与大小写const MARKERS: [char] [TODO, FIXME];匹配时使用eq_ignore_ascii_case做大小写不敏感的前缀比较crates/lint/src/sol/info/todo.rs因此TODO、todo、ToDo、FixMe、fixme均视为同一标记。合法的后续字符边界条件/// Characters that may directly follow a marker and still count as a real marker. const TRAILING: [char] [:, (, ,, ;, ., )];标记后面紧跟:、(、,、;、.、)时必然算作标记覆盖了TODO:、FIXME(...)、TODO(alice):等常见写法。裸标记bare marker的位置限制单独一个TODO后面无任何字符只有出现在行首或紧跟在 NatSpec 标签开头之后才被判定为标记// A bare marker only counts at the start of a line or right after a NatSpec tag. let mut allow_bare true;遍历 token 时非*的 token 会把allow_bare更新为是否以开头crates/lint/src/sol/info/todo.rs。这意味着// TODO implement access control行首裸标记→ 触发// check this later TODO标记不在行首→不触发因为它不是一段待办描述的开头/// dev TODO: implement the actual logicNatSpec 标签后→ 触发。排除普通文件名todo.md.后若紧跟字母数字或下划线则该 token 被判定为普通标识符而非标记这正是原文档所说todo.md这类普通文件名不算标记的实现依据// A . must end the marker, not start an identifier (TODO.md). Some(.) !trailing.next().is_some_and(|c| c.is_alphanumeric() || c _),同理todo-list、TODO_ITEMS_LIMIT、todolist这类作为普通单词/标识符出现的写法也不会被误报。块注释按物理行拆分、去重与聚合报告未归一化的块注释在源码中作为单个字符串存储实现会将其按物理行拆分后再逐行扫描crates/lint/src/sol/info/todo.rs因此/* ... */中间任意一行的标记都能命中包括带*前缀的星号风格块注释。同一注释中出现多个标记时会去重!found.contains(marker)并聚合到一条报告中消息会列出全部不重复的标记单标记unresolved \TODO comment多标记unresolved \TODO, FIXME comments例如// FIXME first, TODO second, and fixme third会报告FIXME而// TODO: first, todo: second, FIXME: third, fixme: fourth会报告TODO, FIXME去重后各计一次。控制注释豁免与字符串安全is_control_comment会豁免两类特殊注释以compile-flags:开头的编译指令注释以及以forge-lint:开头的抑制指令注释crates/lint/src/sol/info/todo.rs。后者保证了// forge-lint: disable-next-line(todo-comment)这条抑制注释本身不会被误判为待办标记。另外该规则只扫描注释节点不触及字符串字面量。测试用例 crates/lint/testdata/Todo.sol 专门验证了return this TODO is just data, not a comment;这种字符串内容不会被误报。测试用例佐证边界行为的完整覆盖仓库为todo-comment提供了完整的端到端测试crates/lint/testdata/Todo.sol 与对应的期望输出 crates/lint/testdata/Todo.stderr。测试文件首行通过编译指令指定只运行该规则//compile-flags: --only-lint todo-comment随后用 20 余个场景逐一覆盖了上述边界行为可作为理解规则的活文档场景示例期望行内标记//TODO: validate this报告TODO大小写不敏感// ToDo: implement access control、// FixMe: this is broken报告句号结尾// TODO. This should still be treated...报告.后跟空格括号形式/*TODO(alice): this one should be fixed */报告NatSpec 行注释/// ToDo: document this properly报告多标记去重// TODO: first, todo: second, FIXME: third, fixme: fourth报告TODO, FIXME两条聚合裸标记行首// TODO报告裸标记句中// check this later TODO不报告块注释多行/* ... TODO: ... */报告星号/纯文本块注释* TODO implement.../FIXME implement...报告紧凑 NatSpec///dev TODO: ...报告普通文件名/单词TODO_ITEM_LIST、todo-list、todo.md不报告字符串字面量return this TODO is just data...不报告行内抑制// forge-lint: disable-next-line(todo-comment)不报告期望输出文件 crates/lint/testdata/Todo.stderr 中每条诊断都带note[todo-comment]前缀和精确的源码位置定位方便与forge lint的实际输出逐条对照。这些测试同时验证了Info级别规则映射为Note级诊断见 crates/config/src/lint.rs 中Severity到诊断Level的转换。如何在项目中启用与配置默认行为Info 规则不在默认严重级别内todo-comment的严重级别是Info而LinterConfig的默认严重级别只包含高、中、低三档severity: vec![Severity::High, Severity::Med, Severity::Low],见 crates/config/src/lint.rs。因此默认的forge lint不会报告todo-comment需要显式启用。命令行启用方式forge lint提供三个相关参数见 crates/forge/src/cmd/lint.rs# 仅运行 todo-comment 这一条规则会绕过严重级别筛选 forge lint --only-lint todo-comment # 按严重级别启用info 级别包含 todo-comment forge lint --severity info # 同时启用多个级别 forge lint --severity high med low info # 报告未被使用的行内抑制注释便于清理过期的 disable 指令 forge lint --only-lint todo-comment --report-unused-suppressions从 lint 命令实现 可以看到当传入--only-lint时严重级别过滤被置空vec![]相当于显式点名运行指定规则未传时则使用--severity参数或项目配置中的severity列表。通过 foundry.toml 持久化配置在项目的foundry.toml中写入[lint]段即可让规则默认生效。LinterConfig支持的字段包括crates/config/src/lint.rs[lint] # 参与的严重级别high / med(medium) / low / info / gas / code-size severity [high, medium, low, info] # 按规则 ID 排除例如不检查 mixed-case-function exclude_lints [mixed-case-function] # 要忽略的路径 glob ignore [lib/**] # 是否在 forge build 时自动执行 lint默认 true lint_on_build true需要说明的是Severity::from_str对大小写不敏感并兼容med/medium、size/codesize/code-size等写法crates/config/src/lint.rs配置值与序列化输出统一为 kebab-casehigh、medium、info、gas、code-sizelint_on_build默认为true意味着启用后forge build也会顺带运行 lint——把todo-comment加入severity列表即可实现构建即提醒命令行参数的优先级高于配置文件--only-lint覆盖severity/exclude_lints传入路径时还会覆盖ignore配置crates/forge/src/cmd/lint.rs。整条规则的启用链路从 CLI 到规则执行的完整链路为forge lint命令crates/forge/src/cmd/lint.rs→SolidityLinter组装含with_severity/with_lints/without_lints→ 由solar编译器解析源码 →TodoComment的check_full_source_unit扫描注释节点crates/lint/src/sol/info/todo.rs→ 命中后经ctx.emit_with_msg产出note[todo-comment]诊断。规则入口check_full_source_unit第一步会通过ctx.is_lint_enabled(TODO_COMMENT.id())检查规则是否被启用未启用时直接返回避免无效扫描。如何抑制保留上下文、显式豁免原文档强调开发分支和已追踪的后续工作中合法地包含这些标记是允许的。当待办事项已被理解、接受并在计划内时应当保留有用的上下文并显式抑制 lint。行内抑制使用forge-lint行内指令作用范围是下一行// forge-lint: disable-next-line(todo-comment) // TODO: this marker is intentionally suppressed function suppressed() public {}该写法在 Todo.sol 测试用例 中得到验证——被抑制的标记不再产生诊断。同时抑制注释本身会被is_control_comment排除在标记扫描之外crates/lint/src/sol/info/todo.rs不会自触发报警。管理过期抑制如果后续代码完成了待办项、但抑制指令忘记删除会残留无效的抑制注释。此时可用forge lint --only-lint todo-comment --report-unused-suppressions--report-unused-suppressions会把运行期间没有实际抑制任何诊断的行内注释报告出来便于清理crates/forge/src/cmd/lint.rs。全局排除不推荐用于该规则也可以通过配置把整个规则从检查中剔除[lint] exclude_lints [todo-comment]但需要注意exclude_lints是整条规则不运行与保留标记但显式豁免不同。若只是个别待办需要保留优先使用行内抑制避免关闭规则后遗漏其他残留标记。小结todo-comment是forge-lint中一条轻量但实用的信息级规则它大小写不敏感地检测行注释、块注释与 NatSpec 注释中的TODO/FIXME标记支持TODO:、FIXME(...)、行首裸标记、NatSpec 标签后裸标记等常见形式它通过精细的边界判断排除todo.md、todo-list、TODO_ITEM_LIMIT等普通单词避免噪音由于属于Info级别默认不启用通过forge lint --only-lint todo-comment、--severity info或在foundry.toml的[lint]段加入severity [..., info]即可启用测试用例 Todo.sol 覆盖了全部边界行为是实现行为的最可靠参照对确实需要保留的标记使用// forge-lint: disable-next-line(todo-comment)显式抑制并配合--report-unused-suppressions定期清理过期指令。将这条规则纳入 CI 或构建流程配合lint_on_build true即可在每一轮构建中持续追踪未完成工作让TODO/FIXME从无声的开发笔记变成可管理、可追踪的质量信号。【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表