为什么scala.meta.Term.Param#toString删除修饰符?



我正在尝试使用语义scalafix插件重命名匿名函数的参数。相关代码如下所示:

case Term.Apply(func, args) =>
args.collect { case Term.Block(List(Term.Function(List(arg), _))) =>
Patch.replaceTree(arg, arg.copy(name = Term.Name("components")).toString())

问题是,这会{ implicit foo =>更改为{ components =>(即它删除了implicit修饰符)。我最初认为由于某种原因它被copy方法删除了,但我添加了一些println,事实并非如此:implicit修饰符存在于副本中,但只是没有包含在toString输出中。有人知道这里发生了什么吗?以及如何将implicit包含在输出中?

printlns:

println("***********ORIGINAL***********")
println("toString:t" + arg.toString())
println("name:tt" + arg.name)
println("modifiers:t" + arg.mods)
println("syntax:tt" + arg.syntax)
println("structure:t" + arg.structure)
println("***********COPY***********")
val copy = arg.copy(name = Term.Name("components"))
println("toString:t" + copy.toString())
println("name:tt" + copy.name)
println("modifiers:t" + copy.mods)
println("syntax:tt" + copy.syntax)
println("structure:t" + copy.structure)

输出:

***********ORIGINAL***********
toString:   implicit app
name:       app
modifiers:  List(implicit)
syntax:     implicit app
structure:  Term.Param(List(Mod.Implicit), Term.Name("app"), None, None)
***********COPY***********
toString:   components
name:       components
modifiers:  List(implicit)
syntax:     components
structure:  Term.Param(List(Mod.Implicit), Term.Name("components"), None, None)

(请注意,copy在其修饰符列表中具有implicit,但它不会显示在toStringsyntax的输出中)

问题是,当 Scalameta (4.5.13) 打印Term.Param时,它会跳过Mod.ImplicitMod.Using

case t: Term.Param =>
// NOTE: `implicit/using` in parameters is skipped as it applies to whole list
printParam(t, t.mods.filterNot(x => x.is[Mod.Implicit] || x.is[Mod.Using]))

然后它正确打印List[List[Term.Param]]

implicit def syntaxParamss: Syntax[List[List[Term.Param]]] = Syntax { paramss =>
def usingImplicit(params: List[Term.Param]): Show.Result = {
if (params.exists(_.mods.exists(_.is[Mod.Using])))
s("using ", r(params, ", "))
else
w("implicit ", r(params, ", "), params.exists(_.mods.exists(_.is[Mod.Implicit])))
}
r(
paramss.map(params => {
s(
"(",
usingImplicit(params),
")"
)
}),
""
)
}

但这对我们没有帮助。

最简单的解决方法是在必要时添加implicit

doc.tree.collect {
case Term.Apply(func, args) =>
args.collect {
case Term.Block(List(Term.Function(List(arg), _))) =>
val res = arg.copy(name = Term.Name("components"))
val prefix = if (res.mods.exists(_.is[Mod.Implicit])) "implicit " else ""
Patch.replaceTree(arg, prefix + res.toString)
}.asPatch
}.asPatch

为什么它打印在原件中而不是副本中

因为 Scalameta 打印新解析的树和转换/生成的树的方式不同。对于前者,它保留其原始字符串表示形式和原始格式。对于后者,它使用相应的scala.meta.prettyprinters.Show实例打印它们,即跳过参数等的implicit

arg.toString打电话给scala.meta.internal.prettyprinters.TreeSyntax.apply[Term.Param](Scala213).apply(arg).

TreeSyntax.apply方法是

def apply[T <: Tree](dialect: Dialect): Syntax[T] = {
// NOTE: This is the current state of the art of smart prettyprinting.
// If we prettyprint a tree that's just been parsed with the same dialect,
// then we retain formatting. Otherwise, we don't, even in the tiniest.
// I expect to improve on this in the nearest future, because we had it much better until recently.
Syntax { (x: T) =>
x.origin match {
// NOTE: Options don't really matter,
// because if we've parsed a tree, it's not gonna contain lazy seqs anyway.
// case Origin.Parsed(_, originalDialect, _) if dialect == originalDialect && options == Options.Eager =>
case o @ Origin.Parsed(_, `dialect`, _) => s(o.position.text)
case _ => reprint(x)(dialect)
}
}
}

Origin.Parsed(新解析的树的原点)的模式匹配中,该方法返回Result.Str,对于Origin.None(转换/生成的树的原点),它返回Result.Sequence

println(arg) // implicit y: Boolean
println(arg.structure) // Term.Param(List(Mod.Implicit), Term.Name("y"), Some(Type.Name("Boolean")), None)
println(arg.getClass) // class scala.meta.Term$Param$TermParamImpl
println(arg.origin) // Parsed(Input.VirtualFile("fix/Scalafixdemo.scala", "... implicit y: Boolean => ..."),Scala213,TokenStreamPosition(45,51))
println(TreeSyntax.apply[Term.Param](Scala213).apply(arg).getClass) 
// class scala.meta.prettyprinters.Show$Str
val res = arg.copy(name = Term.Name("components"))
println(res) // components: Boolean
println(res.structure) // Term.Param(List(Mod.Implicit), Term.Name("components"), Some(Type.Name("Boolean")), None)
println(res.getClass) // class scala.meta.Term$Param$TermParamImpl
println(res.origin) // None
println(TreeSyntax.apply[Term.Param](Scala213).apply(res).getClass) 
// class scala.meta.prettyprinters.Show$Sequence

scala.meta.internal.trees.InternalTree#origin的方法private[meta],所以如果你玩它,把你的规则放到包scala.meta

Term.Param不是事例类,.copy也不是事例类的方法。argres实际上是宏生成的类Term.Param.TermParamImpl的实例。

相关内容

  • 没有找到相关文章

最新更新