eslint-plugin-unicorn no-useless-compound-assignment:抓出恒等值复合赋值,让死代码无处留存
【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn
eslint-plugin-unicorn 的 no-useless-compound-assignment 是一条做复合赋值恒等值检测的建议型规则:x += 0、x *= 1这类写法,它会直接告诉你这是可以删掉的死代码,省掉你逐个审查的成本。下面我们从 5 个维度拆它:抓什么、放过什么、为什么拒绝自动修复、修复怎么双轨,以及边界压力测试。
规则速写
检测范围是一张写死的「运算符 → 恒等值」映射表(rules/no-useless-compound-assignment.js#L20-L26):
| 写法 | 运算符 | 恒等值 |
|---|---|---|
x += 0 | += | 0 |
x -= 0 | -= | 0 |
x *= 1 | *= | 1 |
x /= 1 | /= | 1 |
x **= 1 | **= | 1 |
恒等值——参与运算后不改变另一操作数结果的数。当引擎看到x *= 1,它等价于x = x * 1,而后者就是把x重新赋给x本身。所以这条规则抓的只是一种东西:看起来在做运算、实际在原地打转的那行代码,通常是重构之后忘删的死代码。
白名单地图:这些恒等值复合赋值会被放过
白名单是规则的另一半。你写出下面这些写法时,它们不会被报告:
| 写法 | 为什么放过 |
|---|---|
x \|= 0、x >>>= 0、x &= -1、x ^= 0 | 位运算是 int32/uint32 强转的惯用手法,刻意为之而非失误 |
x %= 1 | x % 1取的是小数部分,不是恒等操作 |
x += 0n、x *= 1n | BigInt 字面量,不是普通数字字面量 |
x += -0、x *= +1 | 一元正负号包裹的是UnaryExpression,且-0并不总是恒等 |
x += 0 as number(TypeScript) | 类型断言表达式,不是纯字面量 |
x \|\|= 0、x &&= 1、x ??= 0 | 逻辑赋值运算符不在检测范围 |
x = 0、x = 1 | 普通赋值,不是复合赋值 |
x += y、x += foo() | 右操作数的运行时值无法静态确定 |
把关的是isNumericLiteral检查(rules/ast/literal.js#L29):节点必须是Literal且value是 number 才放行,再去比对值与当前运算符的恒等值(rules/no-useless-compound-assignment.js#L42)。过不了这道关的,一律跳过。
unicorn no-useless-compound-assignment 为什么不能自动修复
数值上看,x += 0确实是恒等操作;麻烦在于复合赋值不是纯数字加法,结果由变量的运行时取值决定,静态分析无法穷举。比如"a" += 0是字符串拼接,得到"a0"而不是"a";5n *= 1会直接抛TypeError,BigInt 与 Number 不能混算;而如果x恰好装着-0,做完x += 0它就成了+0——两者在 IEEE 754 里并不相等。
所以规则元数据里不声明 fixer,只声明hasSuggestions: true(rules/no-useless-compound-assignment.js#L109)。
自动修复意味着引擎替你默默改写代码,上面三个反例都会变成你没预料到的行为变更。建议模式下,改动以候选形式交给编辑器呈现,由你敲一个键确认——这正是 eslint 建议 vs 自动修复 的本质区别:fix 承担风险,suggestion 把风险留给确认它的人。
修复双轨:删整句,或换左值
命中之后,规则先看赋值表达式在 AST 里的位置,再决定给哪条建议(rules/no-useless-compound-assignment.js#L51-L79)。分支长这样:
x op 恒等值 命中 ├─ 是独立语句,且容器为 Program / BlockStatement / StaticBlock / SwitchCase? │ └─ 是 → 建议「删除整句」(removeStatement,连带周边空白) └─ 结果被引用(函数实参、return 值、for 更新表达式…) └─ 建议「替换为左值」(return count -= 0 → return count) 兜底:表达式内部含注释 → 只报错,不出建议第一轨调用的是公共助手removeStatement(rules/fix/remove-statement.js),它把语句连同周围空白一起抹掉,但语句之外的尾随注释会原样保留。第二轨的左值直接取自源码文本,所以obj.x、this.count、foo.bar.baz这类成员表达式做左值也照样替换得回来。
// ❌ 赋值结果被 return 使用 return count -= 0; // ✅ 建议给出的结果 return count;测试验证过:删除建议覆盖顶层语句、块内缩进语句、switchcase 分支、classstatic块四种容器;替换建议则覆盖了成员表达式左值、逗号表达式、for更新表达式等引用场景。
边界压力测试:4 个容易看漏的写法
测试还专门压了几处判定细节,值得你过一遍:
| 输入 | 预期 | 原因 |
|---|---|---|
x += 0.0、x *= 0x1、x -= 0e3 | 命中,出建议 | 字面量value规范化后就是0/1,等价写法逃不掉 |
x += -0 | 不命中 | UnaryExpression而非纯字面量,且-0语义本身就是坑 |
x += 0; // keep | 命中,仍出「删除整句」 | 注释在语句之外,删除助手会保留尾随注释 |
x += /* keep */ 0; | 命中,但不出建议 | 注释在表达式内部,删除会把它一起弄丢 |
报错信息还会保留字面量的原始文本:x += 0.0会被报告为+= 0.0does not change the value,你核对代码时一眼能对上位置。
启用方式与一段真实 ESLint 报错输出
这条规则默认就在recommended与unopinionated两个配置里开启,不接收任何选项;type: 'suggestion'表明它属于不强加风格偏好的一类。测试快照里记录的一段报错输出,长这样:
1 | x *= 1; | ^^^^^^ This compound assignment is useless as `*= 1` does not change the value. unicorn/no-useless-compound-assignment Suggestion 1/1: Remove the useless assignment.先判恒等,再看上下文能否安全动手,最后保守出手——这是任何死代码清理规则都值得照抄的模板。
【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考