mirror of https://github.com/sbt/sbt.git
Give SourcePosition a macro instance creator
This commit is contained in:
parent
2022713ff5
commit
ada2a8aafa
14
build.sbt
14
build.sbt
|
|
@ -94,11 +94,15 @@ lazy val utilControl = (project in internalPath / "util-control").settings(
|
|||
mimaSettings,
|
||||
)
|
||||
|
||||
val utilPosition = (project in file("internal") / "util-position").settings(
|
||||
commonSettings,
|
||||
name := "Util Position",
|
||||
mimaSettings,
|
||||
)
|
||||
val utilPosition = (project in file("internal") / "util-position")
|
||||
.dependsOn(utilTesting % Test)
|
||||
.settings(
|
||||
commonSettings,
|
||||
name := "Util Position",
|
||||
scalacOptions += "-language:experimental.macros",
|
||||
libraryDependencies += scalaReflect.value,
|
||||
mimaSettings,
|
||||
)
|
||||
|
||||
// logging
|
||||
lazy val utilLogging = (project in internalPath / "util-logging")
|
||||
|
|
|
|||
|
|
@ -18,3 +18,49 @@ final case class LineRange(start: Int, end: Int) {
|
|||
final case class RangePosition(path: String, range: LineRange) extends FilePosition {
|
||||
def startLine = range.start
|
||||
}
|
||||
|
||||
object SourcePosition {
|
||||
|
||||
/** Creates a SourcePosition by using the enclosing position of the invocation of this method.
|
||||
* @see [[scala.reflect.macros.Enclosures#enclosingPosition]]
|
||||
* @return SourcePosition
|
||||
*/
|
||||
def fromEnclosing(): SourcePosition = macro SourcePositionMacro.fromEnclosingImpl
|
||||
|
||||
}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.reflect.macros.blackbox
|
||||
import scala.reflect.internal.util.UndefinedPosition
|
||||
|
||||
final class SourcePositionMacro(val c: blackbox.Context) {
|
||||
import c.universe.{ NoPosition => _, _ }
|
||||
|
||||
def fromEnclosingImpl(): Expr[SourcePosition] = {
|
||||
val pos = c.enclosingPosition
|
||||
if (!pos.isInstanceOf[UndefinedPosition] && pos.line >= 0 && pos.source != null) {
|
||||
val f = pos.source.file
|
||||
val name = constant[String](ownerSource(f.path, f.name))
|
||||
val line = constant[Int](pos.line)
|
||||
reify { LinePosition(name.splice, line.splice) }
|
||||
} else
|
||||
reify { NoPosition }
|
||||
}
|
||||
|
||||
private[this] def ownerSource(path: String, name: String): String = {
|
||||
@tailrec def inEmptyPackage(s: Symbol): Boolean =
|
||||
s != NoSymbol && (
|
||||
s.owner == c.mirror.EmptyPackage
|
||||
|| s.owner == c.mirror.EmptyPackageClass
|
||||
|| inEmptyPackage(s.owner)
|
||||
)
|
||||
|
||||
c.internal.enclosingOwner match {
|
||||
case ec if !ec.isStatic => name
|
||||
case ec if inEmptyPackage(ec) => path
|
||||
case ec => s"(${ec.fullName}) $name"
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def constant[T: WeakTypeTag](t: T): Expr[T] = c.Expr[T](Literal(Constant(t)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package sbt.internal.util
|
||||
|
||||
import org.scalatest._
|
||||
|
||||
class SourcePositionSpec extends FlatSpec {
|
||||
"SourcePosition()" should "return a sane SourcePosition" in {
|
||||
val filename = "SourcePositionSpec.scala"
|
||||
val lineNumber = 9
|
||||
SourcePosition.fromEnclosing() match {
|
||||
case LinePosition(path, startLine) => assert(path === filename && startLine === lineNumber)
|
||||
case RangePosition(path, range) => assert(path === filename && inRange(range, lineNumber))
|
||||
case NoPosition => fail("No source position found")
|
||||
}
|
||||
}
|
||||
|
||||
private def inRange(range: LineRange, lineNo: Int) =
|
||||
range.start until range.end contains lineNo
|
||||
}
|
||||
Loading…
Reference in New Issue