/* 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 /** For sbt compatibility (https://github.com/scala/scala/pull/4588) */ 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, since: String): Unit = currentRun.reporting.deprecationWarning(pos, msg, since) @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() } }