如何用ast-grep实现ESLint的prefer-object-spread规则?
解决ast-grep中
prefer-object-spread规则的嵌套参数匹配问题 问题背景
我正在对比不同代码检查器(linter)的自定义规则性能:ast-grep基于Rust开发,性能优于ESLint,但ESLint拥有大量成熟规则,ast-grep的规则数量相对较少。为此我尝试实现ESLint的经典规则prefer-object-spread,目标是将:
Object.assign({}, defaultConfig, customizedConfig)
转换为:
{...defaultConfig, ...customizedConfig}
遇到的问题
当前实现存在参数匹配错误:匹配Object.assign的顶层参数时,会错误匹配参数的子节点。例如:
- 输入
Object.assign({}, defaultConfigGenerator(16)),会被错误转换为{...defaultConfigGenerator(16), ...16} - 输入
Object.assign({}, Object.assign(a,b)),会错误展开内层Object.assign的参数,而非保留整个调用表达式作为单个展开项
问题根源是处理剩余参数$$$REMAINING_ARGS时,传入重写器的内容包含了子节点,且重写器内的元变量与主规则无关联,无法限制仅匹配arguments节点的直接子节点。
解决方案
修正ast-grep规则,通过精确匹配arguments的直接子节点,避免递归匹配子节点内容。以下是修正后的完整规则:
id: prefer_object_spread language: javascript rule: pattern: Object.assign($FIRST_ARG_OBJ_LITERAL, $$$REMAINING_ARGS) has: kind: object stopBy: end pattern: $FIRST_ARG_OBJ_LITERAL # 关键:限制$$$REMAINING_ARGS仅匹配arguments的直接子节点,不递归 inside: kind: arguments rewriters: - id: argument_spreader rule: # 匹配单个参数节点,而非递归匹配子内容 pattern: $ARG fix: "...$ARG" transform: ORIGINAL_FIRST_OBJ: rewrite: rewriters: [] # 移除第一个空对象字面量的处理 source: $FIRST_ARG_OBJ_LITERAL SPREAD_REMAINDERS: rewrite: rewriters: [argument_spreader] source: $$$REMAINING_ARGS joinBy: "," fix: "{${SPREAD_REMAINDERS}}"
测试案例验证
修正后的规则可以正确处理以下场景:
- 基础场景:
// 输入 Object.assign({}, abc, bde) // 输出 {...abc, ...bde} - 参数为函数调用:
// 输入 Object.assign({}, defaultConfigGenerator(16)) // 输出 {...defaultConfigGenerator(16)} - 嵌套Object.assign:
// 输入 Object.assign({}, Object.assign(a,b)) // 输出 {...Object.assign(a,b)} - 多参数混合:
// 输入 Object.assign({}, {a:1}, {abc:"Def"}, a, b, c) // 输出 {...{a:1}, ...{abc:"Def"}, ...a, ...b, ...c}
内容的提问来源于stack exchange,提问作者Steven Love
相关产品推荐
相关产品推荐

