如何将 Scala 代码块转换为字符串?

How to convert scala code block to string?

我需要实现一个测试函数来检查“splain”插件的编译时错误信息,该函数的一部分需要将代码块转换为字符串,例如:

def convert(fn: => Unit): String

// for testing

val code = convert {
  object I extends Seq {}
}

assert(code == "object I extends Seq {}")

是否可以使用标准 Scala 功能?非常感谢您的建议。

此功能将能够验证复杂代码的编译时消息,这些代码需要 IDE 经常

进行索引和重构

,有可能。

李昊一的微距Text from sourcecode

def text[T: c.WeakTypeTag](c: Compat.Context)(v: c.Expr[T]): c.Expr[sourcecode.Text[T]] = {
  import c.universe._
  val fileContent = new String(v.tree.pos.source.content)
  val start = v.tree.collect {
    case treeVal => treeVal.pos match {
      case NoPosition ⇒ Int.MaxValue
      case p ⇒ p.startOrPoint
    }
  }.min
  val g = c.asInstanceOf[reflect.macros.runtime.Context].global
  val parser = g.newUnitParser(fileContent.drop(start))
  parser.expr()
  val end = parser.in.lastOffset
  val txt = fileContent.slice(start, start + end)
  val tree = q"""${c.prefix}(${v.tree}, $txt)"""
  c.Expr[sourcecode.Text[T]](tree)
}

几乎可以满足您的要求:

def convert[A](fn: => Text[A]): String = fn.source

convert(10 + 20 +
  30
)

//10 + 20 +
//  30

不幸的是,

if you have multiple statements in a {} block, sourcecode.Text will only capture the source code for the last expression that gets returned.

而且由于 { object I extends Seq {} } 实际上是 { object I extends Seq {}; () } 宏在这种情况下将不起作用。

所以让我们编写自己的简单宏

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

def convert(fn: => Any): String = macro convertImpl

def convertImpl(c: blackbox.Context)(fn: c.Tree): c.Tree = {
  import c.universe._

  val pos = fn.pos
  val res = new String(pos.source.content).slice(pos.start, pos.end)

  Literal(Constant(res))
}

用法:

trait Seq

convert {
  val i: Int = 1
  object I extends Seq {}
  10 + 20 + 30
  convert(1)
}

//{
//    val i: Int = 1
//    object I extends Seq {}
//    10 + 20 + 30
//    convert(1)
//  }

注意 def 宏的参数在宏扩展之前进行了类型检查(因此 convert { val i: Int = "a" }convert { object I extends XXX } 没有定义 XXXconvert { (; }等将无法编译)。