aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/Annotations.scala
blob: 0e8e5a1f0dabed0fe8906261d35549d9b825edb4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package dotty.tools.dotc
package core

import Symbols._, Types._, util.Positions._, Contexts._, Constants._, ast.tpd._
import config.ScalaVersion
import StdNames._
import dotty.tools.dotc.ast.{tpd, untpd}

object Annotations {

  abstract class Annotation {
    def tree(implicit ctx: Context): Tree
    def symbol(implicit ctx: Context): Symbol =
      if (tree.symbol.isConstructor) tree.symbol.owner
      else tree.tpe.typeSymbol
    def matches(cls: Symbol)(implicit ctx: Context): Boolean = symbol.derivesFrom(cls)
    def appliesToModule: Boolean = true // for now; see remark in SymDenotations

    def derivedAnnotation(tree: Tree)(implicit ctx: Context) =
      if (tree eq this.tree) this else Annotation(tree)

    def arguments(implicit ctx: Context) = ast.tpd.arguments(tree)
    def argument(i: Int)(implicit ctx: Context): Option[Tree] = {
      val args = arguments
      if (i < args.length) Some(args(i)) else None
    }
    def argumentConstant(i: Int)(implicit ctx: Context): Option[Constant] =
      for (ConstantType(c) <- argument(i) map (_.tpe)) yield c

    def ensureCompleted(implicit ctx: Context): Unit = tree
  }

  case class ConcreteAnnotation(t: Tree) extends Annotation {
    def tree(implicit ctx: Context): Tree = t
  }

  abstract case class LazyAnnotation(sym: Symbol) extends Annotation {
    private var myTree: Tree = null
    def tree(implicit ctx: Context) = {
      if (myTree == null) myTree = complete(ctx)
      myTree
    }
    def complete(implicit ctx: Context): Tree
    override def symbol(implicit ctx: Context): Symbol = sym
  }

  /** An annotation indicating the body of a right-hand side,
   *  typically of an inline method. Treated specially in
   *  pickling/unpickling and TypeTreeMaps
   */
  abstract class BodyAnnotation extends Annotation {
    override def symbol(implicit ctx: Context) = defn.BodyAnnot
    override def derivedAnnotation(tree: Tree)(implicit ctx: Context) =
      if (tree eq this.tree) this else ConcreteBodyAnnotation(tree)
    override def arguments(implicit ctx: Context) = Nil
    override def ensureCompleted(implicit ctx: Context) = ()
  }

  case class ConcreteBodyAnnotation(body: Tree) extends BodyAnnotation {
    def tree(implicit ctx: Context) = body
  }

  case class LazyBodyAnnotation(bodyExpr: Context => Tree) extends BodyAnnotation {
    private var evaluated = false
    private var myBody: Tree = _
    def tree(implicit ctx: Context) = {
      if (evaluated) assert(myBody != null)
      else {
        evaluated = true
        myBody = bodyExpr(ctx)
      }
      myBody
    }
    def isEvaluated = evaluated
  }

  object Annotation {

    def apply(tree: Tree) = ConcreteAnnotation(tree)

    def apply(cls: ClassSymbol)(implicit ctx: Context): Annotation =
      apply(cls, Nil)

    def apply(cls: ClassSymbol, arg: Tree)(implicit ctx: Context): Annotation =
      apply(cls, arg :: Nil)

    def apply(cls: ClassSymbol, arg1: Tree, arg2: Tree)(implicit ctx: Context): Annotation =
      apply(cls, arg1 :: arg2 :: Nil)

    def apply(cls: ClassSymbol, args: List[Tree])(implicit ctx: Context): Annotation =
      apply(cls.typeRef, args)

    def apply(atp: Type, arg: Tree)(implicit ctx: Context): Annotation =
      apply(atp, arg :: Nil)

    def apply(atp: Type, arg1: Tree, arg2: Tree)(implicit ctx: Context): Annotation =
      apply(atp, arg1 :: arg2 :: Nil)

    def apply(atp: Type, args: List[Tree])(implicit ctx: Context): Annotation =
      apply(New(atp, args))

    private def resolveConstructor(atp: Type, args:List[Tree])(implicit ctx: Context): Tree = {
      val targs = atp.argTypes
      tpd.applyOverloaded(New(atp withoutArgs targs), nme.CONSTRUCTOR, args, targs, atp, isAnnotConstructor = true)
    }

    def applyResolve(atp: Type, args: List[Tree])(implicit ctx: Context): Annotation = {
      apply(resolveConstructor(atp, args))
    }

    def deferred(sym: Symbol, treeFn: Context => Tree)(implicit ctx: Context): Annotation =
      new LazyAnnotation(sym) {
        def complete(implicit ctx: Context) = treeFn(ctx)
      }

    def deferred(atp: Type, args: List[Tree])(implicit ctx: Context): Annotation =
      deferred(atp.classSymbol, implicit ctx => New(atp, args))

    def deferredResolve(atp: Type, args: List[Tree])(implicit ctx: Context): Annotation =
      deferred(atp.classSymbol, implicit ctx => resolveConstructor(atp, args))

    def makeAlias(sym: TermSymbol)(implicit ctx: Context) =
      apply(defn.AliasAnnot, List(
        ref(TermRef.withSigAndDenot(sym.owner.thisType, sym.name, sym.signature, sym))))

    def makeChild(sym: Symbol)(implicit ctx: Context) =
      deferred(defn.ChildAnnot,
        implicit ctx => New(defn.ChildAnnotType.appliedTo(sym.owner.thisType.select(sym.name, sym)), Nil))

    def makeSourceFile(path: String)(implicit ctx: Context) =
      apply(defn.SourceFileAnnot, Literal(Constant(path)))
  }

  def ThrowsAnnotation(cls: ClassSymbol)(implicit ctx: Context) = {
    val tref = cls.typeRef
    Annotation(defn.ThrowsAnnotType.appliedTo(tref), Ident(tref))
  }

  /** A decorator that provides queries for specific annotations
   *  of a symbol.
   */
  implicit class AnnotInfo(val sym: Symbol) extends AnyVal {

    def isDeprecated(implicit ctx: Context) =
      sym.hasAnnotation(defn.DeprecatedAnnot)

    def deprecationMessage(implicit ctx: Context) =
      for (annot <- sym.getAnnotation(defn.DeprecatedAnnot);
           arg <- annot.argumentConstant(0))
      yield arg.stringValue

    def migrationVersion(implicit ctx: Context) =
      for (annot <- sym.getAnnotation(defn.MigrationAnnot);
           arg <- annot.argumentConstant(1))
      yield ScalaVersion.parse(arg.stringValue)

    def migrationMessage(implicit ctx: Context) =
      for (annot <- sym.getAnnotation(defn.MigrationAnnot);
           arg <- annot.argumentConstant(0))
      yield ScalaVersion.parse(arg.stringValue)
  }
}