Give SourcePosition a macro instance creator

This commit is contained in:
Dale Wijnand 2018-01-29 11:07:13 +00:00
parent 2022713ff5
commit ada2a8aafa
No known key found for this signature in database
GPG Key ID: 4F256E3D151DF5EF
3 changed files with 73 additions and 5 deletions

View File

@ -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")

View File

@ -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)))
}

View File

@ -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
}