news 2026/9/20 23:46:07

深入理解 Rome 的 noShadowRestrictedNames 规则:禁止遮蔽受限全局名称

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入理解 Rome 的 noShadowRestrictedNames 规则:禁止遮蔽受限全局名称

深入理解 Rome 的 noShadowRestrictedNames 规则:禁止遮蔽受限全局名称

【免费下载链接】toolsUnified developer tools for JavaScript, TypeScript, and the web项目地址: https://gitcode.com/gh_mirrors/to/tools

导读

noShadowRestrictedNames是 Rome(统一 JavaScript/TypeScript 开发者工具链)中一条由官方推荐(recommended)的 lint 规则,从 v0.9.0 起随项目发布。它用于禁止开发者用NaNSetJSONArrayObjectundefined等内置受限名称去声明变量、函数、参数或 catch 子句绑定,从而避免遮蔽(shadowing)全局名称导致的命名混乱与难以排查的 bug。读完本文,你将完整掌握该规则的触发场景、诊断信息格式、底层实现原理、内置受限名称清单,以及如何在rome.json配置中开启、关闭或调整它的告警级别。

规则概览:这是什么规则

noShadowRestrictedNames归类于 Rome lint 的suspicious(可疑代码)分组,规则标识为suspicious/noShadowRestrictedNames。官方文档对它的定义只有一句话:

Disallow identifiers from shadowing restricted names.(禁止标识符遮蔽受限名称。)

所谓"遮蔽",指的是在某个作用域内声明了一个与全局(built-in)名称同名的绑定,导致在该作用域内引用该名称时,解析到的是局部绑定而不是全局对象。例如在函数内声明function JSON() {},函数体内的JSON就不再指向全局JSON对象。这类写法极易造成混淆——正如规则诊断信息中的提示语所说:"It's easy to confuse the origin of variables when they're named after a known global."(当变量以某个已知全局命名时,很容易混淆变量的来源。)

该规则标注为recommended(推荐),意味着在 Rome 默认的推荐配置下它会被自动启用,无需额外手动开启。规则的推荐属性可以从源码中的声明直接确认:no_shadow_restricted_names.rs 中declare_rule!宏显式设置了recommended: true,并在同一位置声明了version: "0.9.0"与规则名noShadowRestrictedNames

何时触发:文档中的 Invalid 示例

官方文档给出了 5 个会触发该规则的典型反例,全部来自规则源码中的文档注释(no_shadow_restricted_names.rs),并经快照测试逐一验证。下面逐一说明:

用受限名称声明函数

function NaN() {}

诊断输出(位置1:10指向函数名):

suspicious/noShadowRestrictedNames.js:1:10 lint/suspicious/noShadowRestrictedNames ━━━━━━━━━━━ ✖ Do not shadow the global "NaN" property. > 1 │ function NaN() {} │ ^^^ ℹ Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

用受限名称声明变量

let Set;

诊断位置1:5指向标识符Set

在 catch 子句中遮蔽

try { } catch(Object) {}

诊断位置1:15指向 catch 参数Object。注意catch (Object)中的Object是一个绑定,它同样会遮蔽全局Object

用受限名称声明构造函数

function Array() {}

在函数参数中遮蔽

function test(JSON) {console.log(JSON)}

诊断位置1:15指向参数JSON。这条示例尤其值得注意:即便函数体内确实"使用"了这个参数,规则仍然会告警,因为遮蔽全局名称本身即被认为是反模式。

诊断信息统一格式

所有触发场景的诊断输出结构一致,包含三个组成部分:

  1. 错误级别前缀,表示该诊断属于 error 级别(当规则以默认 error 级别运行时);
  2. 主消息Do not shadow the global "<名称>" property.,其中的<名称>是实际被遮蔽的受限名称;
  3. 提示(note)Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.,建议开发者重命名该变量。

诊断位置的精确性从快照可以看出:function NaN() {}定位到1:10let Set;定位到1:5catch(Object)定位到1:15,均精准落在标识符绑定 token 上,而非整行或整个声明。这种基于精确 token 范围的定位能力,源于规则的查询类型为Ast<JsIdentifierBinding>(标识符绑定节点),诊断范围使用binding.syntax().text_trimmed_range()取得(见 no_shadow_restricted_names.rs)。

内置受限名称清单:BUILTIN 常量

规则判断"是否为受限名称"的依据是rome_js_analyze中 globals 模块定义的BUILTIN常量:一个长度为 66 的字符串数组(runtime.rs)。完整清单如下:

分类名称
全局对象/构造器ArrayArrayBufferBooleanDataViewDateErrorFunctionJSONMapMathNumberObjectPromiseProxyRegExpSetStringSymbolWeakMapWeakRefWeakSetBigIntBigInt64ArrayBigUint64ArrayFloat32ArrayFloat64ArrayInt8ArrayInt16ArrayInt32ArrayUint8ArrayUint8ClampedArrayUint16ArrayUint32ArraySharedArrayBufferAtomicsFinalizationRegistryReflectAggregateErrorEvalErrorRangeErrorReferenceErrorSyntaxErrorTypeErrorURIError
特殊值NaNInfinityundefinedglobalThis
全局函数decodeURIdecodeURIComponentencodeURIencodeURIComponentescapeunescapeevalisFiniteisNaNparseFloatparseInt
对象原型方法constructorhasOwnPropertyisPrototypeOfpropertyIsEnumerabletoLocaleStringtoStringvalueOf

几个值得注意的细节:

  • 该清单不仅包含标准库构造器,还包含undefinedNaNInfinity这类全局属性,以及evalparseInt等全局函数;
  • 原型链上的通用方法(toStringvalueOfconstructor等)也被纳入,因此function toString() {}这类声明同样会被拦截;
  • 判断是大小写敏感的精确字符串匹配:run函数中执行BUILTIN.contains(&name)(no_shadow_restricted_names.rs),所以let set;(小写)不会被该规则拦截,而let Set;会。从命名上看,该常量描述的是"内置(built-in)全局名称"全集,而同一文件中的ES_5ES_2015ES_2017ES_2020ES_2021等常量则按 ECMAScript 版本细化了各时代的全局集合,BUILTIN可以理解为各版本全局名称的汇总集合(从源码结构看,BUILTIN覆盖了从 ES5 到较新提案的绝大多数全局绑定)。

测试验证:invalid.jsonc 与快照

规则的行为由测试套件固化。测试输入文件位于 crates/rome_js_analyze/tests/specs/suspicious/noShadowRestrictedNames/invalid.jsonc,内容为 7 个待检测的代码片段:

[ "function NaN() {}", "function undefined() {}", "function Infinity() {}", //function arguments() {} //function eval() {} "function Array() {}", "function test(JSON) { console.log(JSON); }", "let Set;", "try {} catch (Object) {}" ]

对应的快照文件 invalid.jsonc.snap 记录了每个输入产生的完整诊断。通过对比测试输入与快照输出,可以得到两个额外的实证结论:

  1. undefinedInfinity同样会被拦截function undefined() {}function Infinity() {}分别产生Do not shadow the global "undefined" property.Do not shadow the global "Infinity" property.的诊断,说明undefinedInfinityNaN一样位于受限清单中(与前述BUILTIN数组内容吻合);
  2. argumentseval被注释排除在测试之外//function arguments() {}//function eval() {}):eval虽在BUILTIN清单中,但这两个用例被刻意注释,说明测试作者对部分边界场景做了有意的取舍,实际拦截行为以源码BUILTIN清单为准。

规则实现原理:从 AST 绑定到诊断

该规则的完整实现位于 no_shadow_restricted_names.rs,整个执行流程只有两个核心阶段:

阶段一:匹配标识符绑定(run)

type Query = Ast<JsIdentifierBinding>; type State = State; type Signals = Option<Self::State>; type Options = (); fn run(ctx: &RuleContext<Self>) -> Option<Self::State> { let binding = ctx.query(); let name = binding.name_token().ok()?; let name = name.text_trimmed(); if BUILTIN.contains(&name) { Some(State { shadowed_name: name.to_string() }) } else { None } }

要点:

  • 查询类型为Ast<JsIdentifierBinding>,即规则只对"标识符绑定"节点生效。JsIdentifierBinding是 Rome JS 语法树中代表变量声明、函数名、参数、catch 绑定等"绑定"位置的节点类型(定义于 rome_js_syntax crate 的生成代码中),这决定了规则天然覆盖let/const/var声明、函数声明名、函数参数、catch 子句参数等所有绑定场景;
  • 取到绑定节点后,通过name_token()获取名称 token,用text_trimmed()去掉可能存在的空白后得到实际标识符文本;
  • BUILTINcontains精确匹配;命中则构造State { shadowed_name }作为信号传出,未命中返回None表示不报告;
  • type Options = ()表明该规则不接受任何配置选项,只能整体开/关或调整级别。

阶段二:生成诊断(diagnostic)

fn diagnostic(ctx: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> { let binding = ctx.query(); let diag = RuleDiagnostic::new( rule_category!(), binding.syntax().text_trimmed_range(), markup! { "Do not shadow the global \"" {state.shadowed_name} "\" property." }, ) .note( markup! {"Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global."}, ); Some(diag) }

要点:

  • 诊断范围直接取自绑定节点语法文本的精确范围(text_trimmed_range()),这就是前面看到的诊断能精确定位到1:101:5这类列号的原因;
  • 主消息中的受限名称来自run阶段捕获的state.shadowed_name,保证"报什么名字"与"检测到什么名字"完全一致;
  • 通过.note()附加重命名建议,即文档中展示的提示行。

调用链与注册

从规则声明到实际生效的完整链路为:

  1. 规则在 suspicious.rs 中通过declare_rule!宏注册;
  2. 规则的诊断分类为lint/suspicious/noShadowRestrictedNames,在 categories.rs 中登记;
  3. 运行时由rome_analyze框架的 visitor 遍历 AST,对每个JsIdentifierBinding节点调用规则的run/diagnostic方法产出诊断。

此外,规则文档生成是源码驱动的:官网文档页面(即本仓库中的 website/src/pages/lint/rules/noShadowRestrictedNames.md)中的示例与诊断输出,与declare_rule!注释中的expect_diagnostic示例一一对应,属于通过 xtask 等代码生成工具从源码注释同步出来的文档,因此文档与实现始终一致。

如何在项目中配置该规则

由于该规则是 recommended 规则,Rome 默认配置会启用它。若需自定义,可在项目根目录的rome.json中针对suspicious分组或单条规则进行配置:

{ "linter": { "enabled": true, "rules": { "recommended": true, "suspicious": { "noShadowRestrictedNames": "off" } } } }

配置语义说明:

  • "recommended": true表示启用所有推荐规则,此时noShadowRestrictedNames默认以error级别生效;
  • 若要降级为警告而不完全关闭,可将值改为"warn";完全关闭则用"off"
  • 规则的配置项在源码中对应 rules.rs 里的pub no_shadow_restricted_names: Option<RuleConfiguration>字段,并通过"noShadowRestrictedNames"字符串在反序列化时映射(见 rules.rs 的匹配分支),因此rome.json中使用的键名是驼峰式的noShadowRestrictedNames
  • 由于该规则Options类型为(),它不支持类似 ESLint 的options数组(如自定义受限名称列表),只能控制开关与告警级别。

也可通过 CLI 参数在命令行临时调整,例如:

rome check --linter-rules-suspicious-no-shadow-restricted-names=off src/

(注:具体 CLI 长选项由 rules.rs 中的bpaf(long(...))属性生成,实际可用选项名以当前版本rome --help输出为准。)

相关使用指引

  • 需要禁用某条规则或添加// rome-ignore抑制注释时,参见 linter 文档中的 Disable a rule 一节;
  • 需要了解规则的全局配置、级别语义与 recommended 机制时,参见 linter 文档中的 Rule options 一节;
  • 所有规则的完整索引见 rules 索引页。

与其他全局相关规则的协同

rome_js_analyze的 globals 模块(crates/rome_js_analyze/src/globals/runtime.rs)中,除了BUILTIN之外,还定义了按 ECMAScript 版本划分的ES_5ES_2015ES_2017ES_2020ES_2021等全局集合。这些集合与BUILTIN共同构成了 Rome 对"全局名称"的认知基础,被其他规则复用:

  • noShadowRestrictedNames(禁止遮蔽内置名称)不同,部分其他规则关注的是"使用未声明变量"或"对只读全局赋值"等相邻问题,它们同样依赖这套全局名称清单做判定;
  • 因此,BUILTIN清单的增删会同时影响多条规则的判定结果,属于全局共享的基础数据。

从源码结构可以推断,BUILTIN是各 ES 版本全局集合的上位汇总:它既包含 ES5 时代的ArrayObjectJSONMath等经典全局,也包含BigIntAtomicsFinalizationRegistryWeakRefAggregateError等较新标准中的全局,甚至纳入了escape/unescape这类历史遗留全局,覆盖面完整。

总结

维度结论
规则全名suspicious/noShadowRestrictedNames
引入版本v0.9.0
是否推荐是(recommended,默认 error 级别启用)
检测对象所有JsIdentifierBinding(变量声明、函数名、参数、catch 绑定)
判定依据标识符文本精确匹配BUILTIN常量(66 个内置全局名称)
支持选项无(仅可开/关/调整级别)
典型消息Do not shadow the global "<名称>" property.

noShadowRestrictedNames是一条小而精的规则:实现只有约 80 行 Rust 代码,却通过精确的 AST 绑定查询与共享的全局名称清单,有效拦截了一类极易引发命名混淆的反模式。结合源码(no_shadow_restricted_names.rs、runtime.rs)、测试(invalid.jsonc)与配置(rules.rs),你可以完整追踪它的行为边界,并在实际项目中正确使用或调整它。

【免费下载链接】toolsUnified developer tools for JavaScript, TypeScript, and the web项目地址: https://gitcode.com/gh_mirrors/to/tools

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 23:45:57

chezmoi 模板函数 protonPassJSON:从 Proton Pass 提取结构化密钥数据

开发工具CLI配置管理 【免费下载链接】chezmoi Manage your dotfiles across multiple diverse machines, securely. 项目地址&#xff1a; https://gitcode.com/gh_mirrors/ch/chezmoi 点击查看 免费下载 本篇技术指南讲解 chezmoi 点文件管理器中内置的模板函数 protonPassJ…

作者头像 李华
网站建设 2026/9/20 23:44:46

构建可进化的AI编程工作台:从工具堆砌到个人知识操作系统

1. 这不是“AI编程工具合集”&#xff0c;而是一套可生长的个人工作台系统我第一次把“AI编程”当真&#xff0c;是在一个凌晨三点的调试现场——本地跑不通的单元测试&#xff0c;被我喂给刚搭好的本地模型&#xff0c;它不仅指出了mock对象初始化顺序的bug&#xff0c;还顺手…

作者头像 李华
网站建设 2026/9/20 23:44:24

Java 6/7/8历史版本官方下载指南与配置避坑手册

你在帮一个上了年纪的金融项目换开发机&#xff0c;或者刚接手一套十年前写的老系统&#xff0c;大概率会被同一个问题卡住&#xff1a;Java 历史版本从哪里下载&#xff1f;尤其是 Java 6、Java 7、Java 8 这种早就被官方“藏”起来的版本&#xff0c;网上搜出来一堆垃圾站、捆…

作者头像 李华
网站建设 2026/9/20 23:43:15

用开源工具搭建本地优先的科研工作台:从文献管理到写作全流程

经常听到身边朋友抱怨&#xff1a;课题一多&#xff0c;手头堆积的文献、实验记录、会议笔记全乱成一锅粥&#xff0c;想找一篇去年读过的论文&#xff0c;翻遍文件夹都找不到。这两年我也一直在折腾怎么把整个研究流程管起来&#xff0c;后来索性用一系列开源工具拼装了一套自…

作者头像 李华