Scalafix SymbolMatcher匹配导入前缀失败问题排查
问题根因
匹配失效的核心原因是SymbolMatcher.normalized的匹配规则和Scala语义符号的实际结构不匹配:
scalaz.std.list.listInstance直接定义在scalaz.std.list包路径下,你定义的std匹配器传入的路径和实际符号前缀完全对齐,所以可以正常触发匹配。scalaz.syntax.std.list路径下的成员实际定义在对应位置的包对象中,ToListOpsFromList的完整符号路径是scalaz.syntax.std.list.package.ToListOpsFromList,你定义的syntax匹配器传入的路径没有包含.package段,无法识别Importer节点对应的语义符号,导致第一个匹配分支完全不触发。- 你之前测试把
syntax(_)换成通配符能匹配成功、手动写AST结构精确匹配能生效,就是绕过了SymbolMatcher的符号校验,直接匹配了语法树结构,反过来也验证了是符号路径配置错误的问题。
正确实现方式
不要拆分Importer的前缀和导入名分别匹配,直接对要移除的导入项本身做全符号匹配,这种写法可以避开包对象、路径嵌套、多导入同个前缀等场景的匹配异常,鲁棒性更高:
- 给两个要移除的导入项配置完整的符号路径,注意给属于包对象的
ToListOpsFromList补全.package路径段 - 遍历语法树时直接匹配目标
Importee.Name节点,匹配到就调用移除逻辑即可
修复后可正常运行的规则代码
package fix import scala.meta._ import scalafix.v1._ class MigrateList extends SemanticRule("MigrateList") { private lazy val toListOpsImport = SymbolMatcher.normalized("scalaz.syntax.std.list.package.ToListOpsFromList") private lazy val listInstanceImport = SymbolMatcher.normalized("scalaz.std.list.listInstance") override def fix(implicit doc: SemanticDocument): Patch = doc.tree.collect { case ip @ Importee.Name(toListOpsImport(_)) => Patch.removeImportee(ip) case ip @ Importee.Name(listInstanceImport(_)) => Patch.removeImportee(ip) }.asPatch }
用你提供的测试输入运行上述规则,
scalaz.std.list.listInstance和scalaz.syntax.std.list.ToListOpsFromList两个导入会被全部移除,输出结果完全符合预期。
内容的提问来源于stack exchange,提问作者Charles_F
相关产品推荐
相关产品推荐

