/* NSC -- new Scala compiler * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky */ package scala.tools.nsc package typechecker import scala.collection.{ immutable, mutable } import scala.annotation.tailrec import scala.reflect.internal.util.shortClassOfInstance import scala.tools.nsc.reporters.Reporter /** * @author Martin Odersky * @version 1.0 */ trait Contexts { self: Analyzer => import global._ import definitions.{ JavaLangPackage, ScalaPackage, PredefModule, ScalaXmlTopScope, ScalaXmlPackage } import ContextMode._ protected def onTreeCheckerError(pos: Position, msg: String): Unit = () object NoContext extends Context(EmptyTree, NoSymbol, EmptyScope, NoCompilationUnit, // We can't pass the uninitialized `this`. Instead, we treat null specially in `Context#outer` null) { enclClass = this enclMethod = this override val depth = 0 override def nextEnclosing(p: Context => Boolean): Context = this override def enclosingContextChain: List[Context] = Nil override def implicitss: List[List[ImplicitInfo]] = Nil override def imports: List[ImportInfo] = Nil override def firstImport: Option[ImportInfo] = None override def toString = "NoContext" } private object RootImports { // Possible lists of root imports val javaList = JavaLangPackage :: Nil val javaAndScalaList = JavaLangPackage :: ScalaPackage :: Nil val completeList = JavaLangPackage :: ScalaPackage :: PredefModule :: Nil } def ambiguousImports(imp1: ImportInfo, imp2: ImportInfo) = LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") private lazy val startContext = NoContext.make( Template(List(), noSelfType, List()) setSymbol global.NoSymbol setType global.NoType, rootMirror.RootClass, rootMirror.RootClass.info.decls ) private lazy val allUsedSelectors = mutable.Map[ImportInfo, Set[ImportSelector]]() withDefaultValue Set() private lazy val allImportInfos = mutable.Map[CompilationUnit, List[ImportInfo]]() withDefaultValue Nil def warnUnusedImports(unit: CompilationUnit) = if (!unit.isJava) { for (imps <- allImportInfos.remove(unit)) { for (imp <- imps.distinct.reverse) { val used = allUsedSelectors(imp) imp.tree.selectors filterNot (s => isMaskImport(s) || used(s)) foreach { sel => reporter.warning(imp posOf sel, "Unused import") } } allUsedSelectors --= imps } } def isMaskImport(s: ImportSelector): Boolean = s.name != nme.WILDCARD && s.rename == nme.WILDCARD def isIndividualImport(s: ImportSelector): Boolean = s.name != nme.WILDCARD && s.rename != nme.WILDCARD def isWildcardImport(s: ImportSelector): Boolean = s.name == nme.WILDCARD var lastAccessCheckDetails: String = "" /** List of symbols to import from in a root context. Typically that * is `java.lang`, `scala`, and [[scala.Predef]], in that order. Exceptions: * * - if option `-Yno-imports` is given, nothing is imported * - if the unit is java defined, only `java.lang` is imported * - if option `-Yno-predef` is given, if the unit body has an import of Predef * among its leading imports, or if the tree is [[scala.Predef]], `Predef` is not imported. */ protected def rootImports(unit: CompilationUnit): List[Symbol] = { assert(definitions.isDefinitionsInitialized, "definitions uninitialized") if (settings.noimports) Nil else if (unit.isJava) RootImports.javaList else if (settings.nopredef || treeInfo.noPredefImportForUnit(unit.body)) { // SI-8258 Needed for the presentation compiler using -sourcepath, otherwise cycles can occur. See the commit // message for this ticket for an example. debuglog("Omitted import of Predef._ for " + unit) RootImports.javaAndScalaList } else RootImports.completeList } def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, throwing: Boolean = false, checking: Boolean = false): Context = { val rootImportsContext = (startContext /: rootImports(unit))((c, sym) => c.make(gen.mkWildcardImport(sym))) // there must be a scala.xml package when xml literals were parsed in this unit if (unit.hasXml && ScalaXmlPackage == NoSymbol) reporter.error(unit.firstXmlPos, "To compile XML syntax, the scala.xml package must be on the classpath.\nPlease see https://github.com/scala/scala-xml for details.") // scala-xml needs `scala.xml.TopScope` to be in scope globally as `$scope` // We detect `scala-xml` by looking for `scala.xml.TopScope` and // inject the equivalent of `import scala.xml.{TopScope => $scope}` val contextWithXML = if (!unit.hasXml || ScalaXmlTopScope == NoSymbol) rootImportsContext else rootImportsContext.make(gen.mkImport(ScalaXmlPackage, nme.TopScope, nme.dollarScope)) val c = contextWithXML.make(tree, unit = unit) c.initRootContext(throwing, checking) c } def rootContextPostTyper(unit: CompilationUnit, tree: Tree = EmptyTree): Context = rootContext(unit, tree, throwing = true) def resetContexts() { startContext.enclosingContextChain foreach { context => context.tree match { case Import(qual, _) => qual setType singleType(qual.symbol.owner.thisType, qual.symbol) case _ => } context.reporter.clearAll() } } /** * A motley collection of the state and loosely associated behaviour of the type checker. * Each `Typer` has an associated context, and as it descends into the tree new `(Typer, Context)` * pairs are spawned. * * Meet the crew; first the state: * * - A tree, symbol, and scope representing the focus of the typechecker * - An enclosing context, `outer`. * - The current compilation unit. * - A variety of bits that track the current error reporting policy (more on this later); * whether or not implicits/macros are enabled, whether we are in a self or super call or * in a constructor suffix. These are represented as bits in the mask `contextMode`. * - Some odds and ends: undetermined type parameters of the current line of type inference; * contextual augmentation for error messages, tracking of the nesting depth. * * And behaviour: * * - The central point for issuing errors and warnings from the typechecker, with a means * to buffer these for use in 'silent' type checking, when some recovery might be possible. * - `Context` is something of a Zipper for the tree were are typechecking: it `enclosingContextChain` * is the path back to the root. This is exactly what we need to resolve names (`lookupSymbol`) * and to collect in-scope implicit definitions (`implicitss`) * Supporting these are `imports`, which represents all `Import` trees in in the enclosing context chain. * - In a similar vein, we can assess accessibility (`isAccessible`.) * * More on error buffering: * When are type errors recoverable? In quite a few places, it turns out. Some examples: * trying to type an application with/without the expected type, or with/without implicit views * enabled. This is usually mediated by `Typer.silent`, `Inferencer#tryTwice`. * * Initially, starting from the `typer` phase, the contexts either buffer or report errors; * afterwards errors are thrown. This is configured in `rootContext`. Additionally, more * fine grained control is needed based on the kind of error; ambiguity errors are often * suppressed during exploratory typing, such as determining whether `a == b` in an argument * position is an assignment or a named argument, when `Inferencer#isApplicableSafe` type checks * applications with and without an expected type, or when `Typer#tryTypedApply` tries to fit arguments to * a function type with/without implicit views. * * When the error policies entail error/warning buffering, the mutable [[ReportBuffer]] records * everything that is issued. It is important to note, that child Contexts created with `make` * "inherit" the very same `ReportBuffer` instance, whereas children spawned through `makeSilent` * receive a separate, fresh buffer. * * @param tree Tree associated with this context * @param owner The current owner * @param scope The current scope * @param _outer The next outer context. */ class Context private[typechecker](val tree: Tree, val owner: Symbol, val scope: Scope, val unit: CompilationUnit, _outer: Context, private[this] var _reporter: ContextReporter = new ThrowingReporter) { private def outerIsNoContext = _outer eq null final def outer: Context = if (outerIsNoContext) NoContext else _outer /** The next outer context whose tree is a template or package definition */ var enclClass: Context = _ @inline private def savingEnclClass[A](c: Context)(a: => A): A = { val saved = enclClass enclClass = c try a finally enclClass = saved } /** A bitmask containing all the boolean flags in a context, e.g. are implicit views enabled */ var contextMode: ContextMode = ContextMode.DefaultMode /** Update all modes in `mask` to `value` */ def update(mask: ContextMode, value: Boolean) { contextMode = contextMode.set(value, mask) } /** Set all modes in the mask `enable` to true, and all in `disable` to false. */ def set(enable: ContextMode = NOmode, disable: ContextMode = NOmode): this.type = { contextMode = contextMode.set(true, enable).set(false, disable) this } /** Is this context in all modes in the given `mask`? */ def apply(mask: ContextMode): Boolean = contextMode.inAll(mask) /** The next outer context whose tree is a method */ var enclMethod: Context = _ /** Variance relative to enclosing class */ var variance: Variance = Variance.Invariant private var _undetparams: List[Symbol] = List() protected def outerDepth = if (outerIsNoContext) 0 else outer.depth val depth: Int = { val increasesDepth = isRootImport || outerIsNoContext || (outer.scope != scope) ( if (increasesDepth) 1 else 0 ) + outerDepth } /** The currently visible imports */ def imports: List[ImportInfo] = outer.imports /** Equivalent to `imports.headOption`, but more efficient */ def firstImport: Option[ImportInfo] = outer.firstImport def isRootImport: Boolean = false /** Types for which implicit arguments are currently searched */ var openImplicits: List[OpenImplicit] = List() /* For a named application block (`Tree`) the corresponding `NamedApplyInfo`. */ var namedApplyBlockInfo: Option[(Tree, NamedApplyInfo)] = None var prefix: Type = NoPrefix def inSuperInit_=(value: Boolean) = this(SuperInit) = value def inSuperInit = this(SuperInit) def inConstructorSuffix_=(value: Boolean) = this(ConstructorSuffix) = value def inConstructorSuffix = this(ConstructorSuffix) def inPatAlternative_=(value: Boolean) = this(PatternAlternative) = value def inPatAlternative = this(PatternAlternative) def starPatterns_=(value: Boolean) = this(StarPatterns) = value def starPatterns = this(StarPatterns) def returnsSeen_=(value: Boolean) = this(ReturnsSeen) = value def returnsSeen = this(ReturnsSeen) def inSelfSuperCall_=(value: Boolean) = this(SelfSuperCall) = value def inSelfSuperCall = this(SelfSuperCall) def implicitsEnabled_=(value: Boolean) = this(ImplicitsEnabled) = value def implicitsEnabled = this(ImplicitsEnabled) def macrosEnabled_=(value: Boolean) = this(MacrosEnabled) = value def macrosEnabled = this(MacrosEnabled) def enrichmentEnabled_=(value: Boolean) = this(EnrichmentEnabled) = value def enrichmentEnabled = this(EnrichmentEnabled) def retyping_=(value: Boolean) = this(ReTyping) = value def retyping = this(ReTyping) def inSecondTry = this(SecondTry) def inSecondTry_=(value: Boolean) = this(SecondTry) = value def inReturnExpr = this(ReturnExpr) def inTypeConstructorAllowed = this(TypeConstructorAllowed) def defaultModeForTyped: Mode = if (inTypeConstructorAllowed) Mode.NOmode else Mode.EXPRmode /** To enrich error messages involving default arguments. When extending the notion, group diagnostics in an object. */ var diagUsedDefaults: Boolean = false /** Saved type bounds for type parameters which are narrowed in a GADT. */ var savedTypeBounds: List[(Symbol, Type)] = List() /** The next enclosing context (potentially `this`) that is owned by a class or method */ def enclClassOrMethod: Context = if (!owner.exists || owner.isClass || owner.isMethod) this else outer.enclClassOrMethod /** The next enclosing context (potentially `this`) that has a `CaseDef` as a tree */ def enclosingCaseDef = nextEnclosing(_.tree.isInstanceOf[CaseDef]) /** ...or an Apply. */ def enclosingApply = nextEnclosing(_.tree.isInstanceOf[Apply]) def siteString = { def what_s = if (owner.isConstructor) "" else owner.kindString def where_s = if (owner.isClass) "" else "in " + enclClass.owner.decodedName List(what_s, owner.decodedName, where_s) filterNot (_ == "") mkString " " } // // Tracking undetermined type parameters for type argument inference. // def undetparamsString = if (undetparams.isEmpty) "" else undetparams.mkString("undetparams=", ", ", "") /** Undetermined type parameters. See `Infer#{inferExprInstance, adjustTypeArgs}`. Not inherited to child contexts */ def undetparams: List[Symbol] = _undetparams def undetparams_=(ps: List[Symbol]) = { _undetparams = ps } /** Return and clear the undetermined type parameters */ def extractUndetparams(): List[Symbol] = { val tparams = undetparams undetparams = List() tparams } /** Run `body` with this context with no undetermined type parameters, restore the original * the original list afterwards. * @param reportAmbiguous Should ambiguous errors be reported during evaluation of `body`? */ def savingUndeterminedTypeParams[A](reportAmbiguous: Boolean = ambiguousErrors)(body: => A): A = { withMode() { setAmbiguousErrors(reportAmbiguous) val saved = extractUndetparams() try body finally undetparams = saved } } // // Error reporting policies and buffer. // // the reporter for this context def reporter: ContextReporter = _reporter // if set, errors will not be reporter/thrown def bufferErrors = reporter.isBuffering def reportErrors = !(bufferErrors || reporter.isThrowing) // whether to *report* (which is separate from buffering/throwing) ambiguity errors def ambiguousErrors = this(AmbiguousErrors) private def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report /** * Try inference twice: once without views and once with views, * unless views are already disabled. */ abstract class TryTwice { def tryOnce(isLastTry: Boolean): Unit final def apply(): Unit = { val doLastTry = // do first try if implicits are enabled if (implicitsEnabled) { // We create a new BufferingReporter to // distinguish errors that occurred before entering tryTwice // and our first attempt in 'withImplicitsDisabled'. If the // first attempt fails, we try with implicits on // and the original reporter. // immediate reporting of ambiguous errors is suppressed, so that they are buffered inSilentMode { try { set(disable = ImplicitsEnabled | EnrichmentEnabled) // restored by inSilentMode tryOnce(false) reporter.hasErrors } catch { case ex: CyclicReference => throw ex case ex: TypeError => true // recoverable cyclic references? } } } else true // do last try if try with implicits enabled failed // (or if it was not attempted because they were disabled) if (doLastTry) tryOnce(true) } } // // Temporary mode adjustment // @inline final def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = { val saved = contextMode set(enabled, disabled) try op finally contextMode = saved } @inline final def withImplicitsEnabled[T](op: => T): T = withMode(enabled = ImplicitsEnabled)(op) @inline final def withImplicitsDisabled[T](op: => T): T = withMode(disabled = ImplicitsEnabled | EnrichmentEnabled)(op) @inline final def withImplicitsDisabledAllowEnrichment[T](op: => T): T = withMode(enabled = EnrichmentEnabled, disabled = ImplicitsEnabled)(op) @inline final def withImplicits[T](enabled: Boolean)(op: => T): T = if (enabled) withImplicitsEnabled(op) else withImplicitsDisabled(op) @inline final def withMacrosEnabled[T](op: => T): T = withMode(enabled = MacrosEnabled)(op) @inline final def withMacrosDisabled[T](op: => T): T = withMode(disabled = MacrosEnabled)(op) @inline final def withMacros[T](enabled: Boolean)(op: => T): T = if (enabled) withMacrosEnabled(op) else withMacrosDisabled(op) @inline final def withinStarPatterns[T](op: => T): T = withMode(enabled = StarPatterns)(op) @inline final def withinSuperInit[T](op: => T): T = withMode(enabled = SuperInit)(op) @inline final def withinSecondTry[T](op: => T): T = withMode(enabled = SecondTry)(op) @inline final def withinPatAlternative[T](op: => T): T = withMode(enabled = PatternAlternative)(op) /** TypeConstructorAllowed is enabled when we are typing a higher-kinded type. * adapt should then check kind-arity based on the prototypical type's kind * arity. Type arguments should not be inferred. */ @inline final def withinTypeConstructorAllowed[T](op: => T): T = withMode(enabled = TypeConstructorAllowed)(op) /* TODO - consolidate returnsSeen (which seems only to be used by checkDead) * and ReturnExpr. */ @inline final def withinReturnExpr[T](op: => T): T = { enclMethod.returnsSeen = true withMode(enabled = ReturnExpr)(op) } // See comment on FormerNonStickyModes. @inline final def withOnlyStickyModes[T](op: => T): T = withMode(disabled = FormerNonStickyModes)(op) // inliner note: this has to be a simple method for inlining to work -- moved the `&& !reporter.hasErrors` out @inline final def inSilentMode(expr: => Boolean): Boolean = { val savedContextMode = contextMode val savedReporter = reporter setAmbiguousErrors(false) _reporter = new BufferingReporter try expr finally { contextMode = savedContextMode _reporter = savedReporter } } // // Child Context Creation // /** * Construct a child context. The parent and child will share the report buffer. * Compare with `makeSilent`, in which the child has a fresh report buffer. * * If `tree` is an `Import`, that import will be avaiable at the head of * `Context#imports`. */ def make(tree: Tree = tree, owner: Symbol = owner, scope: Scope = scope, unit: CompilationUnit = unit, reporter: ContextReporter = this.reporter): Context = { val isTemplateOrPackage = tree match { case _: Template | _: PackageDef => true case _ => false } val isDefDef = tree match { case _: DefDef => true case _ => false } val isImport = tree match { // The guard is for SI-8403. It prevents adding imports again in the context created by // `Namer#createInnerNamer` case _: Import if tree != this.tree => true case _ => false } val sameOwner = owner == this.owner val prefixInChild = if (isTemplateOrPackage) owner.thisType else if (!sameOwner && owner.isTerm) NoPrefix else prefix // The blank canvas val c = if (isImport) new Context(tree, owner, scope, unit, this, reporter) with ImportContext else new Context(tree, owner, scope, unit, this, reporter) // Fields that are directly propagated c.variance = variance c.diagUsedDefaults = diagUsedDefaults c.openImplicits = openImplicits c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below. // Fields that may take on a different value in the child c.prefix = prefixInChild c.enclClass = if (isTemplateOrPackage) c else enclClass c(ConstructorSuffix) = !isTemplateOrPackage && c(ConstructorSuffix) // SI-8245 `isLazy` need to skip lazy getters to ensure `return` binds to the right place c.enclMethod = if (isDefDef && !owner.isLazy) c else enclMethod if (tree != outer.tree) c(TypeConstructorAllowed) = false registerContext(c.asInstanceOf[analyzer.Context]) debuglog("[context] ++ " + c.unit + " / " + tree.summaryString) c } /** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/ def initRootContext(throwing: Boolean = false, checking: Boolean = false): Unit = { _reporter = if (checking) new CheckingReporter else if (throwing) new ThrowingReporter else new ImmediateReporter setAmbiguousErrors(!throwing) this(EnrichmentEnabled | ImplicitsEnabled) = !throwing } def make(tree: Tree, owner: Symbol, scope: Scope): Context = // TODO SI-7345 Moving this optimization into the main overload of `make` causes all tests to fail. // even if it is extended to check that `unit == this.unit`. Why is this? if (tree == this.tree && owner == this.owner && scope == this.scope) this else make(tree, owner, scope, unit) /** Make a child context that represents a new nested scope */ def makeNewScope(tree: Tree, owner: Symbol, reporter: ContextReporter = this.reporter): Context = make(tree, owner, newNestedScope(scope), reporter = reporter) /** Make a child context that buffers errors and warnings into a fresh report buffer. */ def makeSilent(reportAmbiguousErrors: Boolean = ambiguousErrors, newtree: Tree = tree): Context = { // A fresh buffer so as not to leak errors/warnings into `this`. val c = make(newtree, reporter = new BufferingReporter) c.setAmbiguousErrors(reportAmbiguousErrors) c } def makeNonSilent(newtree: Tree): Context = { val c = make(newtree, reporter = reporter.makeImmediate) c.setAmbiguousErrors(true) c } /** Make a silent child context does not allow implicits. Used to prevent chaining of implicit views. */ def makeImplicit(reportAmbiguousErrors: Boolean) = { val c = makeSilent(reportAmbiguousErrors) c(ImplicitsEnabled | EnrichmentEnabled) = false c } /** * A context for typing constructor parameter ValDefs, super or self invocation arguments and default getters * of constructors. These expressions need to be type checked in a scope outside the class, cf. spec 5.3.1. * * This method is called by namer / typer where `this` is the context for the constructor DefDef. The * owner of the resulting (new) context is the outer context for the Template, i.e. the context for the * ClassDef. This means that class type parameters will be in scope. The value parameters of the current * constructor are also entered into the new constructor scope. Members of the class however will not be * accessible. */ def makeConstructorContext = { val baseContext = enclClass.outer.nextEnclosing(!_.tree.isInstanceOf[Template]) // must propagate reporter! // (caught by neg/t3649 when refactoring reporting to be specified only by this.reporter and not also by this.contextMode) val argContext = baseContext.makeNewScope(tree, owner, reporter = this.reporter) argContext.contextMode = contextMode argContext.inSelfSuperCall = true def enterElems(c: Context) { def enterLocalElems(e: ScopeEntry) { if (e != null && e.owner == c.scope) { enterLocalElems(e.next) argContext.scope enter e.sym } } if (c.owner.isTerm && !c.owner.isLocalDummy) { enterElems(c.outer) enterLocalElems(c.scope.elems) } } // Enter the scope elements of this (the scope for the constructor DefDef) into the new constructor scope. // Concretely, this will enter the value parameters of constructor. enterElems(this) argContext } // // Error and warning issuance // /** Issue/buffer/throw the given type error according to the current mode for error reporting. */ private[typechecker] def issue(err: AbsTypeError) = reporter.issue(err)(this) /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ private[typechecker] def issueAmbiguousError(err: AbsAmbiguousTypeError) = reporter.issueAmbiguousError(err)(this) /** Issue/throw the given error message according to the current mode for error reporting. */ def error(pos: Position, msg: String) = reporter.error(fixPosition(pos), msg) /** Issue/throw the given error message according to the current mode for error reporting. */ def warning(pos: Position, msg: String) = reporter.warning(fixPosition(pos), msg) def echo(pos: Position, msg: String) = reporter.echo(fixPosition(pos), msg) def fixPosition(pos: Position): Position = pos match { case NoPosition => nextEnclosing(_.tree.pos != NoPosition).tree.pos case _ => pos } def deprecationWarning(pos: Position, sym: Symbol, msg: String, since: String): Unit = currentRun.reporting.deprecationWarning(fixPosition(pos), sym, msg, since) def deprecationWarning(pos: Position, sym: Symbol): Unit = currentRun.reporting.deprecationWarning(fixPosition(pos), sym) // TODO: allow this to escalate to an error, and implicit search will ignore deprecated implicits def featureWarning(pos: Position, featureName: String, featureDesc: String, featureTrait: Symbol, construct: => String = "", required: Boolean): Unit = currentRun.reporting.featureWarning(fixPosition(pos), featureName, featureDesc, featureTrait, construct, required) // nextOuter determines which context is searched next for implicits // (after `this`, which contributes `newImplicits` below.) In // most cases, it is simply the outer context: if we're owned by // a constructor, the actual current context and the conceptual // context are different when it comes to scoping. The current // conceptual scope is the context enclosing the blocks which // represent the constructor body (TODO: why is there more than one // such block in the outer chain?) private def nextOuter = { // Drop the constructor body blocks, which come in varying numbers. // -- If the first statement is in the constructor, scopingCtx == (constructor definition) // -- Otherwise, scopingCtx == (the class which contains the constructor) val scopingCtx = if (owner.isConstructor) nextEnclosing(c => !c.tree.isInstanceOf[Block]) else this scopingCtx.outer } def nextEnclosing(p: Context => Boolean): Context = if (p(this)) this else outer.nextEnclosing(p) def enclosingContextChain: List[Context] = this :: outer.enclosingContextChain private def treeTruncated = tree.toString.replaceAll("\\s+", " ").lines.mkString("\\n").take(70) private def treeIdString = if (settings.uniqid.value) "#" + System.identityHashCode(tree).toString.takeRight(3) else "" private def treeString = tree match { case x: Import => "" + x case Template(parents, `noSelfType`, body) => val pstr = if ((parents eq null) || parents.isEmpty) "Nil" else parents mkString " " val bstr = if (body eq null) "" else body.length + " stats" s"""Template($pstr, _, $bstr)""" case x => s"${tree.shortClass}${treeIdString}:${treeTruncated}" } override def toString = sm"""|Context($unit) { | owner = $owner | tree = $treeString | scope = ${scope.size} decls | contextMode = $contextMode | outer.owner = ${outer.owner} |}""" // // Accessibility checking // /** Is `sub` a subclass of `base` or a companion object of such a subclass? */ private def isSubClassOrCompanion(sub: Symbol, base: Symbol) = sub.isNonBottomSubClass(base) || sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base) /** Return the closest enclosing context that defines a subclass of `clazz` * or a companion object thereof, or `NoContext` if no such context exists. */ def enclosingSubClassContext(clazz: Symbol): Context = { var c = this.enclClass while (c != NoContext && !isSubClassOrCompanion(c.owner, clazz)) c = c.outer.enclClass c } def enclosingNonImportContext: Context = { var c = this while (c != NoContext && c.tree.isInstanceOf[Import]) c = c.outer c } /** Is `sym` accessible as a member of `pre` in current context? */ def isAccessible(sym: Symbol, pre: Type, superAccess: Boolean = false): Boolean = { lastAccessCheckDetails = "" // Console.println("isAccessible(%s, %s, %s)".format(sym, pre, superAccess)) // don't have access if there is no linked class (so exclude linkedClass=NoSymbol) def accessWithinLinked(ab: Symbol) = { val linked = linkedClassOfClassOf(ab, this) linked.fold(false)(accessWithin) } /* Are we inside definition of `ab`? */ def accessWithin(ab: Symbol) = { // #3663: we must disregard package nesting if sym isJavaDefined if (sym.isJavaDefined) { // is `o` or one of its transitive owners equal to `ab`? // stops at first package, since further owners can only be surrounding packages @tailrec def abEnclosesStopAtPkg(o: Symbol): Boolean = (o eq ab) || (!o.isPackageClass && (o ne NoSymbol) && abEnclosesStopAtPkg(o.owner)) abEnclosesStopAtPkg(owner) } else (owner hasTransOwner ab) } def isSubThisType(pre: Type, clazz: Symbol): Boolean = pre match { case ThisType(pclazz) => pclazz isNonBottomSubClass clazz case _ => false } /* Is protected access to target symbol permitted */ def isProtectedAccessOK(target: Symbol) = { val c = enclosingSubClassContext(sym.owner) if (c == NoContext) lastAccessCheckDetails = "\n Access to protected "+target+" not permitted because"+ "\n "+"enclosing "+this.enclClass.owner+ this.enclClass.owner.locationString+" is not a subclass of "+ "\n "+sym.owner+sym.owner.locationString+" where target is defined" c != NoContext && { target.isType || { // allow accesses to types from arbitrary subclasses fixes #4737 val res = isSubClassOrCompanion(pre.widen.typeSymbol, c.owner) || c.owner.isModuleClass && isSubClassOrCompanion(pre.widen.typeSymbol, c.owner.linkedClassOfClass) if (!res) lastAccessCheckDetails = "\n Access to protected "+target+" not permitted because"+ "\n prefix type "+pre.widen+" does not conform to"+ "\n "+c.owner+c.owner.locationString+" where the access take place" res } } } (pre == NoPrefix) || { val ab = sym.accessBoundary(sym.owner) ( (ab.isTerm || ab == rootMirror.RootClass) || (accessWithin(ab) || accessWithinLinked(ab)) && ( !sym.isLocalToThis || sym.isProtected && isSubThisType(pre, sym.owner) || pre =:= sym.owner.thisType ) || sym.isProtected && ( superAccess || pre.isInstanceOf[ThisType] || phase.erasedTypes || (sym.overrideChain exists isProtectedAccessOK) // that last condition makes protected access via self types work. ) ) // note: phase.erasedTypes disables last test, because after addinterfaces // implementation classes are not in the superclass chain. If we enable the // test, bug780 fails. } } // // Type bound management // def pushTypeBounds(sym: Symbol) { sym.info match { case tb: TypeBounds => if (!tb.isEmptyBounds) log(s"Saving $sym info=$tb") case info => devWarning(s"Something other than a TypeBounds seen in pushTypeBounds: $info is a ${shortClassOfInstance(info)}") } savedTypeBounds ::= ((sym, sym.info)) } def restoreTypeBounds(tp: Type): Type = { def restore(): Type = savedTypeBounds.foldLeft(tp) { case (current, (sym, savedInfo)) => def bounds_s(tb: TypeBounds) = if (tb.isEmptyBounds) "" else s"TypeBounds(lo=${tb.lo}, hi=${tb.hi})" //@M TODO: when higher-kinded types are inferred, probably need a case PolyType(_, TypeBounds(...)) if ... => val TypeBounds(lo, hi) = sym.info.bounds val isUnique = lo <:< hi && hi <:< lo val isPresent = current contains sym def saved_s = bounds_s(savedInfo.bounds) def current_s = bounds_s(sym.info.bounds) if (isUnique && isPresent) devWarningResult(s"Preserving inference: ${sym.nameString}=$hi in $current (based on $current_s) before restoring $sym to saved $saved_s")( current.instantiateTypeParams(List(sym), List(hi)) ) else if (isPresent) devWarningResult(s"Discarding inferred $current_s because it does not uniquely determine $sym in")(current) else logResult(s"Discarding inferred $current_s because $sym does not appear in")(current) } try restore() finally { for ((sym, savedInfo) <- savedTypeBounds) sym setInfo debuglogResult(s"Discarding inferred $sym=${sym.info}, restoring saved info")(savedInfo) savedTypeBounds = Nil } } // // Implicit collection // private var implicitsCache: List[ImplicitInfo] = null private var implicitsRunId = NoRunId def resetCache() { implicitsRunId = NoRunId implicitsCache = null if (outer != null && outer != this) outer.resetCache() } /** A symbol `sym` qualifies as an implicit if it has the IMPLICIT flag set, * it is accessible, and if it is imported there is not already a local symbol * with the same names. Local symbols override imported ones. This fixes #2866. */ private def isQualifyingImplicit(name: Name, sym: Symbol, pre: Type, imported: Boolean) = sym.isImplicit && isAccessible(sym, pre) && !(imported && { val e = scope.lookupEntry(name) (e ne null) && (e.owner == scope) && (!settings.isScala212 || e.sym.exists) }) /** Do something with the symbols with name `name` imported via the import in `imp`, * if any such symbol is accessible from this context and is a qualifying implicit. */ private def withQualifyingImplicitAlternatives(imp: ImportInfo, name: Name, pre: Type)(f: Symbol => Unit) = for { sym <- importedAccessibleSymbol(imp, name, requireExplicit = false, record = false).alternatives if isQualifyingImplicit(name, sym, pre, imported = true) } f(sym) private def collectImplicits(syms: Scope, pre: Type, imported: Boolean = false): List[ImplicitInfo] = for (sym <- syms.toList if isQualifyingImplicit(sym.name, sym, pre, imported)) yield new ImplicitInfo(sym.name, pre, sym) private def collectImplicitImports(imp: ImportInfo): List[ImplicitInfo] = { val qual = imp.qual val qualSym = qual.tpe.typeSymbol val pre = qual.tpe def collect(sels: List[ImportSelector]): List[ImplicitInfo] = sels match { case List() => List() case List(ImportSelector(nme.WILDCARD, _, _, _)) => // Using pre.implicitMembers seems to exposes a problem with out-dated symbols in the IDE, // see the example in https://www.assembla.com/spaces/scala-ide/tickets/1002552#/activity/ticket // I haven't been able to boil that down the an automated test yet. // Looking up implicit members in the package, rather than package object, here is at least // consistent with what is done just below for named imports. collectImplicits(qual.tpe.implicitMembers, pre, imported = true) case ImportSelector(from, _, to, _) :: sels1 => var impls = collect(sels1) filter (info => info.name != from) if (to != nme.WILDCARD) { withQualifyingImplicitAlternatives(imp, to, pre) { sym => impls = new ImplicitInfo(to, pre, sym) :: impls } } impls } //debuglog("collect implicit imports " + imp + "=" + collect(imp.tree.selectors))//DEBUG collect(imp.tree.selectors) } /* SI-5892 / SI-4270: `implicitss` can return results which are not accessible at the * point where implicit search is triggered. Example: implicits in (annotations of) * class type parameters (SI-5892). The `context.owner` is the class symbol, therefore * `implicitss` will return implicit conversions defined inside the class. These are * filtered out later by `eligibleInfos` (SI-4270 / 9129cfe9), as they don't type-check. */ def implicitss: List[List[ImplicitInfo]] = { val nextOuter = this.nextOuter def withOuter(is: List[ImplicitInfo]): List[List[ImplicitInfo]] = is match { case Nil => nextOuter.implicitss case _ => is :: nextOuter.implicitss } val CycleMarker = NoRunId - 1 if (implicitsRunId == CycleMarker) { debuglog(s"cycle while collecting implicits at owner ${owner}, probably due to an implicit without an explicit return type. Continuing with implicits from enclosing contexts.") withOuter(Nil) } else if (implicitsRunId != currentRunId) { implicitsRunId = CycleMarker implicits(nextOuter) match { case None => implicitsRunId = NoRunId withOuter(Nil) case Some(is) => implicitsRunId = currentRunId implicitsCache = is withOuter(is) } } else withOuter(implicitsCache) } /** @return None if a cycle is detected, or Some(infos) containing the in-scope implicits at this context */ private def implicits(nextOuter: Context): Option[List[ImplicitInfo]] = { val imports = this.imports if (owner != nextOuter.owner && owner.isClass && !owner.isPackageClass && !inSelfSuperCall) { if (!owner.isInitialized) None else savingEnclClass(this) { // !!! In the body of `class C(implicit a: A) { }`, `implicitss` returns `List(List(a), List(a), List( isAccessible(s, imp1.qual.tpe, superAccess = false)) val imp2Symbol = (imp2 importedSymbol name).initialize filter (s => isAccessible(s, imp2.qual.tpe, superAccess = false)) // The types of the qualifiers from which the ambiguous imports come. // If the ambiguous name is a value, these must be the same. def t1 = imp1.qual.tpe def t2 = imp2.qual.tpe // The types of the ambiguous symbols, seen as members of their qualifiers. // If the ambiguous name is a monomorphic type, we can relax this far. def mt1 = t1 memberType imp1Symbol def mt2 = t2 memberType imp2Symbol def characterize = List( s"types: $t1 =:= $t2 ${t1 =:= t2} members: ${mt1 =:= mt2}", s"member type 1: $mt1", s"member type 2: $mt2" ).mkString("\n ") if (!ambiguous || !imp2Symbol.exists) Some(imp1) else if (!imp1Symbol.exists) Some(imp2) else ( // The symbol names are checked rather than the symbols themselves because // each time an overloaded member is looked up it receives a new symbol. // So foo.member("x") != foo.member("x") if x is overloaded. This seems // likely to be the cause of other bugs too... if (t1 =:= t2 && imp1Symbol.name == imp2Symbol.name) { log(s"Suppressing ambiguous import: $t1 =:= $t2 && $imp1Symbol == $imp2Symbol") Some(imp1) } // Monomorphism restriction on types is in part because type aliases could have the // same target type but attach different variance to the parameters. Maybe it can be // relaxed, but doesn't seem worth it at present. else if (mt1 =:= mt2 && name.isTypeName && imp1Symbol.isMonomorphicType && imp2Symbol.isMonomorphicType) { log(s"Suppressing ambiguous import: $mt1 =:= $mt2 && $imp1Symbol and $imp2Symbol are equivalent") Some(imp1) } else { log(s"Import is genuinely ambiguous:\n " + characterize) None } ) } /** The symbol with name `name` imported via the import in `imp`, * if any such symbol is accessible from this context. */ private def importedAccessibleSymbol(imp: ImportInfo, name: Name, requireExplicit: Boolean, record: Boolean): Symbol = imp.importedSymbol(name, requireExplicit, record) filter (s => isAccessible(s, imp.qual.tpe, superAccess = false)) private def requiresQualifier(s: Symbol): Boolean = ( s.owner.isClass && !s.owner.isPackageClass && !s.isTypeParameterOrSkolem && !s.isExistentiallyBound ) /** Must `sym` defined in package object of package `pkg`, if * it selected from a prefix with `pkg` as its type symbol? */ def isInPackageObject(sym: Symbol, pkg: Symbol): Boolean = { if (sym.isOverloaded) sym.alternatives.exists(alt => isInPackageObject(alt, pkg)) else pkg.hasPackageFlag && sym.owner != pkg && requiresQualifier(sym) } def isNameInScope(name: Name) = lookupSymbol(name, _ => true).isSuccess /** Find the symbol of a simple name starting from this context. * All names are filtered through the "qualifies" predicate, * the search continuing as long as no qualifying name is found. */ def lookupSymbol(name: Name, qualifies: Symbol => Boolean): NameLookup = { var lookupError: NameLookup = null // set to non-null if a definite error is encountered var inaccessible: NameLookup = null // records inaccessible symbol for error reporting in case none is found var defSym: Symbol = NoSymbol // the directly found symbol var pre: Type = NoPrefix // the prefix type of defSym, if a class member var cx: Context = this // the context under consideration var symbolDepth: Int = -1 // the depth of the directly found symbol def finish(qual: Tree, sym: Symbol): NameLookup = ( if (lookupError ne null) lookupError else sym match { case NoSymbol if inaccessible ne null => inaccessible case NoSymbol => LookupNotFound case _ => LookupSucceeded(qual, sym) } ) def finishDefSym(sym: Symbol, pre0: Type): NameLookup = if (requiresQualifier(sym)) finish(gen.mkAttributedQualifier(pre0), sym) else finish(EmptyTree, sym) def isPackageOwnedInDifferentUnit(s: Symbol) = ( s.isDefinedInPackage && ( !currentRun.compiles(s) || unit.exists && s.sourceFile != unit.source.file ) ) def lookupInPrefix(name: Name) = { val sym = pre.member(name).filter(qualifies) def isNonPackageNoModuleClass(sym: Symbol) = sym.isClass && !sym.isModuleClass && !sym.isPackageClass if (!sym.exists && unit.isJava && isNonPackageNoModuleClass(pre.typeSymbol)) { // TODO factor out duplication with Typer::inCompanionForJavaStatic val pre1 = companionSymbolOf(pre.typeSymbol, this).typeOfThis pre1.member(name).filter(qualifies).andAlso(_ => pre = pre1) } else sym } def accessibleInPrefix(s: Symbol) = isAccessible(s, pre, superAccess = false) def searchPrefix = { cx = cx.enclClass val found0 = lookupInPrefix(name) val found1 = found0 filter accessibleInPrefix if (found0.exists && !found1.exists && inaccessible == null) inaccessible = LookupInaccessible(found0, analyzer.lastAccessCheckDetails) found1 } def lookupInScope(scope: Scope) = (scope lookupUnshadowedEntries name filter (e => qualifies(e.sym))).toList def newOverloaded(owner: Symbol, pre: Type, entries: List[ScopeEntry]) = logResult(s"overloaded symbol in $pre")(owner.newOverloaded(pre, entries map (_.sym))) // Constructor lookup should only look in the decls of the enclosing class // not in the self-type, nor in the enclosing context, nor in imports (SI-4460, SI-6745) if (name == nme.CONSTRUCTOR) return { val enclClassSym = cx.enclClass.owner val scope = cx.enclClass.prefix.baseType(enclClassSym).decls val constructorSym = lookupInScope(scope) match { case Nil => NoSymbol case hd :: Nil => hd.sym case entries => newOverloaded(enclClassSym, cx.enclClass.prefix, entries) } finishDefSym(constructorSym, cx.enclClass.prefix) } // cx.scope eq null arises during FixInvalidSyms in Duplicators while (defSym == NoSymbol && (cx ne NoContext) && (cx.scope ne null)) { pre = cx.enclClass.prefix defSym = lookupInScope(cx.scope) match { case Nil => searchPrefix case entries @ (hd :: tl) => // we have a winner: record the symbol depth symbolDepth = (cx.depth - cx.scope.nestingLevel) + hd.depth if (tl.isEmpty) hd.sym else newOverloaded(cx.owner, pre, entries) } if (!defSym.exists) cx = cx.outer // push further outward } if (symbolDepth < 0) symbolDepth = cx.depth var impSym: Symbol = NoSymbol var imports = Context.this.imports def imp1 = imports.head def imp2 = imports.tail.head def sameDepth = imp1.depth == imp2.depth def imp1Explicit = imp1 isExplicitImport name def imp2Explicit = imp2 isExplicitImport name def lookupImport(imp: ImportInfo, requireExplicit: Boolean) = importedAccessibleSymbol(imp, name, requireExplicit, record = true) filter qualifies // Java: A single-type-import declaration d in a compilation unit c of package p // that imports a type named n shadows, throughout c, the declarations of: // // 1) any top level type named n declared in another compilation unit of p // // A type-import-on-demand declaration never causes any other declaration to be shadowed. // // Scala: Bindings of different kinds have a precedence defined on them: // // 1) Definitions and declarations that are local, inherited, or made available by a // package clause in the same compilation unit where the definition occurs have // highest precedence. // 2) Explicit imports have next highest precedence. def depthOk(imp: ImportInfo) = ( imp.depth > symbolDepth || (unit.isJava && imp.isExplicitImport(name) && imp.depth == symbolDepth) ) while (!impSym.exists && imports.nonEmpty && depthOk(imports.head)) { impSym = lookupImport(imp1, requireExplicit = false) if (!impSym.exists) imports = imports.tail } if (defSym.exists && impSym.exists) { // imported symbols take precedence over package-owned symbols in different compilation units. if (isPackageOwnedInDifferentUnit(defSym)) defSym = NoSymbol // Defined symbols take precedence over erroneous imports. else if (impSym.isError || impSym.name == nme.CONSTRUCTOR) impSym = NoSymbol // Otherwise they are irreconcilably ambiguous else return ambiguousDefnAndImport(defSym.alternatives.head.owner, imp1) } // At this point only one or the other of defSym and impSym might be set. if (defSym.exists) finishDefSym(defSym, pre) else if (impSym.exists) { // We continue walking down the imports as long as the tail is non-empty, which gives us: // imports == imp1 :: imp2 :: _ // And at least one of the following is true: // - imp1 and imp2 are at the same depth // - imp1 is a wildcard import, so all explicit imports from outer scopes must be checked def keepLooking = ( lookupError == null && imports.tail.nonEmpty && (sameDepth || !imp1Explicit) ) // If we find a competitor imp2 which imports the same name, possible outcomes are: // // - same depth, imp1 wild, imp2 explicit: imp2 wins, drop imp1 // - same depth, imp1 wild, imp2 wild: ambiguity check // - same depth, imp1 explicit, imp2 explicit: ambiguity check // - differing depth, imp1 wild, imp2 explicit: ambiguity check // - all others: imp1 wins, drop imp2 // // The ambiguity check is: if we can verify that both imports refer to the same // symbol (e.g. import foo.X followed by import foo._) then we discard imp2 // and proceed. If we cannot, issue an ambiguity error. while (keepLooking) { // If not at the same depth, limit the lookup to explicit imports. // This is desirable from a performance standpoint (compare to // filtering after the fact) but also necessary to keep the unused // import check from being misled by symbol lookups which are not // actually used. val other = lookupImport(imp2, requireExplicit = !sameDepth) def imp1wins() = { imports = imp1 :: imports.tail.tail } def imp2wins() = { impSym = other ; imports = imports.tail } if (!other.exists) // imp1 wins; drop imp2 and continue. imp1wins() else if (sameDepth && !imp1Explicit && imp2Explicit) // imp2 wins; drop imp1 and continue. imp2wins() else resolveAmbiguousImport(name, imp1, imp2) match { case Some(imp) => if (imp eq imp1) imp1wins() else imp2wins() case _ => lookupError = ambiguousImports(imp1, imp2) } } // optimization: don't write out package prefixes finish(resetPos(imp1.qual.duplicate), impSym) } else finish(EmptyTree, NoSymbol) } /** * Find a symbol in this context or one of its outers. * * Used to find symbols are owned by methods (or fields), they can't be * found in some scope. * * Examples: companion module of classes owned by a method, default getter * methods of nested methods. See NamesDefaults.scala */ def lookup(name: Name, expectedOwner: Symbol) = { var res: Symbol = NoSymbol var ctx = this while (res == NoSymbol && ctx.outer != ctx) { val s = ctx.scope lookup name if (s != NoSymbol && s.owner == expectedOwner) res = s else ctx = ctx.outer } res } final def lookupCompanionInIncompleteOwner(original: Symbol): Symbol = { /* Search scopes in current and enclosing contexts for the definition of `symbol` */ def lookupScopeEntry(symbol: Symbol): ScopeEntry = { var res: ScopeEntry = null var ctx = this while (res == null && ctx.outer != ctx) { val s = ctx.scope lookupSymbolEntry symbol if (s != null) res = s else ctx = ctx.outer } res } // 1) Must be owned by the same Scope, to ensure that in // `{ class C; { ...; object C } }`, the class is not seen as a companion of the object. // 2) Must be a class and module symbol, so that `{ class C; def C }` or `{ type T; object T }` are not companions. lookupScopeEntry(original) match { case null => NoSymbol case entry => def isCompanion(sym: Symbol): Boolean = (original.isModule && sym.isClass || sym.isModule && original.isClass) && sym.isCoDefinedWith(original) entry.owner.lookupNameInSameScopeAs(original, original.name.companionName).filter(isCompanion) } } } //class Context /** A `Context` focussed on an `Import` tree */ trait ImportContext extends Context { private val impInfo: ImportInfo = { val info = new ImportInfo(tree.asInstanceOf[Import], outerDepth) if (settings.warnUnusedImport && !isRootImport) // excludes java.lang/scala/Predef imports allImportInfos(unit) ::= info info } override final def imports = impInfo :: super.imports override final def firstImport = Some(impInfo) override final def isRootImport = !tree.pos.isDefined override final def toString = super.toString + " with " + s"ImportContext { $impInfo; outer.owner = ${outer.owner} }" } /** A reporter for use during type checking. It has multiple modes for handling errors. * * The default (immediate mode) is to send the error to the global reporter. * When switched into buffering mode via makeBuffering, errors and warnings are buffered and not be reported * (there's a special case for ambiguity errors for some reason: those are force to the reporter when context.ambiguousErrors, * or else they are buffered -- TODO: can we simplify this?) * * When using the type checker after typers, an error results in a TypeError being thrown. TODO: get rid of this mode. * * To handle nested contexts, reporters share buffers. TODO: only buffer in BufferingReporter, emit immediately in ImmediateReporter */ abstract class ContextReporter(private[this] var _errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, private[this] var _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends Reporter { type Error = AbsTypeError type Warning = (Position, String) def issue(err: AbsTypeError)(implicit context: Context): Unit = handleError(context.fixPosition(err.errPos), addDiagString(err.errMsg)) protected def handleError(pos: Position, msg: String): Unit protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = () protected def handleWarning(pos: Position, msg: String): Unit = reporter.warning(pos, msg) def makeImmediate: ContextReporter = this def makeBuffering: ContextReporter = this def isBuffering: Boolean = false def isThrowing: Boolean = false /** Emit an ambiguous error according to context.ambiguousErrors * * - when true, use global.reporter regardless of whether we're buffering (TODO: can we change this?) * - else, let this context reporter decide */ final def issueAmbiguousError(err: AbsAmbiguousTypeError)(implicit context: Context): Unit = if (context.ambiguousErrors) reporter.error(context.fixPosition(err.errPos), addDiagString(err.errMsg)) // force reporting... see TODO above else handleSuppressedAmbiguous(err) @inline final def withFreshErrorBuffer[T](expr: => T): T = { val previousBuffer = _errorBuffer _errorBuffer = newBuffer val res = expr // expr will read _errorBuffer _errorBuffer = previousBuffer res } @inline final def propagatingErrorsTo[T](target: ContextReporter)(expr: => T): T = { val res = expr // TODO: make sure we're okay skipping the try/finally overhead if ((this ne target) && hasErrors) { // `this eq target` in e.g., test/files/neg/divergent-implicit.scala // assert(target.errorBuffer ne _errorBuffer) target ++= errors // TODO: is clearAllErrors necessary? (no tests failed when dropping it) // NOTE: even though `this ne target`, it may still be that `target.errorBuffer eq _errorBuffer`, // so don't clear the buffer, but null out the reference so that a new one will be created when necessary (should be never??) // (we should refactor error buffering to avoid mutation on shared buffers) clearAllErrors() } res } protected final def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = severity match { case ERROR => handleError(pos, msg) case WARNING => handleWarning(pos, msg) case INFO => reporter.echo(pos, msg) } final override def hasErrors = super.hasErrors || errorBuffer.nonEmpty // TODO: everything below should be pushed down to BufferingReporter (related to buffering) // Implicit relies on this most heavily, but there you know reporter.isInstanceOf[BufferingReporter] // can we encode this statically? // have to pass in context because multiple contexts may share the same ReportBuffer def reportFirstDivergentError(fun: Tree, param: Symbol, paramTp: Type)(implicit context: Context): Unit = errors.collectFirst { case dte: DivergentImplicitTypeError => dte } match { case Some(divergent) => // DivergentImplicit error has higher priority than "no implicit found" // no need to issue the problem again if we are still in silent mode if (context.reportErrors) { context.issue(divergent.withPt(paramTp)) errorBuffer.retain { case dte: DivergentImplicitTypeError => false case _ => true } } case _ => NoImplicitFoundError(fun, param)(context) } def retainDivergentErrorsExcept(saved: DivergentImplicitTypeError) = errorBuffer.retain { case err: DivergentImplicitTypeError => err ne saved case _ => false } def propagateImplicitTypeErrorsTo(target: ContextReporter) = { errors foreach { case err@(_: DivergentImplicitTypeError | _: AmbiguousImplicitTypeError) => target.errorBuffer += err case _ => } // debuglog("propagateImplicitTypeErrorsTo: " + errors) } protected def addDiagString(msg: String)(implicit context: Context): String = { val diagUsedDefaultsMsg = "Error occurred in an application involving default arguments." if (context.diagUsedDefaults && !(msg endsWith diagUsedDefaultsMsg)) msg + "\n" + diagUsedDefaultsMsg else msg } final def emitWarnings() = if (_warningBuffer != null) { _warningBuffer foreach { case (pos, msg) => reporter.warning(pos, msg) } _warningBuffer = null } // [JZ] Contexts, pre- the SI-7345 refactor, avoided allocating the buffers until needed. This // is replicated here out of conservatism. private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results. final protected def errorBuffer = { if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer } final protected def warningBuffer = { if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer } final def errors: immutable.Seq[Error] = errorBuffer.toVector final def warnings: immutable.Seq[Warning] = warningBuffer.toVector final def firstError: Option[AbsTypeError] = errorBuffer.headOption // TODO: remove ++= and clearAll* entirely in favor of more high-level combinators like withFreshErrorBuffer final private[typechecker] def ++=(errors: Traversable[AbsTypeError]): Unit = errorBuffer ++= errors // null references to buffers instead of clearing them, // as the buffers may be shared between different reporters final def clearAll(): Unit = { _errorBuffer = null; _warningBuffer = null } final def clearAllErrors(): Unit = { _errorBuffer = null } } private[typechecker] class ImmediateReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) { override def makeBuffering: ContextReporter = new BufferingReporter(errorBuffer, warningBuffer) protected def handleError(pos: Position, msg: String): Unit = reporter.error(pos, msg) } private[typechecker] class BufferingReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) { override def isBuffering = true override def issue(err: AbsTypeError)(implicit context: Context): Unit = errorBuffer += err // this used to throw new TypeError(pos, msg) -- buffering lets us report more errors (test/files/neg/macro-basic-mamdmi) // the old throwing behavior was relied on by diagnostics in manifestOfType protected def handleError(pos: Position, msg: String): Unit = errorBuffer += TypeErrorWrapper(new TypeError(pos, msg)) override protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = errorBuffer += err override protected def handleWarning(pos: Position, msg: String): Unit = warningBuffer += ((pos, msg)) // TODO: emit all buffered errors, warnings override def makeImmediate: ContextReporter = new ImmediateReporter(errorBuffer, warningBuffer) } /** Used after typer (specialization relies on TypeError being thrown, among other post-typer phases). * * TODO: get rid of it, use ImmediateReporter and a check for reporter.hasErrors where necessary */ private[typechecker] class ThrowingReporter extends ContextReporter { override def isThrowing = true protected def handleError(pos: Position, msg: String): Unit = throw new TypeError(pos, msg) } /** Used during a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */ private[typechecker] class CheckingReporter extends ContextReporter { protected def handleError(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg) } class ImportInfo(val tree: Import, val depth: Int) { def pos = tree.pos def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos /** The prefix expression */ def qual: Tree = tree.symbol.info match { case ImportType(expr) => expr case ErrorType => tree setType NoType // fix for #2870 case _ => throw new FatalError("symbol " + tree.symbol + " has bad type: " + tree.symbol.info) //debug } /** Is name imported explicitly, not via wildcard? */ def isExplicitImport(name: Name): Boolean = tree.selectors exists (_.rename == name.toTermName) /** The symbol with name `name` imported from import clause `tree`. */ def importedSymbol(name: Name): Symbol = importedSymbol(name, requireExplicit = false, record = true) private def recordUsage(sel: ImportSelector, result: Symbol): Unit = { debuglog(s"In $this at ${ pos.source.file.name }:${ posOf(sel).line }, selector '${ selectorString(sel) }' resolved to ${ if (tree.symbol.hasCompleteInfo) s"(qual=$qual, $result)" else s"(expr=${tree.expr}, ${result.fullLocationString})" }") allUsedSelectors(this) += sel } /** If requireExplicit is true, wildcard imports are not considered. */ def importedSymbol(name: Name, requireExplicit: Boolean, record: Boolean): Symbol = { var result: Symbol = NoSymbol var renamed = false var selectors = tree.selectors def current = selectors.head while ((selectors ne Nil) && result == NoSymbol) { if (current.rename == name.toTermName) result = qual.tpe.nonLocalMember( // new to address #2733: consider only non-local members for imports if (name.isTypeName) current.name.toTypeName else current.name) else if (current.name == name.toTermName) renamed = true else if (current.name == nme.WILDCARD && !renamed && !requireExplicit) result = qual.tpe.nonLocalMember(name) if (result == NoSymbol) selectors = selectors.tail } if (record && settings.warnUnusedImport && selectors.nonEmpty && result != NoSymbol && pos != NoPosition) recordUsage(current, result) // Harden against the fallout from bugs like SI-6745 // // [JZ] I considered issuing a devWarning and moving the // check inside the above loop, as I believe that // this always represents a mistake on the part of // the caller. if (definitions isImportable result) result else NoSymbol } private def selectorString(s: ImportSelector): String = { if (s.name == nme.WILDCARD && s.rename == null) "_" else if (s.name == s.rename) "" + s.name else s.name + " => " + s.rename } def allImportedSymbols: Iterable[Symbol] = importableMembers(qual.tpe) flatMap (transformImport(tree.selectors, _)) private def transformImport(selectors: List[ImportSelector], sym: Symbol): List[Symbol] = selectors match { case List() => List() case List(ImportSelector(nme.WILDCARD, _, _, _)) => List(sym) case ImportSelector(from, _, to, _) :: _ if from == sym.name => if (to == nme.WILDCARD) List() else List(sym.cloneSymbol(sym.owner, sym.rawflags, to)) case _ :: rest => transformImport(rest, sym) } override def hashCode = tree.## override def equals(other: Any) = other match { case that: ImportInfo => (tree == that.tree) case _ => false } override def toString = tree.toString } type ImportType = global.ImportType val ImportType = global.ImportType } object ContextMode { import scala.language.implicitConversions private implicit def liftIntBitsToContextState(bits: Int): ContextMode = apply(bits) def apply(bits: Int): ContextMode = new ContextMode(bits) final val NOmode: ContextMode = 0 final val AmbiguousErrors: ContextMode = 1 << 2 /** Are we in a secondary constructor after the this constructor call? */ final val ConstructorSuffix: ContextMode = 1 << 3 /** For method context: were returns encountered? */ final val ReturnsSeen: ContextMode = 1 << 4 /** Is this context (enclosed in) a constructor call? * (the call to the super or self constructor in the first line of a constructor.) * In such a context, the object's fields should not be in scope */ final val SelfSuperCall: ContextMode = 1 << 5 // TODO harvest documentation for this final val ImplicitsEnabled: ContextMode = 1 << 6 final val MacrosEnabled: ContextMode = 1 << 7 /** To selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed */ final val EnrichmentEnabled: ContextMode = 1 << 8 /** Are we retypechecking arguments independently from the function applied to them? See `Typer.tryTypedApply` * TODO - iron out distinction/overlap with SecondTry. */ final val ReTyping: ContextMode = 1 << 10 /** Are we typechecking pattern alternatives. Formerly ALTmode. */ final val PatternAlternative: ContextMode = 1 << 11 /** Are star patterns allowed. Formerly STARmode. */ final val StarPatterns: ContextMode = 1 << 12 /** Are we typing the "super" in a superclass constructor call super.. Formerly SUPERCONSTRmode. */ final val SuperInit: ContextMode = 1 << 13 /* Is this the second attempt to type this tree? In that case functions * may no longer be coerced with implicit views. Formerly SNDTRYmode. */ final val SecondTry: ContextMode = 1 << 14 /** Are we in return position? Formerly RETmode. */ final val ReturnExpr: ContextMode = 1 << 15 /** Are unapplied type constructors allowed here? Formerly HKmode. */ final val TypeConstructorAllowed: ContextMode = 1 << 16 /** TODO: The "sticky modes" are EXPRmode, PATTERNmode, TYPEmode. * To mimic the sticky mode behavior, when captain stickyfingers * comes around we need to propagate those modes but forget the other * context modes which were once mode bits; those being so far the * ones listed here. */ final val FormerNonStickyModes: ContextMode = ( PatternAlternative | StarPatterns | SuperInit | SecondTry | ReturnExpr | TypeConstructorAllowed ) final val DefaultMode: ContextMode = MacrosEnabled private val contextModeNameMap = Map( AmbiguousErrors -> "AmbiguousErrors", ConstructorSuffix -> "ConstructorSuffix", SelfSuperCall -> "SelfSuperCall", ImplicitsEnabled -> "ImplicitsEnabled", MacrosEnabled -> "MacrosEnabled", ReTyping -> "ReTyping", PatternAlternative -> "PatternAlternative", StarPatterns -> "StarPatterns", SuperInit -> "SuperInit", SecondTry -> "SecondTry", TypeConstructorAllowed -> "TypeConstructorAllowed" ) } /** * A value class to carry the boolean flags of a context, such as whether errors should * be buffered or reported. */ final class ContextMode private (val bits: Int) extends AnyVal { import ContextMode._ def &(other: ContextMode): ContextMode = new ContextMode(bits & other.bits) def |(other: ContextMode): ContextMode = new ContextMode(bits | other.bits) def &~(other: ContextMode): ContextMode = new ContextMode(bits & ~(other.bits)) def set(value: Boolean, mask: ContextMode) = if (value) |(mask) else &~(mask) def inAll(required: ContextMode) = (this & required) == required def inAny(required: ContextMode) = (this & required) != NOmode def inNone(prohibited: ContextMode) = (this & prohibited) == NOmode override def toString = if (bits == 0) "NOmode" else (contextModeNameMap filterKeys inAll).values.toList.sorted mkString " " }