summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala
blob: dc19ea6f181e18e6e7dae46581acebac75d28d40 (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
/* NSC -- new Scala compiler
 * Copyright 2007-2009 LAMP/EPFL
 * @author  Martin Odersky
 */
// $Id$

package scala.tools.nsc.symtab

import scala.tools.nsc.transform.Reifiers
import util._

/** AnnotationInfo and its helpers */
trait AnnotationInfos {
  self: SymbolTable =>

  /** Convert a tree to a Constant, if possible */
  private def tree2cons(tree: Tree): Option[Constant] =
    tree match {
      case Literal(v) => Some(v)

      case Apply(
        TypeApply(
          meth@Select(_,_),
	  List(elemType)),
	members)
      if (definitions.ArrayModule_apply.alternatives contains meth.symbol) =>
	trees2consArray(members, tree.tpe)


      case Apply(meth, members)
      if (definitions.ArrayModule_apply.alternatives contains meth.symbol) =>
 	trees2consArray(members, tree.tpe)

      case Typed(t, _) => tree2cons(t)

      case tree =>
        //println("could not convert: " + tree);
        None
    }

  private def trees2consArray(trees: Seq[Tree], arrayType:Type)
  : Option[Constant] =
  {
    val mems = trees.map(tree2cons)

    if (mems.exists(_.isEmpty))
      None
    else
      Some(new ArrayConstant(
	mems.map(_.get).toArray,
	arrayType))
  }


  /** An argument to an annotation.  It includes a parse tree,
   *  and it includes a compile-time constant for the tree if possible.
   */
  class AnnotationArgument(val intTree: Tree) {
    def this(cons: Constant) = this(
      Literal(cons).setType(cons.tpe))


    @deprecated
    lazy val tree = {
      object reifiers extends {
	val symbols: AnnotationInfos.this.type = AnnotationInfos.this
      } with Reifiers

      reifiers.reify(intTree)
    }

    val constant: Option[Constant] = tree2cons(intTree)

    def isConstant = !constant.isEmpty

    override def toString: String =
      constant match {
        case Some(cons) => cons.escapedStringValue
        case None => intTree.toString
      }
  }

  /** Typed information about an annotation.  It can be attached to
   *  either a symbol or an annotated type.
   */
  case class AnnotationInfo(
    atp: Type,
    args: List[AnnotationArgument],
    assocs: List[(Name, AnnotationArgument)])
  {
    override def toString: String =
      atp +
      (if (args.isEmpty) ""
       else args.mkString("(", ", ", ")")) +
      (if (assocs.isEmpty) ""
       else (assocs map { case (x, y) => x+" = "+y } mkString ("{", ", ", "}")))

    /** Check whether all arguments and assocations are constants */
    def isConstant =
      ((args forall (_.isConstant)) &&
       (assocs map (_._2) forall (_.isConstant)))

    /** Check whether the type or any of the arguments are erroneous */
    def isErroneous = atp.isErroneous || args.exists(_.intTree.isErroneous)

    /** Check whether any of the arguments mention a symbol */
    def refsSymbol(sym: Symbol) =
      args.exists(_.intTree.exists(_.symbol == sym))

    /** Change all ident's with Symbol "from" to instead use symbol "to" */
    def substIdentSyms(from: Symbol, to: Symbol) = {
      val subs = new TreeSymSubstituter(List(from), List(to))
      AnnotationInfo(atp,
		     args.map(arg => new AnnotationArgument(subs(arg.intTree))),
		     assocs)
    }
  }
}