Foundry Forge Lint 变更解析:mixed-case-function对 public 常量 getter 命名的豁免机制
【免费下载链接】foundryFoundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.项目地址: https://gitcode.com/GitHub_Trending/fo/foundry
导读
本篇文章围绕 Foundry 仓库中 public-constant-getter-naming.md 这条 changelog 变更展开:forge与forge-lint两个子命令以patch级别更新,允许mixed-case-function规则放过使用大写(SCREAMING_SNAKE_CASE)命名的public viewgetter 函数,从而与既有对外部 getter 的豁免保持一致。读完本文,你将理解该规则的判定边界、底层源码实现、测试用例验证方式,以及在foundry.toml中如何通过mixed_case_exceptions调整行为。
变更背景:两条命名规范的一次对齐
Solidity 官方风格指南建议函数名使用mixedCase(小驼峰),这也是 Forge Lint 中mixed-case-function规则(严重级别Info)的检查目标。然而,对于"读取常量/存储的 getter",业界通行做法是使用SCREAMING_SNAKE_CASE,例如DOMAIN_SEPARATOR()。此前该规则只豁免external view的常量风格 getter,而public view的同类 getter 仍会被提示改名——这导致合约中语义等价、仅可见性不同的 getter 收到不一致的 lint 结果。
本次 changelog 变更正是为了消除这一不一致:
Allow uppercase public view getter names in
mixed-case-function, matching the existing exemption for external getters.
即:mixed-case-function现在同时接受public view与external view的常量风格 getter,判定条件完全一致。该变更同时作用于forge与forge-lint两个包(变更条目 的 frontmatter 声明了两者的patch级更新)。关于该 changelog 条目格式(包名: patch/minor/major+ 非空说明)的约定,可参见 .changelog/README.md。
规则核心:什么是"常量 getter"
判定启发式is_constant_getter
该豁免的实现位于 crates/lint/src/sol/info/mixed_case.rs。源码通过is_constant_getter函数对函数头做四项启发式判定,全部满足才视为"常量 getter"并跳过mixed-case-function检查:
| 判定条件 | 源码依据 | 说明 |
|---|---|---|
可见性为public或external | matches!(header.visibility(), Some(Visibility::Public \| Visibility::External)) | 内部函数不豁免,仍要求mixedCase |
状态可变性为view | header.state_mutability().is_view() | 必须是无状态修改的只读函数 |
| 无参数 | header.parameters.is_empty() | 带参数的函数即使view也不豁免 |
| 恰好一个返回值,且类型为 elementary 或 custom | matches!(header.returns(), [ret] if ret.ty.kind.is_elementary() \|\| ret.ty.kind.is_custom()) | 单个内置类型或自定义类型;数组、多返回值不豁免 |
名字本身符合SCREAMING_SNAKE_CASE | header.name.is_some_and(\|name\| check_screaming_snake_case(name.as_str()).is_none()) | 名字必须已经是大写蛇形,否则无豁免必要 |
其中check_screaming_snake_case定义于共享命名工具模块 crates/lint/src/sol/naming.rs,基于heck::AsShoutySnakeCase生成期望值,并保留首尾下划线;单字符名称在所有命名规范中一律豁免(naming.rs)。
检查流程:先判规范、再判豁免
在MixedCaseFunctionPass的check_item_function中(mixed_case.rs),执行顺序是:
- 仅当常量泛型参数
FUNCTIONS为true时处理函数; - 取函数名调用
check_mixed_case(含测试前缀与缩写模式两大领域豁免); - 若名字违反
mixedCase且不是常量 getter(!is_constant_getter(&func.header)),才通过emit_rename输出带机器可应用修复建议的告警。
这意味着:如果一个函数名字既不合法、又不满足 getter 启发式,才会收到类似consider using: hasParams的重命名提示。同一 lint pass 还被复用于可变变量的mixed-case-variable规则(mixed_case.rs),两者共享MixedCasePass<FUNCTIONS>泛型实现。
测试用例:豁免边界的完整覆盖
仓库测试数据文件 crates/lint/testdata/MixedCase.sol 直接体现了本次变更的验证点:
豁免通过(不报错)的示例:
contract PublicConstantGetters { bytes32 private separator; // Public getters follow the same naming convention as external getters. function DOMAIN_SEPARATOR() public view returns (bytes32) { return separator; } function PUBLIC_CUSTOM_TYPE() public view returns (IERC20) { return IERC20(address(0)); } }// SCREAMING_SNAKE_CASE is allowed for functions that are most likely constant getters function MAX_NUMBER() external view returns (uint256) {} function CUSTOM_TYPE_RETURN() external view returns (IERC20) {}仍然报错的边界情况(带~NOTE断言):
function PUBLIC_WITH_PARAM(uint256 value) public view returns (uint256) { return value; } // 有参数,不豁免 function INTERNAL_GETTER() internal view returns (bytes32) { return separator; } // internal,不豁免 function PUBLIC_MUTATOR() public returns (bytes32) { ... } // 非 view,不豁免对应的期望输出记录在 crates/lint/testdata/MixedCase.stderr,例如HAS_PARAMS、HAS_NO_RETURN、HAS_MORE_THAN_ONE_RETURN、NOT_ELEMENTARY_RETURN分别验证了"带参数""无返回值""多返回值""数组返回"四类不满足启发式的场景均照常告警,而DOMAIN_SEPARATOR与PUBLIC_CUSTOM_TYPE则没有对应告警条目。可以看出:豁免的条件是组合性的,任何一个条件不满足都会回退到常规mixedCase检查。
其他既有豁免:测试前缀、缩写模式与下划线
除了常量 getter 豁免外,mixed-case-function还内置了以下豁免(mixed_case.rs):
- 测试函数前缀:以
test、invariant_、statefulFuzz开头的函数名直接放行(Foundry 测试框架的既有命名约定); - 配置的缩写模式:通过
has_acronym_exception匹配用户配置的缩写列表(如ERC20中的ERC),规则要求缩写前缀本身已是 lowerCamelCase、后缀为 UpperCamelCase 且允许中间夹数字; - 首尾下划线保留:
check_mixed_case依赖的preserve_underscores会保留名字最前/最后的单个下划线,因此rescueERC20__这类名字在转换建议中下划线不会丢失(见 naming.rs 与 MixedCase.stderr 中的_rescueErc20/rescueErc20_建议)。
配置:调整允许的缩写模式
如果你有ERC、URI之外的自定义缩写需要放行,可在foundry.toml的[lint.lint_specific]段配置mixed_case_exceptions,它会整体替换默认列表(ERC、URI、ID、URL、API、JSON、XML、HTML、HTTP、HTTPS):
[lint.lint_specific] mixed_case_exceptions = ["ERC", "URI", "NFT"]该配置项的详细说明见规则文档 crates/lint/docs/mixed-case-function.md,文档同时给出了违规与修复的示例:get_balance/GetBalance应改写为getBalance。
使用建议与注意事项
- 命名优先遵循规范:普通函数、可变变量仍应使用
mixedCase;只有"确为常量 getter"(public/external+view+ 无参 + 单返回值 + 大写蛇形命名)才可依赖本豁免。 - 不要滥用豁免来规避改名:若函数带参数或修改状态,即使命名为
SCREAMING_SNAKE_CASE也仍会被提示改名,此时应遵循建议重命名而非改动签名。 - 兼容性考量:当重命名会破坏既有外部 ABI 或接口约定时,规则文档建议保留原拼写并对相应声明做 lint 抑制,而不是强行改变函数选择器(mixed-case-function.md)。
- 验证行为:可直接参照 MixedCase.sol 中的
PublicConstantGetters合约,在自己的项目中对public与external常量 getter 各写一个用例,运行forge lint观察是否如预期豁免。
小结
本次public-constant-getter-naming变更是一次聚焦的规则行为对齐:forge的mixed-case-functionlint 现在对public view与external view的常量风格 getter 一视同仁。从 源码实现 到 测试数据,仓库提供了完整的判定条件与边界用例,开发者可以据此精确预判自己的合约代码在forge lint下是否会触发mixed-case-function告警,并在必要时通过mixed_case_exceptions做细粒度配置。
【免费下载链接】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),仅供参考