mirror of https://github.com/sbt/sbt.git
Record dependencies on macro arguments
Macros take arguments as trees and return some other trees; both of them have dependencies but we see trees only after expansion and recorded only those dependencies. This commit solves this problem by looking into the attachments of the trees that are supposed to contain originals of macro expansions and recording dependencies of the macro before its expansion.
This commit is contained in:
parent
2a98355c64
commit
70fecfe767
|
|
@ -91,4 +91,42 @@ abstract class Compat
|
|||
private[this] def sourceCompatibilityOnly: Nothing = throw new RuntimeException("For source compatibility only: should not get here.")
|
||||
|
||||
private[this] final implicit def miscCompat(n: AnyRef): MiscCompat = new MiscCompat
|
||||
|
||||
object MacroExpansionOf {
|
||||
def unapply(tree: Tree): Option[Tree] = {
|
||||
|
||||
// MacroExpansionAttachment (MEA) compatibility for 2.8.x and 2.9.x
|
||||
object Compat {
|
||||
class MacroExpansionAttachment(val original: Tree)
|
||||
|
||||
// Trees have no attachments in 2.8.x and 2.9.x
|
||||
implicit def withAttachments(tree: Tree): WithAttachments = new WithAttachments(tree)
|
||||
class WithAttachments(val tree: Tree) {
|
||||
object EmptyAttachments {
|
||||
def all = Set.empty[Any]
|
||||
}
|
||||
val attachments = EmptyAttachments
|
||||
}
|
||||
}
|
||||
import Compat._
|
||||
|
||||
locally {
|
||||
// Wildcard imports are necessary since 2.8.x and 2.9.x don't have `MacroExpansionAttachment` at all
|
||||
import global._ // this is where MEA lives in 2.10.x
|
||||
|
||||
// `original` has been renamed to `expandee` in 2.11.x
|
||||
implicit def withExpandee(att: MacroExpansionAttachment): WithExpandee = new WithExpandee(att)
|
||||
class WithExpandee(att: MacroExpansionAttachment) {
|
||||
def expandee: Tree = att.original
|
||||
}
|
||||
|
||||
locally {
|
||||
import analyzer._ // this is where MEA lives in 2.11.x
|
||||
tree.attachments.all.collect {
|
||||
case att: MacroExpansionAttachment => att.expandee
|
||||
} headOption
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile
|
|||
deps.foreach(addDependency)
|
||||
case Template(parents, self, body) =>
|
||||
traverseTrees(body)
|
||||
case MacroExpansionOf(original) =>
|
||||
this.traverse(original)
|
||||
case other => ()
|
||||
}
|
||||
super.traverse(tree)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import scala.tools.nsc._
|
|||
* The tree walking algorithm walks into TypeTree.original explicitly.
|
||||
*
|
||||
*/
|
||||
class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) {
|
||||
class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) extends Compat {
|
||||
import global._
|
||||
|
||||
def extract(unit: CompilationUnit): Set[String] = {
|
||||
|
|
@ -53,30 +53,44 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) {
|
|||
val symbolNameAsString = symbol.name.decode.trim
|
||||
namesBuffer += symbolNameAsString
|
||||
}
|
||||
def handleTreeNode(node: Tree): Unit = node match {
|
||||
case _: DefTree | _: Template => ()
|
||||
// turns out that Import node has a TermSymbol associated with it
|
||||
// I (Grzegorz) tried to understand why it's there and what does it represent but
|
||||
// that logic was introduced in 2005 without any justification I'll just ignore the
|
||||
// import node altogether and just process the selectors in the import node
|
||||
case Import(_, selectors: List[ImportSelector]) =>
|
||||
def usedNameInImportSelector(name: Name): Unit =
|
||||
if ((name != null) && (name != nme.WILDCARD)) namesBuffer += name.toString
|
||||
selectors foreach { selector =>
|
||||
usedNameInImportSelector(selector.name)
|
||||
usedNameInImportSelector(selector.rename)
|
||||
}
|
||||
// TODO: figure out whether we should process the original tree or walk the type
|
||||
// the argument for processing the original tree: we process what user wrote
|
||||
// the argument for processing the type: we catch all transformations that typer applies
|
||||
// to types but that might be a bad thing because it might expand aliases eagerly which
|
||||
// not what we need
|
||||
case t: TypeTree if t.original != null =>
|
||||
t.original.foreach(handleTreeNode)
|
||||
case t if t.hasSymbol && eligibleAsUsedName(t.symbol) =>
|
||||
addSymbol(t.symbol)
|
||||
case _ => ()
|
||||
|
||||
def handleTreeNode(node: Tree): Unit = {
|
||||
def handleMacroExpansion(original: Tree): Unit = original.foreach(handleTreeNode)
|
||||
|
||||
def handleClassicTreeNode(node: Tree): Unit = node match {
|
||||
case _: DefTree | _: Template => ()
|
||||
// turns out that Import node has a TermSymbol associated with it
|
||||
// I (Grzegorz) tried to understand why it's there and what does it represent but
|
||||
// that logic was introduced in 2005 without any justification I'll just ignore the
|
||||
// import node altogether and just process the selectors in the import node
|
||||
case Import(_, selectors: List[ImportSelector]) =>
|
||||
def usedNameInImportSelector(name: Name): Unit =
|
||||
if ((name != null) && (name != nme.WILDCARD)) namesBuffer += name.toString
|
||||
selectors foreach { selector =>
|
||||
usedNameInImportSelector(selector.name)
|
||||
usedNameInImportSelector(selector.rename)
|
||||
}
|
||||
// TODO: figure out whether we should process the original tree or walk the type
|
||||
// the argument for processing the original tree: we process what user wrote
|
||||
// the argument for processing the type: we catch all transformations that typer applies
|
||||
// to types but that might be a bad thing because it might expand aliases eagerly which
|
||||
// not what we need
|
||||
case t: TypeTree if t.original != null =>
|
||||
t.original.foreach(handleTreeNode)
|
||||
case t if t.hasSymbol && eligibleAsUsedName(t.symbol) =>
|
||||
addSymbol(t.symbol)
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
node match {
|
||||
case MacroExpansionOf(original) =>
|
||||
handleClassicTreeNode(node)
|
||||
handleMacroExpansion(original)
|
||||
case _ =>
|
||||
handleClassicTreeNode(node)
|
||||
}
|
||||
}
|
||||
|
||||
tree.foreach(handleTreeNode)
|
||||
namesBuffer.toSet
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue