diff options
18 files changed, 338 insertions, 163 deletions
diff --git a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala index 7aa87dc2f8..bb7e1f9b56 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala @@ -69,8 +69,7 @@ trait GenTypes { def reificationIsConcrete: Boolean = state.reificationIsConcrete def spliceType(tpe: Type): Tree = { - val quantified = currentQuantified - if (tpe.isSpliceable && !(quantified contains tpe.typeSymbol)) { + if (tpe.isSpliceable && !(boundSymbolsInCallstack contains tpe.typeSymbol)) { if (reifyDebug) println("splicing " + tpe) val tagFlavor = if (concrete) tpnme.TypeTag.toString else tpnme.WeakTypeTag.toString diff --git a/src/compiler/scala/reflect/reify/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala index dc0028be38..8e13a45cdb 100644 --- a/src/compiler/scala/reflect/reify/phases/Reify.scala +++ b/src/compiler/scala/reflect/reify/phases/Reify.scala @@ -28,7 +28,10 @@ trait Reify extends GenSymbols finally currents = currents.tail } } - def currentQuantified = flatCollect(reifyStack.currents)({ case ExistentialType(quantified, _) => quantified }) + def boundSymbolsInCallstack = flatCollect(reifyStack.currents) { + case ExistentialType(quantified, _) => quantified + case PolyType(typeParams, _) => typeParams + } def current = reifyStack.currents.head def currents = reifyStack.currents diff --git a/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala index f60d56d9bb..f509c63ba0 100755 --- a/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala +++ b/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala @@ -100,26 +100,26 @@ trait CommentFactoryBase { this: MemberLookupBase => } - protected val endOfText = '\u0003' - protected val endOfLine = '\u000A' + private val endOfText = '\u0003' + private val endOfLine = '\u000A' /** Something that should not have happened, happened, and Scaladoc should exit. */ - protected def oops(msg: String): Nothing = + private def oops(msg: String): Nothing = throw FatalError("program logic: " + msg) /** The body of a line, dropping the (optional) start star-marker, * one leading whitespace and all trailing whitespace. */ - protected val CleanCommentLine = + private val CleanCommentLine = new Regex("""(?:\s*\*\s?)?(.*)""") /** Dangerous HTML tags that should be replaced by something safer, * such as wiki syntax, or that should be dropped. */ - protected val DangerousTags = + private val DangerousTags = new Regex("""<(/?(div|ol|ul|li|h[1-6]|p))( [^>]*)?/?>|<!--.*-->""") /** Maps a dangerous HTML tag to a safe wiki replacement, or an empty string * if it cannot be salvaged. */ - protected def htmlReplacement(mtch: Regex.Match): String = mtch.group(1) match { + private def htmlReplacement(mtch: Regex.Match): String = mtch.group(1) match { case "p" | "div" => "\n\n" case "h1" => "\n= " case "/h1" => " =\n" @@ -135,11 +135,11 @@ trait CommentFactoryBase { this: MemberLookupBase => /** Javadoc tags that should be replaced by something useful, such as wiki * syntax, or that should be dropped. */ - protected val JavadocTags = + private val JavadocTags = new Regex("""\{\@(code|docRoot|inheritDoc|link|linkplain|literal|value)([^}]*)\}""") /** Maps a javadoc tag to a useful wiki replacement, or an empty string if it cannot be salvaged. */ - protected def javadocReplacement(mtch: Regex.Match): String = mtch.group(1) match { + private def javadocReplacement(mtch: Regex.Match): String = mtch.group(1) match { case "code" => "`" + mtch.group(2) + "`" case "docRoot" => "" case "inheritDoc" => "" @@ -151,41 +151,41 @@ trait CommentFactoryBase { this: MemberLookupBase => } /** Safe HTML tags that can be kept. */ - protected val SafeTags = + private val SafeTags = new Regex("""((&\w+;)|(&#\d+;)|(</?(abbr|acronym|address|area|a|bdo|big|blockquote|br|button|b|caption|cite|code|col|colgroup|dd|del|dfn|em|fieldset|form|hr|img|input|ins|i|kbd|label|legend|link|map|object|optgroup|option|param|pre|q|samp|select|small|span|strong|sub|sup|table|tbody|td|textarea|tfoot|th|thead|tr|tt|var)( [^>]*)?/?>))""") - protected val safeTagMarker = '\u000E' + private val safeTagMarker = '\u000E' /** A Scaladoc tag not linked to a symbol and not followed by text */ - protected val SingleTag = + private val SingleTagRegex = new Regex("""\s*@(\S+)\s*""") /** A Scaladoc tag not linked to a symbol. Returns the name of the tag, and the rest of the line. */ - protected val SimpleTag = + private val SimpleTagRegex = new Regex("""\s*@(\S+)\s+(.*)""") /** A Scaladoc tag linked to a symbol. Returns the name of the tag, the name * of the symbol, and the rest of the line. */ - protected val SymbolTag = + private val SymbolTagRegex = new Regex("""\s*@(param|tparam|throws|groupdesc|groupname|groupprio)\s+(\S*)\s*(.*)""") /** The start of a scaladoc code block */ - protected val CodeBlockStart = + private val CodeBlockStartRegex = new Regex("""(.*?)((?:\{\{\{)|(?:\u000E<pre(?: [^>]*)?>\u000E))(.*)""") /** The end of a scaladoc code block */ - protected val CodeBlockEnd = + private val CodeBlockEndRegex = new Regex("""(.*?)((?:\}\}\})|(?:\u000E</pre>\u000E))(.*)""") /** A key used for a tag map. The key is built from the name of the tag and * from the linked symbol if the tag has one. * Equality on tag keys is structural. */ - protected sealed abstract class TagKey { + private sealed abstract class TagKey { def name: String } - protected final case class SimpleTagKey(name: String) extends TagKey - protected final case class SymbolTagKey(name: String, symbol: String) extends TagKey + private final case class SimpleTagKey(name: String) extends TagKey + private final case class SymbolTagKey(name: String, symbol: String) extends TagKey /** Parses a raw comment string into a `Comment` object. * @param comment The expanded comment string (including start and end markers) to be parsed. @@ -231,7 +231,7 @@ trait CommentFactoryBase { this: MemberLookupBase => inCodeBlock: Boolean ): Comment = remaining match { - case CodeBlockStart(before, marker, after) :: ls if (!inCodeBlock) => + case CodeBlockStartRegex(before, marker, after) :: ls if (!inCodeBlock) => if (!before.trim.isEmpty && !after.trim.isEmpty) parse0(docBody, tags, lastTagKey, before :: marker :: after :: ls, false) else if (!before.trim.isEmpty) @@ -250,7 +250,7 @@ trait CommentFactoryBase { this: MemberLookupBase => parse0(docBody append endOfLine append marker, tags, lastTagKey, ls, true) } - case CodeBlockEnd(before, marker, after) :: ls => + case CodeBlockEndRegex(before, marker, after) :: ls => if (!before.trim.isEmpty && !after.trim.isEmpty) parse0(docBody, tags, lastTagKey, before :: marker :: after :: ls, true) if (!before.trim.isEmpty) @@ -269,17 +269,17 @@ trait CommentFactoryBase { this: MemberLookupBase => parse0(docBody append endOfLine append marker, tags, lastTagKey, ls, false) } - case SymbolTag(name, sym, body) :: ls if (!inCodeBlock) => + case SymbolTagRegex(name, sym, body) :: ls if (!inCodeBlock) => val key = SymbolTagKey(name, sym) val value = body :: tags.getOrElse(key, Nil) parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) - case SimpleTag(name, body) :: ls if (!inCodeBlock) => + case SimpleTagRegex(name, body) :: ls if (!inCodeBlock) => val key = SimpleTagKey(name) val value = body :: tags.getOrElse(key, Nil) parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) - case SingleTag(name) :: ls if (!inCodeBlock) => + case SingleTagRegex(name) :: ls if (!inCodeBlock) => val key = SimpleTagKey(name) val value = "" :: tags.getOrElse(key, Nil) parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala index 7dc0b786a7..73738ebd21 100644 --- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -162,6 +162,20 @@ trait CompilerControl { self: Global => def askLinkPos(sym: Symbol, source: SourceFile, response: Response[Position]) = postWorkItem(new AskLinkPosItem(sym, source, response)) + /** Sets sync var `response` to doc comment information for a given symbol. + * + * @param sym The symbol whose doc comment should be retrieved (might come from a classfile) + * @param site The place where sym is observed. + * @param source The source file that's supposed to contain the definition + * @param response A response that will be set to the following: + * If `source` contains a definition of a given symbol that has a doc comment, + * the (expanded, raw, position) triplet for a comment, otherwise ("", "", NoPosition). + * Note: This operation does not automatically load `source`. If `source` + * is unloaded, it stays that way. + */ + def askDocComment(sym: Symbol, site: Symbol, source: SourceFile, response: Response[(String, String, Position)]) = + postWorkItem(new AskDocCommentItem(sym, site, source, response)) + /** Sets sync var `response` to list of members that are visible * as members of the tree enclosing `pos`, possibly reachable by an implicit. * @pre source is loaded @@ -245,15 +259,12 @@ trait CompilerControl { self: Global => } /** Returns parse tree for source `source`. No symbols are entered. Syntax errors are reported. - * Can be called asynchronously from presentation compiler. + * + * This method is thread-safe and as such can safely run outside of the presentation + * compiler thread. */ - def parseTree(source: SourceFile): Tree = ask { () => - getUnit(source) match { - case Some(unit) if unit.status >= JustParsed => - unit.body - case _ => - new UnitParser(new CompilationUnit(source)).parse() - } + def parseTree(source: SourceFile): Tree = { + new UnitParser(new CompilationUnit(source)).parse() } /** Asks for a computation to be done quickly on the presentation compiler thread */ @@ -379,6 +390,14 @@ trait CompilerControl { self: Global => response raise new MissingResponse } + case class AskDocCommentItem(val sym: Symbol, val site: Symbol, val source: SourceFile, response: Response[(String, String, Position)]) extends WorkItem { + def apply() = self.getDocComment(sym, site, source, response) + override def toString = "doc comment "+sym+" in "+source + + def raiseMissing() = + response raise new MissingResponse + } + case class AskLoadedTypedItem(val source: SourceFile, response: Response[Tree]) extends WorkItem { def apply() = self.waitLoadedTyped(source, response, this.onCompilerThread) override def toString = "wait loaded & typed "+source diff --git a/src/compiler/scala/tools/nsc/interactive/Doc.scala b/src/compiler/scala/tools/nsc/interactive/Doc.scala deleted file mode 100755 index ad28a28105..0000000000 --- a/src/compiler/scala/tools/nsc/interactive/Doc.scala +++ /dev/null @@ -1,59 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2007-2012 LAMP/EPFL - * @author Eugene Vigdorchik - */ - -package scala.tools.nsc -package interactive - -import doc.base._ -import comment._ -import scala.xml.NodeSeq - -sealed trait DocResult -final case class UrlResult(url: String) extends DocResult -final case class HtmlResult(comment: Comment) extends DocResult - -abstract class Doc(val settings: doc.Settings) extends MemberLookupBase with CommentFactoryBase { - - override val global: interactive.Global - import global._ - - def chooseLink(links: List[LinkTo]): LinkTo - - override def internalLink(sym: Symbol, site: Symbol): Option[LinkTo] = - ask { () => - if (sym.isClass || sym.isModule) - Some(LinkToTpl(sym)) - else - if ((site.isClass || site.isModule) && site.info.members.toList.contains(sym)) - Some(LinkToMember(sym, site)) - else - None - } - - override def toString(link: LinkTo) = ask { () => - link match { - case LinkToMember(mbr: Symbol, site: Symbol) => - mbr.signatureString + " in " + site.toString - case LinkToTpl(sym: Symbol) => sym.toString - case _ => link.toString - } - } - - def retrieve(sym: Symbol, site: Symbol): Option[DocResult] = { - val sig = ask { () => externalSignature(sym) } - findExternalLink(sym, sig) map { link => UrlResult(link.url) } orElse { - val resp = new Response[Tree] - // Ensure docComment tree is type-checked. - val pos = ask { () => docCommentPos(sym) } - askTypeAt(pos, resp) - resp.get.left.toOption flatMap { _ => - ask { () => - val comment = parseAtSymbol(expandedDocComment(sym), rawDocComment(sym), pos, Some(site)) - Some(HtmlResult(comment)) - } - } - } - } -} diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 4ab7b98b3d..105b0e4833 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -225,7 +225,10 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") /** Called from parser, which signals hereby that a method definition has been parsed. */ override def signalParseProgress(pos: Position) { - checkForMoreWork(pos) + // We only want to be interruptible when running on the PC thread. + if(onCompilerThread) { + checkForMoreWork(pos) + } } /** Called from typechecker, which signals hereby that a node has been completely typechecked. @@ -447,7 +450,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") */ @elidable(elidable.WARNING) override def assertCorrectThread() { - assert(initializing || (Thread.currentThread() eq compileRunner), + assert(initializing || onCompilerThread, "Race condition detected: You are running a presentation compiler method outside the PC thread.[phase: %s]".format(globalPhase) + " Please file a ticket with the current stack trace at https://www.assembla.com/spaces/scala-ide/support/tickets") } @@ -462,6 +465,9 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") compileRunner } + private def ensureUpToDate(unit: RichCompilationUnit) = + if (!unit.isUpToDate && unit.status != JustParsed) reset(unit) // reparse previously typechecked units. + /** Compile all loaded source files in the order given by `allSources`. */ private[interactive] final def backgroundCompile() { @@ -474,7 +480,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") // ensure all loaded units are parsed for (s <- allSources; unit <- getUnit(s)) { // checkForMoreWork(NoPosition) // disabled, as any work done here would be in an inconsistent state - if (!unit.isUpToDate && unit.status != JustParsed) reset(unit) // reparse previously typechecked units. + ensureUpToDate(unit) parseAndEnter(unit) serviceParsedEntered() } @@ -727,7 +733,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") try { debugLog("starting targeted type check") typeCheck(unit) - println("tree not found at "+pos) +// println("tree not found at "+pos) EmptyTree } catch { case ex: TyperResult => new Locator(pos) locateIn ex.tree @@ -758,64 +764,69 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") respond(response)(typedTree(source, forceReload)) } - /** Implements CompilerControl.askLinkPos */ - private[interactive] def getLinkPos(sym: Symbol, source: SourceFile, response: Response[Position]) { + private def withTempUnit[T](source: SourceFile)(f: RichCompilationUnit => T): T = + getUnit(source) match { + case None => + reloadSources(List(source)) + try f(getUnit(source).get) + finally afterRunRemoveUnitOf(source) + case Some(unit) => + f(unit) + } - /** Find position of symbol `sym` in unit `unit`. Pre: `unit is loaded. */ - def findLinkPos(unit: RichCompilationUnit): Position = { - val originalTypeParams = sym.owner.typeParams - parseAndEnter(unit) - val pre = adaptToNewRunMap(ThisType(sym.owner)) - val rawsym = pre.typeSymbol.info.decl(sym.name) - val newsym = rawsym filter { alt => - sym.isType || { - try { - val tp1 = pre.memberType(alt) onTypeError NoType - val tp2 = adaptToNewRunMap(sym.tpe) substSym (originalTypeParams, sym.owner.typeParams) - matchesType(tp1, tp2, false) || { - debugLog(s"getLinkPos matchesType($tp1, $tp2) failed") - val tp3 = adaptToNewRunMap(sym.tpe) substSym (originalTypeParams, alt.owner.typeParams) - matchesType(tp1, tp3, false) || { - debugLog(s"getLinkPos fallback matchesType($tp1, $tp3) failed") - false - } - } - } - catch { - case ex: ControlThrowable => throw ex - case ex: Throwable => - println("error in hyperlinking: " + ex) - ex.printStackTrace() + /** Find a 'mirror' of symbol `sym` in unit `unit`. Pre: `unit is loaded. */ + private def findMirrorSymbol(sym: Symbol, unit: RichCompilationUnit): Symbol = { + val originalTypeParams = sym.owner.typeParams + ensureUpToDate(unit) + parseAndEnter(unit) + val pre = adaptToNewRunMap(ThisType(sym.owner)) + val rawsym = pre.typeSymbol.info.decl(sym.name) + val newsym = rawsym filter { alt => + sym.isType || { + try { + val tp1 = pre.memberType(alt) onTypeError NoType + val tp2 = adaptToNewRunMap(sym.tpe) substSym (originalTypeParams, sym.owner.typeParams) + matchesType(tp1, tp2, false) || { + debugLog(s"findMirrorSymbol matchesType($tp1, $tp2) failed") + val tp3 = adaptToNewRunMap(sym.tpe) substSym (originalTypeParams, alt.owner.typeParams) + matchesType(tp1, tp3, false) || { + debugLog(s"findMirrorSymbol fallback matchesType($tp1, $tp3) failed") false + } } } - } - if (newsym == NoSymbol) { - if (rawsym.exists && !rawsym.isOverloaded) rawsym.pos - else { - debugLog("link not found " + sym + " " + source + " " + pre) - NoPosition + catch { + case ex: ControlThrowable => throw ex + case ex: Throwable => + debugLog("error in findMirrorSymbol: " + ex) + ex.printStackTrace() + false } - } else if (newsym.isOverloaded) { - settings.uniqid.value = true - debugLog("link ambiguous " + sym + " " + source + " " + pre + " " + newsym.alternatives) - NoPosition - } else { - debugLog("link found for " + newsym + ": " + newsym.pos) - newsym.pos } } + if (newsym == NoSymbol) { + if (rawsym.exists && !rawsym.isOverloaded) rawsym + else { + debugLog("mirror not found " + sym + " " + unit.source + " " + pre) + NoSymbol + } + } else if (newsym.isOverloaded) { + settings.uniqid.value = true + debugLog("mirror ambiguous " + sym + " " + unit.source + " " + pre + " " + newsym.alternatives) + NoSymbol + } else { + debugLog("mirror found for " + newsym + ": " + newsym.pos) + newsym + } + } + /** Implements CompilerControl.askLinkPos */ + private[interactive] def getLinkPos(sym: Symbol, source: SourceFile, response: Response[Position]) { informIDE("getLinkPos "+sym+" "+source) respond(response) { if (sym.owner.isClass) { - getUnit(source) match { - case None => - reloadSources(List(source)) - try findLinkPos(getUnit(source).get) - finally afterRunRemoveUnitOf(source) - case Some(unit) => - findLinkPos(unit) + withTempUnit(source){ u => + findMirrorSymbol(sym, u).pos } } else { debugLog("link not in class "+sym+" "+source+" "+sym.owner) @@ -824,6 +835,50 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } } + /** Implements CompilerControl.askDocComment */ + private[interactive] def getDocComment(sym: Symbol, site: Symbol, source: SourceFile, response: Response[(String, String, Position)]) { + informIDE("getDocComment "+sym+" "+source) + respond(response) { + withTempUnit(source){ u => + val mirror = findMirrorSymbol(sym, u) + if (mirror eq NoSymbol) + ("", "", NoPosition) + else { + forceDocComment(mirror, u) + (expandedDocComment(mirror), rawDocComment(mirror), docCommentPos(mirror)) + } + } + } + } + + private def forceDocComment(sym: Symbol, unit: RichCompilationUnit) { + // Either typer has been run and we don't find DocDef, + // or we force the targeted typecheck here. + // In both cases doc comment maps should be filled for the subject symbol. + val docTree = + unit.body find { + case DocDef(_, defn) if defn.symbol eq sym => true + case _ => false + } + + for (t <- docTree) { + debugLog("Found DocDef tree for "+sym) + // Cannot get a typed tree at position since DocDef range is transparent. + val prevPos = unit.targetPos + val prevInterruptsEnabled = interruptsEnabled + try { + unit.targetPos = t.pos + interruptsEnabled = true + typeCheck(unit) + } catch { + case _: TyperResult => // ignore since we are after the side effect. + } finally { + unit.targetPos = prevPos + interruptsEnabled = prevInterruptsEnabled + } + } + } + def stabilizedType(tree: Tree): Type = tree match { case Ident(_) if tree.symbol.isStable => singleType(NoPrefix, tree.symbol) diff --git a/src/compiler/scala/tools/nsc/interactive/Picklers.scala b/src/compiler/scala/tools/nsc/interactive/Picklers.scala index ffad19fbaa..84cb03c140 100644 --- a/src/compiler/scala/tools/nsc/interactive/Picklers.scala +++ b/src/compiler/scala/tools/nsc/interactive/Picklers.scala @@ -165,6 +165,11 @@ trait Picklers { self: Global => .wrapped { case sym ~ source => new AskLinkPosItem(sym, source, new Response) } { item => item.sym ~ item.source } .asClass (classOf[AskLinkPosItem]) + implicit def askDocCommentItem: CondPickler[AskDocCommentItem] = + (pkl[Symbol] ~ pkl[Symbol] ~ pkl[SourceFile]) + .wrapped { case sym ~ site ~ source => new AskDocCommentItem(sym, site, source, new Response) } { item => item.sym ~ item.site ~ item.source } + .asClass (classOf[AskDocCommentItem]) + implicit def askLoadedTypedItem: CondPickler[AskLoadedTypedItem] = pkl[SourceFile] .wrapped { source => new AskLoadedTypedItem(source, new Response) } { _.source } @@ -182,5 +187,5 @@ trait Picklers { self: Global => implicit def action: Pickler[() => Unit] = reloadItem | askTypeAtItem | askTypeItem | askTypeCompletionItem | askScopeCompletionItem | - askToDoFirstItem | askLinkPosItem | askLoadedTypedItem | askParsedEnteredItem | emptyAction + askToDoFirstItem | askLinkPosItem | askDocCommentItem | askLoadedTypedItem | askParsedEnteredItem | emptyAction } diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala index ea2333a65b..7bb2b7b7c8 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala @@ -8,7 +8,8 @@ import scala.reflect.internal.util.Position /** Trait encapsulating the creation of a presentation compiler's instance.*/ private[tests] trait PresentationCompilerInstance extends TestSettings { protected val settings = new Settings - protected def docSettings: doc.Settings = new doc.Settings(_ => ()) + protected val docSettings = new doc.Settings(_ => ()) + protected val compilerReporter: CompilerReporter = new InteractiveReporter { override def compiler = PresentationCompilerInstance.this.compiler } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 72ad84edec..4ffd198dc4 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -2515,7 +2515,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => } override def outerSource: Symbol = - if (originalName == nme.OUTER) initialize.referenced + // SI-6888 Approximate the name to workaround the deficiencies in `nme.originalName` + // in the face of clases named '$'. SI-2806 remains open to address the deeper problem. + if (originalName endsWith (nme.OUTER)) initialize.referenced else NoSymbol def setModuleClass(clazz: Symbol): TermSymbol = { diff --git a/test/files/presentation/doc.check b/test/files/presentation/doc.check index 62b3bfc2e3..e33756773d 100755..100644 --- a/test/files/presentation/doc.check +++ b/test/files/presentation/doc.check @@ -1,3 +1,4 @@ +reload: Test.scala body:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(This is a test comment), Text(.)))), Text( )))))) @example:Body(List(Paragraph(Chain(List(Summary(Monospace(Text("abb".permutations = Iterator(abb, bab, bba))))))))) diff --git a/test/files/presentation/doc.scala b/test/files/presentation/doc/doc.scala index 4b0d6baa1f..475d92b861 100755 --- a/test/files/presentation/doc.scala +++ b/test/files/presentation/doc/doc.scala @@ -1,5 +1,5 @@ import scala.tools.nsc.doc -import scala.tools.nsc.doc.base.LinkTo +import scala.tools.nsc.doc.base._ import scala.tools.nsc.doc.base.comment._ import scala.tools.nsc.interactive._ import scala.tools.nsc.interactive.tests._ @@ -28,12 +28,33 @@ object Test extends InteractiveTest { |trait Commented {} |class User(c: %sCommented)""".stripMargin.format(comment, tags take nTags mkString "\n", caret) - override def main(args: Array[String]) { - val documenter = new Doc(settings) { - val global: compiler.type = compiler - + override lazy val compiler = { + new { + override val settings = { + prepareSettings(Test.this.settings) + Test.this.settings + } + } with Global(settings, compilerReporter) with MemberLookupBase with CommentFactoryBase { + val global: this.type = this def chooseLink(links: List[LinkTo]): LinkTo = links.head + def internalLink(sym: Symbol, site: Symbol) = None + def toString(link: LinkTo) = link.toString + + def getComment(sym: Symbol, source: SourceFile) = { + val docResponse = new Response[(String, String, Position)] + askDocComment(sym, sym.owner, source, docResponse) + docResponse.get.left.toOption flatMap { + case (expanded, raw, pos) => + if (expanded.isEmpty) + None + else + Some(ask { () => parseAtSymbol(expanded, raw, pos, Some(sym.owner)) }) + } + } } + } + + override def runDefaultTests() { for (i <- 1 to tags.length) { val markedText = text(i) val idx = markedText.indexOf(caret) @@ -52,18 +73,17 @@ object Test extends InteractiveTest { treeResponse.get.left.toOption match { case Some(tree) => val sym = tree.tpe.typeSymbol - documenter.retrieve(sym, sym.owner) match { - case Some(HtmlResult(comment)) => - import comment._ - val tags: List[(String, Iterable[Body])] = - List(("@example", example), ("@version", version), ("@since", since.toList), ("@todo", todo), ("@note", note), ("@see", see)) - val str = ("body:" + body + "\n") + - tags.map{ case (name, bodies) => name + ":" + bodies.mkString("\n") }.mkString("\n") - reporter.println(str) - case Some(_) => reporter.println("Got unexpected result") - case None => reporter.println("Got no result") + compiler.getComment(sym, batch) match { + case None => println("Got no doc comment") + case Some(comment) => + import comment._ + val tags: List[(String, Iterable[Body])] = + List(("@example", example), ("@version", version), ("@since", since.toList), ("@todo", todo), ("@note", note), ("@see", see)) + val str = ("body:" + body + "\n") + + tags.map{ case (name, bodies) => name + ":" + bodies.mkString("\n") }.mkString("\n") + println(str) } - case None => reporter.println("Couldn't find a typedTree") + case None => println("Couldn't find a typedTree") } } } diff --git a/test/files/presentation/ide-t1001326.check b/test/files/presentation/ide-t1001326.check new file mode 100644 index 0000000000..0ac15faed4 --- /dev/null +++ b/test/files/presentation/ide-t1001326.check @@ -0,0 +1,4 @@ +Unique OK +Unattributed OK +NeverModify OK +AlwaysParseTree OK
\ No newline at end of file diff --git a/test/files/presentation/ide-t1001326/Test.scala b/test/files/presentation/ide-t1001326/Test.scala new file mode 100644 index 0000000000..3091da4b40 --- /dev/null +++ b/test/files/presentation/ide-t1001326/Test.scala @@ -0,0 +1,91 @@ +import scala.tools.nsc.interactive.tests.InteractiveTest +import scala.reflect.internal.util.SourceFile +import scala.tools.nsc.interactive.Response + +object Test extends InteractiveTest { + + override def execute(): Unit = { + val sf = sourceFiles.find(_.file.name == "A.scala").head + uniqueParseTree_t1001326(sf) + unattributedParseTree_t1001326(sf) + neverModifyParseTree_t1001326(sf) + shouldAlwaysReturnParseTree_t1001326(sf) + } + + /** + * Asking twice for a parseTree on the same source should always return a new tree + */ + private def uniqueParseTree_t1001326(sf: SourceFile) { + val parseTree1 = compiler.parseTree(sf) + val parseTree2 = compiler.parseTree(sf) + if (parseTree1 != parseTree2) { + reporter.println("Unique OK") + } else { + reporter.println("Unique FAILED") + } + } + + /** + * A parseTree should never contain any symbols or types + */ + private def unattributedParseTree_t1001326(sf: SourceFile) { + if (noSymbolsOrTypes(compiler.parseTree(sf))) { + reporter.println("Unattributed OK") + } else { + reporter.println("Unattributed FAILED") + } + } + + /** + * Once you have obtained a parseTree it should never change + */ + private def neverModifyParseTree_t1001326(sf: SourceFile) { + val parsedTree = compiler.parseTree(sf) + loadSourceAndWaitUntilTypechecked(sf) + if (noSymbolsOrTypes(parsedTree)) { + reporter.println("NeverModify OK") + } else { + reporter.println("NeverModify FAILED") + } + } + + /** + * Should always return a parse tree + */ + private def shouldAlwaysReturnParseTree_t1001326(sf: SourceFile) { + loadSourceAndWaitUntilTypechecked(sf) + if (noSymbolsOrTypes(compiler.parseTree(sf))) { + reporter.println("AlwaysParseTree OK") + } else { + reporter.println("AlwaysParseTree FAILED") + } + } + + /** + * Load a source and block while it is type-checking. + */ + private def loadSourceAndWaitUntilTypechecked(sf: SourceFile): Unit = { + compiler.askToDoFirst(sf) + val res = new Response[Unit] + compiler.askReload(List(sf), res) + res.get + askLoadedTyped(sf).get + } + + /** + * Traverses a tree and makes sure that there are no types or symbols present in the tree with + * the exception of the symbol for the package 'scala'. This is because that symbol will be + * present in some of the nodes that the compiler generates. + */ + private def noSymbolsOrTypes(tree: compiler.Tree): Boolean = { + tree.forAll { t => + (t.symbol == null || + t.symbol == compiler.NoSymbol || + t.symbol == compiler.definitions.ScalaPackage // ignore the symbol for the scala package for now + ) && ( + t.tpe == null || + t.tpe == compiler.NoType) + } + } + +}
\ No newline at end of file diff --git a/test/files/presentation/ide-t1001326/src/a/A.scala b/test/files/presentation/ide-t1001326/src/a/A.scala new file mode 100644 index 0000000000..c82ca02231 --- /dev/null +++ b/test/files/presentation/ide-t1001326/src/a/A.scala @@ -0,0 +1,5 @@ +package a + +class A { + def foo(s: String) = s + s +}
\ No newline at end of file diff --git a/test/files/run/t6113.check b/test/files/run/t6113.check new file mode 100644 index 0000000000..65fb3cd090 --- /dev/null +++ b/test/files/run/t6113.check @@ -0,0 +1 @@ +Foo[[X](Int, X)] diff --git a/test/files/run/t6113.scala b/test/files/run/t6113.scala new file mode 100644 index 0000000000..321cae86a3 --- /dev/null +++ b/test/files/run/t6113.scala @@ -0,0 +1,6 @@ +trait Foo[C[_]] + +object Test extends App { + import scala.reflect.runtime.universe._ + println(typeOf[Foo[({type l[X] = (Int, X)})#l]]) +}
\ No newline at end of file diff --git a/test/files/run/t6888.check b/test/files/run/t6888.check new file mode 100644 index 0000000000..4e8a2de2db --- /dev/null +++ b/test/files/run/t6888.check @@ -0,0 +1,3 @@ +2 +3 +3 diff --git a/test/files/run/t6888.scala b/test/files/run/t6888.scala new file mode 100644 index 0000000000..0c64cbe5b6 --- /dev/null +++ b/test/files/run/t6888.scala @@ -0,0 +1,19 @@ +class C { + val x = 1 + object $ { + val y = x + x + class abc$ { + def xy = x + y + } + object abc$ { + def xy = x + y + } + } +} + +object Test extends App { + val c = new C() + println(c.$.y) + println(c.$.abc$.xy) + println(new c.$.abc$().xy) +} |