ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

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

深入理解 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 起随项目发布。它用于禁止开发者用NaN、Set、JSON、Array、Object、undefined等内置受限名称去声明变量、函数、参数或 catch 子句绑定从而避免遮蔽shadowing全局名称导致的命名混乱与难以排查的 bug。读完本文你将完整掌握该规则的触发场景、诊断信息格式、底层实现原理、内置受限名称清单以及如何在rome.json配置中开启、关闭或调整它的告警级别。规则概览这是什么规则noShadowRestrictedNames归类于 Rome lint 的suspicious可疑代码分组规则标识为suspicious/noShadowRestrictedNames。官方文档对它的定义只有一句话Disallow identifiers from shadowing restricted names.禁止标识符遮蔽受限名称。所谓遮蔽指的是在某个作用域内声明了一个与全局built-in名称同名的绑定导致在该作用域内引用该名称时解析到的是局部绑定而不是全局对象。例如在函数内声明function JSON() {}函数体内的JSON就不再指向全局JSON对象。这类写法极易造成混淆——正如规则诊断信息中的提示语所说Its easy to confuse the origin of variables when theyre 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. Its easy to confuse the origin of variables when theyre 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。这条示例尤其值得注意即便函数体内确实使用了这个参数规则仍然会告警因为遮蔽全局名称本身即被认为是反模式。诊断信息统一格式所有触发场景的诊断输出结构一致包含三个组成部分错误级别✖前缀表示该诊断属于 error 级别当规则以默认 error 级别运行时主消息Do not shadow the global 名称 property.其中的名称是实际被遮蔽的受限名称提示noteConsider renaming this variable. Its easy to confuse the origin of variables when theyre named after a known global.建议开发者重命名该变量。诊断位置的精确性从快照可以看出function NaN() {}定位到1:10let Set;定位到1:5catch(Object)定位到1:15均精准落在标识符绑定 token 上而非整行或整个声明。这种基于精确 token 范围的定位能力源于规则的查询类型为AstJsIdentifierBinding标识符绑定节点诊断范围使用binding.syntax().text_trimmed_range()取得见 no_shadow_restricted_names.rs。内置受限名称清单BUILTIN 常量规则判断是否为受限名称的依据是rome_js_analyze中 globals 模块定义的BUILTIN常量一个长度为 66 的字符串数组runtime.rs。完整清单如下分类名称全局对象/构造器Array、ArrayBuffer、Boolean、DataView、Date、Error、Function、JSON、Map、Math、Number、Object、Promise、Proxy、RegExp、Set、String、Symbol、WeakMap、WeakRef、WeakSet、BigInt、BigInt64Array、BigUint64Array、Float32Array、Float64Array、Int8Array、Int16Array、Int32Array、Uint8Array、Uint8ClampedArray、Uint16Array、Uint32Array、SharedArrayBuffer、Atomics、FinalizationRegistry、Reflect、AggregateError、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError特殊值NaN、Infinity、undefined、globalThis全局函数decodeURI、decodeURIComponent、encodeURI、encodeURIComponent、escape、unescape、eval、isFinite、isNaN、parseFloat、parseInt对象原型方法constructor、hasOwnProperty、isPrototypeOf、propertyIsEnumerable、toLocaleString、toString、valueOf几个值得注意的细节该清单不仅包含标准库构造器还包含undefined、NaN、Infinity这类全局属性以及eval、parseInt等全局函数原型链上的通用方法toString、valueOf、constructor等也被纳入因此function toString() {}这类声明同样会被拦截判断是大小写敏感的精确字符串匹配run函数中执行BUILTIN.contains(name)no_shadow_restricted_names.rs所以let set;小写不会被该规则拦截而let Set;会。从命名上看该常量描述的是内置built-in全局名称全集而同一文件中的ES_5、ES_2015、ES_2017、ES_2020、ES_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 记录了每个输入产生的完整诊断。通过对比测试输入与快照输出可以得到两个额外的实证结论undefined、Infinity同样会被拦截function undefined() {}与function Infinity() {}分别产生Do not shadow the global undefined property.与Do not shadow the global Infinity property.的诊断说明undefined、Infinity与NaN一样位于受限清单中与前述BUILTIN数组内容吻合arguments与eval被注释排除在测试之外//function arguments() {}、//function eval() {}eval虽在BUILTIN清单中但这两个用例被刻意注释说明测试作者对部分边界场景做了有意的取舍实际拦截行为以源码BUILTIN清单为准。规则实现原理从 AST 绑定到诊断该规则的完整实现位于 no_shadow_restricted_names.rs整个执行流程只有两个核心阶段阶段一匹配标识符绑定runtype Query AstJsIdentifierBinding; type State State; type Signals OptionSelf::State; type Options (); fn run(ctx: RuleContextSelf) - OptionSelf::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 } }要点查询类型为AstJsIdentifierBinding即规则只对标识符绑定节点生效。JsIdentifierBinding是 Rome JS 语法树中代表变量声明、函数名、参数、catch 绑定等绑定位置的节点类型定义于 rome_js_syntax crate 的生成代码中这决定了规则天然覆盖let/const/var声明、函数声明名、函数参数、catch 子句参数等所有绑定场景取到绑定节点后通过name_token()获取名称 token用text_trimmed()去掉可能存在的空白后得到实际标识符文本与BUILTIN做contains精确匹配命中则构造State { shadowed_name }作为信号传出未命中返回None表示不报告type Options ()表明该规则不接受任何配置选项只能整体开/关或调整级别。阶段二生成诊断diagnosticfn diagnostic(ctx: RuleContextSelf, state: Self::State) - OptionRuleDiagnostic { 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. Its easy to confuse the origin of variables when theyre named after a known global.}, ); Some(diag) }要点诊断范围直接取自绑定节点语法文本的精确范围text_trimmed_range()这就是前面看到的诊断能精确定位到1:10、1:5这类列号的原因主消息中的受限名称来自run阶段捕获的state.shadowed_name保证报什么名字与检测到什么名字完全一致通过.note()附加重命名建议即文档中展示的ℹ提示行。调用链与注册从规则声明到实际生效的完整链路为规则在 suspicious.rs 中通过declare_rule!宏注册规则的诊断分类为lint/suspicious/noShadowRestrictedNames在 categories.rs 中登记运行时由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: OptionRuleConfiguration字段并通过noShadowRestrictedNames字符串在反序列化时映射见 rules.rs 的匹配分支因此rome.json中使用的键名是驼峰式的noShadowRestrictedNames由于该规则Options类型为()它不支持类似 ESLint 的options数组如自定义受限名称列表只能控制开关与告警级别。也可通过 CLI 参数在命令行临时调整例如rome check --linter-rules-suspicious-no-shadow-restricted-namesoff 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_5、ES_2015、ES_2017、ES_2020、ES_2021等全局集合。这些集合与BUILTIN共同构成了 Rome 对全局名称的认知基础被其他规则复用与noShadowRestrictedNames禁止遮蔽内置名称不同部分其他规则关注的是使用未声明变量或对只读全局赋值等相邻问题它们同样依赖这套全局名称清单做判定因此BUILTIN清单的增删会同时影响多条规则的判定结果属于全局共享的基础数据。从源码结构可以推断BUILTIN是各 ES 版本全局集合的上位汇总它既包含 ES5 时代的Array、Object、JSON、Math等经典全局也包含BigInt、Atomics、FinalizationRegistry、WeakRef、AggregateError等较新标准中的全局甚至纳入了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),仅供参考
返回列表