mirror of
https://github.com/sbt/sbt.git
synced 2026-08-22 14:17:13 +02:00
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:
@@ -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] 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
|
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)
|
deps.foreach(addDependency)
|
||||||
case Template(parents, self, body) =>
|
case Template(parents, self, body) =>
|
||||||
traverseTrees(body)
|
traverseTrees(body)
|
||||||
|
case MacroExpansionOf(original) =>
|
||||||
|
this.traverse(original)
|
||||||
case other => ()
|
case other => ()
|
||||||
}
|
}
|
||||||
super.traverse(tree)
|
super.traverse(tree)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import scala.tools.nsc._
|
|||||||
* The tree walking algorithm walks into TypeTree.original explicitly.
|
* 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._
|
import global._
|
||||||
|
|
||||||
def extract(unit: CompilationUnit): Set[String] = {
|
def extract(unit: CompilationUnit): Set[String] = {
|
||||||
@@ -53,30 +53,44 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) {
|
|||||||
val symbolNameAsString = symbol.name.decode.trim
|
val symbolNameAsString = symbol.name.decode.trim
|
||||||
namesBuffer += symbolNameAsString
|
namesBuffer += symbolNameAsString
|
||||||
}
|
}
|
||||||
def handleTreeNode(node: Tree): Unit = node match {
|
|
||||||
case _: DefTree | _: Template => ()
|
def handleTreeNode(node: Tree): Unit = {
|
||||||
// turns out that Import node has a TermSymbol associated with it
|
def handleMacroExpansion(original: Tree): Unit = original.foreach(handleTreeNode)
|
||||||
// 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
|
def handleClassicTreeNode(node: Tree): Unit = node match {
|
||||||
// import node altogether and just process the selectors in the import node
|
case _: DefTree | _: Template => ()
|
||||||
case Import(_, selectors: List[ImportSelector]) =>
|
// turns out that Import node has a TermSymbol associated with it
|
||||||
def usedNameInImportSelector(name: Name): Unit =
|
// I (Grzegorz) tried to understand why it's there and what does it represent but
|
||||||
if ((name != null) && (name != nme.WILDCARD)) namesBuffer += name.toString
|
// that logic was introduced in 2005 without any justification I'll just ignore the
|
||||||
selectors foreach { selector =>
|
// import node altogether and just process the selectors in the import node
|
||||||
usedNameInImportSelector(selector.name)
|
case Import(_, selectors: List[ImportSelector]) =>
|
||||||
usedNameInImportSelector(selector.rename)
|
def usedNameInImportSelector(name: Name): Unit =
|
||||||
}
|
if ((name != null) && (name != nme.WILDCARD)) namesBuffer += name.toString
|
||||||
// TODO: figure out whether we should process the original tree or walk the type
|
selectors foreach { selector =>
|
||||||
// the argument for processing the original tree: we process what user wrote
|
usedNameInImportSelector(selector.name)
|
||||||
// the argument for processing the type: we catch all transformations that typer applies
|
usedNameInImportSelector(selector.rename)
|
||||||
// to types but that might be a bad thing because it might expand aliases eagerly which
|
}
|
||||||
// not what we need
|
// TODO: figure out whether we should process the original tree or walk the type
|
||||||
case t: TypeTree if t.original != null =>
|
// the argument for processing the original tree: we process what user wrote
|
||||||
t.original.foreach(handleTreeNode)
|
// the argument for processing the type: we catch all transformations that typer applies
|
||||||
case t if t.hasSymbol && eligibleAsUsedName(t.symbol) =>
|
// to types but that might be a bad thing because it might expand aliases eagerly which
|
||||||
addSymbol(t.symbol)
|
// not what we need
|
||||||
case _ => ()
|
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)
|
tree.foreach(handleTreeNode)
|
||||||
namesBuffer.toSet
|
namesBuffer.toSet
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user