summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/CompilationUnits.scala
blob: 6be1fda1b512a22e18068077c500656158229238 (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
/* NSC -- new Scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.tools.nsc

import scala.reflect.internal.util.{ SourceFile, NoSourceFile, FreshNameCreator }
import scala.collection.mutable
import scala.collection.mutable.{ LinkedHashSet, ListBuffer }

trait CompilationUnits { global: Global =>

  /** An object representing a missing compilation unit.
   */
  object NoCompilationUnit extends CompilationUnit(NoSourceFile) {
    override lazy val isJava = false
    override def exists = false
    override def toString() = "NoCompilationUnit"
  }

  /** One unit of compilation that has been submitted to the compiler.
    * It typically corresponds to a single file of source code.  It includes
    * error-reporting hooks.  */
  class CompilationUnit(val source: SourceFile) extends CompilationUnitContextApi { self =>

    /** the fresh name creator */
    implicit val fresh: FreshNameCreator                           = new FreshNameCreator
    def freshTermName(prefix: String = nme.FRESH_TERM_NAME_PREFIX) = global.freshTermName(prefix)
    def freshTypeName(prefix: String)                              = global.freshTypeName(prefix)

    /** the content of the compilation unit in tree form */
    var body: Tree = EmptyTree

    /** The position of the first xml literal encountered while parsing this compilation unit.
     * NoPosition if there were none. Write-once.
     */
    private[this] var _firstXmlPos: Position = NoPosition

    /** Record that we encountered XML. Should only be called once. */
    protected[nsc] def encounteredXml(pos: Position) = _firstXmlPos = pos

    /** Does this unit contain XML? */
    def hasXml = _firstXmlPos ne NoPosition

    /** Position of first XML literal in this unit. */
    def firstXmlPos = _firstXmlPos

    def exists = source != NoSourceFile && source != null

    /** Note: depends now contains toplevel classes.
     *  To get their sourcefiles, you need to dereference with .sourcefile
     */
    private[this] val _depends = mutable.HashSet[Symbol]()
    // SBT compatibility (SI-6875)
    //
    // imagine we have a file named A.scala, which defines a trait named Foo and a module named Main
    // Main contains a call to a macro, which calls compileLate to define a mock for Foo
    // compileLate creates a virtual file Virt35af32.scala, which contains a class named FooMock extending Foo,
    // and macro expansion instantiates FooMock. the stage is now set. let's see what happens next.
    //
    // without this workaround in scalac or without being patched itself, sbt will think that
    // * Virt35af32 depends on A (because it extends Foo from A)
    // * A depends on Virt35af32 (because it contains a macro expansion referring to FooMock from Virt35af32)
    //
    // after compiling A.scala, SBT will notice that it has a new source file named Virt35af32.
    // it will also think that this file hasn't yet been compiled and since A depends on it
    // it will think that A needs to be recompiled.
    //
    // recompilation will lead to another macro expansion. that another macro expansion might choose to create a fresh mock,
    // producing another virtual file, say, Virtee509a, which will again trick SBT into thinking that A needs a recompile,
    // which will lead to another macro expansion, which will produce another virtual file and so on
    def depends = if (exists && !source.file.isVirtual) _depends else mutable.HashSet[Symbol]()

    /** so we can relink
     */
    private[this] val _defined = mutable.HashSet[Symbol]()
    def defined = if (exists && !source.file.isVirtual) _defined else mutable.HashSet[Symbol]()

    /** Synthetic definitions generated by namer, eliminated by typer.
     */
    object synthetics {
      private val map = mutable.HashMap[Symbol, Tree]()
      def update(sym: Symbol, tree: Tree) {
        debuglog(s"adding synthetic ($sym, $tree) to $self")
        map.update(sym, tree)
      }
      def -=(sym: Symbol) {
        debuglog(s"removing synthetic $sym from $self")
        map -= sym
      }
      def get(sym: Symbol): Option[Tree] = debuglogResultIf[Option[Tree]](s"found synthetic for $sym in $self", _.isDefined) {
        map get sym
      }
      def keys: Iterable[Symbol] = map.keys
      def clear(): Unit = map.clear()
      override def toString = map.toString
    }

    // namer calls typer.computeType(rhs) on DefDef / ValDef when tpt is empty. the result
    // is cached here and re-used in typedDefDef / typedValDef
    // Also used to cache imports type-checked by namer.
    val transformed = new mutable.AnyRefMap[Tree, Tree]

    /** things to check at end of compilation unit */
    val toCheck = new ListBuffer[() => Unit]

    /** The features that were already checked for this unit */
    var checkedFeatures = Set[Symbol]()

    def position(pos: Int) = source.position(pos)

    /** The position of a targeted type check
     *  If this is different from NoPosition, the type checking
     *  will stop once a tree that contains this position range
     *  is fully attributed.
     */
    def targetPos: Position = NoPosition

    /** The icode representation of classes in this compilation unit.
     *  It is empty up to phase 'icode'.
     */
    val icode: LinkedHashSet[icodes.IClass] = new LinkedHashSet

    @deprecated("Call global.reporter.echo directly instead.", "2.11.2")
    final def echo(pos: Position, msg: String): Unit    = reporter.echo(pos, msg)
    @deprecated("Call global.reporter.error (or typer.context.error) directly instead.", "2.11.2")
    final def error(pos: Position, msg: String): Unit   = reporter.error(pos, msg)
    @deprecated("Call global.reporter.warning (or typer.context.warning) directly instead.", "2.11.2")
    final def warning(pos: Position, msg: String): Unit = reporter.warning(pos, msg)

    @deprecated("Call global.currentRun.reporting.deprecationWarning directly instead.", "2.11.2")
    final def deprecationWarning(pos: Position, msg: String): Unit = currentRun.reporting.deprecationWarning(pos, msg)
    @deprecated("Call global.currentRun.reporting.uncheckedWarning directly instead.", "2.11.2")
    final def uncheckedWarning(pos: Position, msg: String): Unit   = currentRun.reporting.uncheckedWarning(pos, msg)

    @deprecated("This method will be removed. It does nothing.", "2.11.2")
    final def comment(pos: Position, msg: String): Unit = {}

    /** Is this about a .java source file? */
    lazy val isJava = source.file.name.endsWith(".java")

    override def toString() = source.toString()
  }
}