package scala.tools.nsc
package typechecker
trait StdAttachments {
self: Analyzer =>
import global._
/** Carries information necessary to expand the host tree.
* At times we need to store this info, because macro expansion can be delayed until its targs are inferred.
* After a macro application has been successfully expanded, this attachment is destroyed.
*/
type UnaffiliatedMacroContext = scala.reflect.macros.contexts.Context
type MacroContext = UnaffiliatedMacroContext { val universe: self.global.type }
case class MacroRuntimeAttachment(delayed: Boolean, typerContext: Context, macroContext: Option[MacroContext])
/** Scratchpad for the macro expander, which is used to store all intermediate data except the details about the runtime.
*/
case class MacroExpanderAttachment(original: Tree, desugared: Tree)
/** Loads underlying MacroExpanderAttachment from a macro expandee or returns a default value for that attachment.
*/
def macroExpanderAttachment(tree: Tree): MacroExpanderAttachment =
tree.attachments.get[MacroExpanderAttachment] getOrElse {
tree match {
case Apply(fn, _) if tree.isInstanceOf[ApplyToImplicitArgs] => macroExpanderAttachment(fn)
case _ => MacroExpanderAttachment(tree, EmptyTree)
}
}
/** After macro expansion is completed, links the expandee and the expansion result
* by annotating them both with a `MacroExpansionAttachment`.
*/
def linkExpandeeAndDesugared(expandee: Tree, desugared: Tree): Unit = {
val metadata = MacroExpanderAttachment(expandee, desugared)
expandee updateAttachment metadata
desugared updateAttachment metadata
}
/** Is added by the macro engine to originals and results of macro expansions.
* Stores the original expandee as it entered the `macroExpand` function.
*/
case class MacroExpansionAttachment(expandee: Tree, expanded: Any)
/** Determines whether the target is either an original or a result of a macro expansion.
* The parameter is of type `Any`, because macros can expand both into trees and into annotations.
*/
def hasMacroExpansionAttachment(any: Any): Boolean = any match {
case tree: Tree => tree.hasAttachment[MacroExpansionAttachment]
case _ => false
}
/** Returns the original tree of the macro expansion if the argument is a macro expansion or EmptyTree otherwise.
*/
def macroExpandee(tree: Tree): Tree = tree.attachments.get[MacroExpansionAttachment].map(_.expandee).getOrElse(EmptyTree)
/** After macro expansion is completed, links the expandee and the expansion result by annotating them both with a `MacroExpansionAttachment`.
* The `expanded` parameter is of type `Any`, because macros can expand both into trees and into annotations.
*/
def linkExpandeeAndExpanded(expandee: Tree, expanded: Any): Unit = {
val metadata = MacroExpansionAttachment(expandee, expanded)
expandee updateAttachment metadata
expanded match {
case expanded: Tree if !expanded.isEmpty => expanded updateAttachment metadata
case _ => // do nothing
}
}
/** When present, suppresses macro expansion for the host.
* This is occasionally necessary, e.g. to prohibit eta-expansion of macros.
*
* Does not affect expandability of child nodes, there's context.withMacrosDisabled for that
* (but think thrice before using that API - see the discussion at https://github.com/scala/scala/pull/1639).
*/
case object SuppressMacroExpansionAttachment
/** Suppresses macro expansion of the tree by putting SuppressMacroExpansionAttachment on it.
*/
def suppressMacroExpansion(tree: Tree) = tree.updateAttachment(SuppressMacroExpansionAttachment)
/** Unsuppresses macro expansion of the tree by removing SuppressMacroExpansionAttachment from it and its children.
*/
def unsuppressMacroExpansion(tree: Tree): Tree = {
tree.removeAttachment[SuppressMacroExpansionAttachment.type]
tree match {
// see the comment to `isMacroExpansionSuppressed` to learn why we need
// a special traversal strategy here
case Apply(fn, _) => unsuppressMacroExpansion(fn)
case TypeApply(fn, _) => unsuppressMacroExpansion(fn)
case _ => // do nothing
}
tree
}
/** Determines whether a tree should not be expanded, because someone has put SuppressMacroExpansionAttachment on it or one of its children.
*/
def isMacroExpansionSuppressed(tree: Tree): Boolean =
( settings.Ymacroexpand.value == settings.MacroExpand.None // SI-6812
|| tree.hasAttachment[SuppressMacroExpansionAttachment.type]
|| (tree match {
// we have to account for the fact that during typechecking an expandee might become wrapped,
// i.e. surrounded by an inferred implicit argument application or by an inferred type argument application.
// in that case the expandee itself will no longer be suppressed and we need to look at the core
case Apply(fn, _) => isMacroExpansionSuppressed(fn)
case TypeApply(fn, _) => isMacroExpansionSuppressed(fn)
case _ => false
})
)
/** After being synthesized by the parser, primary constructors aren't fully baked yet.
* A call to super in such constructors is just a fill-me-in-later dummy resolved later
* by `parentTypes`. This attachment coordinates `parentTypes` and `typedTemplate` and
* allows them to complete the synthesis.
*/
case class SuperArgsAttachment(argss: List[List[Tree]])
/** Convenience method for `SuperArgsAttachment`.
* Compared with `MacroRuntimeAttachment` this attachment has different a usage pattern,
* so it really benefits from a dedicated extractor.
*/
def superArgs(tree: Tree): Option[List[List[Tree]]] =
tree.attachments.get[SuperArgsAttachment] collect { case SuperArgsAttachment(argss) => argss }
/** Determines whether the given tree has an associated SuperArgsAttachment.
*/
def hasSuperArgs(tree: Tree): Boolean = superArgs(tree).nonEmpty
/** @see markMacroImplRef
*/
case object MacroImplRefAttachment
/** Marks the tree as a macro impl reference, which is a naked reference to a method.
*
* This is necessary for typechecking macro impl references (see `DefaultMacroCompiler.defaultResolveMacroImpl`),
* because otherwise typing a naked reference will result in the "follow this method with `_` if you want to
* treat it as a partially applied function" errors.
*
* This mark suppresses adapt except for when the annottee is a macro application.
*/
def markMacroImplRef(tree: Tree): Tree = tree.updateAttachment(MacroImplRefAttachment)
/** Unmarks the tree as a macro impl reference (see `markMacroImplRef` for more information).
*
* This is necessary when a tree that was previously deemed to be a macro impl reference,
* typechecks to be a macro application. Then we need to unmark it, expand it and try to treat
* its expansion as a macro impl reference.
*/
def unmarkMacroImplRef(tree: Tree): Tree = tree.removeAttachment[MacroImplRefAttachment.type]
/** Determines whether a tree should or should not be adapted,
* because someone has put MacroImplRefAttachment on it.
*/
def isMacroImplRef(tree: Tree): Boolean = tree.hasAttachment[MacroImplRefAttachment.type]
/** Since mkInvoke, the applyDynamic/selectDynamic/etc desugarer, is disconnected
* from typedNamedApply, the applyDynamicNamed argument rewriter, the latter
* doesn’t know whether it needs to apply the rewriting because the application
* has just been desugared or it needs to hold on because it’s already performed
* a desugaring on this tree. This has led to SI-8006.
*
* This attachment solves the problem by providing a means of communication
* between the two Dynamic desugarers, which solves the aforementioned issue.
*/
case object DynamicRewriteAttachment
def markDynamicRewrite(tree: Tree): Tree = tree.updateAttachment(DynamicRewriteAttachment)
def unmarkDynamicRewrite(tree: Tree): Tree = tree.removeAttachment[DynamicRewriteAttachment.type]
def isDynamicRewrite(tree: Tree): Boolean = tree.attachments.get[DynamicRewriteAttachment.type].isDefined
}