Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 11a33e4

Browse files
committed
PC invalidates previous work on new run
The "up-to-date" check is not sufficient when fetching doc comments, since a unit may have been typechecked, with doc wrapper trees removed. A test instability was due to whether the scheduler was checked by a callback or the runner thread. One sample file had no parser callbacks at all.
1 parent 1c9aa59 commit 11a33e4

File tree

11 files changed

+159
-88
lines changed

11 files changed

+159
-88
lines changed

src/compiler/scala/tools/nsc/Global.scala

+4-3
Original file line numberDiff line numberDiff line change
@@ -1691,10 +1691,11 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
16911691
def compileLate(unit: CompilationUnit): Unit = {
16921692
addUnit(unit)
16931693

1694-
if (firstPhase ne null) { // we might get here during initialization, is a source is newer than the binary
1694+
if (firstPhase ne null) { // we might get here during initialization, if a source is newer than the binary
16951695
val maxId = math.max(globalPhase.id, typerPhase.id)
1696-
firstPhase.iterator takeWhile (_.id < maxId) foreach (ph =>
1697-
enteringPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit))
1696+
firstPhase.iterator.takeWhile(_.id < maxId).foreach { ph =>
1697+
enteringPhase(ph)(ph.asInstanceOf[GlobalPhase].applyPhase(unit))
1698+
}
16981699
refreshProgress()
16991700
}
17001701
}

src/interactive/scala/tools/nsc/interactive/Global.scala

+30-26
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
209209
/** A map that associates with each abstract file the set of responses that ware waiting
210210
* (via build) for the unit associated with the abstract file to be parsed and entered
211211
*/
212-
protected var getParsedEnteredResponses = newResponseMap
212+
protected val getParsedEnteredResponses = newResponseMap
213213

214214
private def cleanResponses(rmap: ResponseMap): Unit = {
215215
for ((source, rs) <- rmap.toList) {
@@ -301,6 +301,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
301301
def isOutOfDate: Boolean = outOfDate
302302

303303
def demandNewCompilerRun() = {
304+
if (!lastWasReload) allSources.foreach(getUnit(_).foreach(reset(_)))
304305
if (outOfDate) throw new FreshRunReq // cancel background compile
305306
else outOfDate = true // proceed normally and enable new background compile
306307
}
@@ -428,13 +429,14 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
428429
* @param pos The position of the tree if polling while typechecking, NoPosition otherwise
429430
*
430431
*/
432+
@nowarn("cat=lint-nonlocal-return")
431433
private[interactive] def pollForWork(pos: Position): Unit = {
432434
var loop: Boolean = true
433435
while (loop) {
434-
breakable{
436+
breakable {
435437
loop = false
436438
// TODO refactor to eliminate breakable/break/return?
437-
(if (!interruptsEnabled) return): @nowarn("cat=lint-nonlocal-return")
439+
if (!interruptsEnabled) return
438440
if (pos == NoPosition || nodesSeen % yieldPeriod == 0)
439441
Thread.`yield`()
440442

@@ -447,7 +449,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
447449
case Some(WorkEvent(id, _)) =>
448450
debugLog("some work at node "+id+" current = "+nodesSeen)
449451
// assert(id >= nodesSeen)
450-
moreWorkAtNode = id
452+
moreWorkAtNode = id
451453
case None =>
452454
}
453455

@@ -463,7 +465,8 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
463465
debugLog("ask finished"+timeStep)
464466
interruptsEnabled = true
465467
}
466-
loop = true; break()
468+
loop = true
469+
break()
467470
case _ =>
468471
}
469472

@@ -474,8 +477,8 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
474477
logreplay("exception thrown", scheduler.pollThrowable()) match {
475478
case Some(ex: FreshRunReq) =>
476479
newTyperRun()
477-
minRunId = currentRunId
478-
demandNewCompilerRun()
480+
minRunId = currentRunId
481+
demandNewCompilerRun()
479482

480483
case Some(ShutdownReq) =>
481484
scheduler.synchronized { // lock the work queue so no more items are posted while we clean it up
@@ -497,7 +500,9 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
497500
throw ShutdownReq
498501
}
499502

500-
case Some(ex: Throwable) => log.flush(); throw ex
503+
case Some(ex: Throwable) =>
504+
log.flush()
505+
throw ex
501506
case _ =>
502507
}
503508

@@ -562,7 +567,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
562567
reporter.reset()
563568

564569
// remove any files in first that are no longer maintained by presentation compiler (i.e. closed)
565-
allSources = allSources filter (s => unitOfFile contains (s.file))
570+
allSources = allSources.filter(s => unitOfFile.contains(s.file))
566571

567572
// ensure all loaded units are parsed
568573
for (s <- allSources; unit <- getUnit(s)) {
@@ -582,7 +587,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
582587
}
583588

584589
// ensure all loaded units are typechecked
585-
for (s <- allSources; if !ignoredFiles(s.file); unit <- getUnit(s)) {
590+
for (s <- allSources if !ignoredFiles(s.file); unit <- getUnit(s))
586591
try {
587592
if (!unit.isUpToDate)
588593
if (unit.problems.isEmpty || !settings.YpresentationStrict.value)
@@ -610,7 +615,6 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
610615

611616
reporter.error(unit.body.pos, "Presentation compiler crashed while type checking this file: %s".format(ex.toString()))
612617
}
613-
}
614618

615619
// move units removable after this run to the "to-be-removed" buffer
616620
toBeRemoved.synchronized {
@@ -719,9 +723,11 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
719723
val result = results.head
720724
results = results.tail
721725
if (results.isEmpty) {
722-
response set result
726+
response.set(result)
723727
debugLog("responded"+timeStep)
724-
} else response setProvisionally result
728+
}
729+
else
730+
response.setProvisionally(result)
725731
}
726732
}
727733
} catch {
@@ -859,15 +865,13 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
859865

860866
private def withTempUnits[T](sources: List[SourceFile])(f: (SourceFile => RichCompilationUnit) => T): T = {
861867
val unitOfSrc: SourceFile => RichCompilationUnit = src => unitOfFile(src.file)
862-
sources filterNot (getUnit(_).isDefined) match {
868+
sources.filterNot(getUnit(_).isDefined) match {
863869
case Nil =>
864870
f(unitOfSrc)
865871
case unknown =>
866872
reloadSources(unknown)
867-
try {
868-
f(unitOfSrc)
869-
} finally
870-
afterRunRemoveUnitsOf(unknown)
873+
try f(unitOfSrc)
874+
finally afterRunRemoveUnitsOf(unknown)
871875
}
872876
}
873877

@@ -948,21 +952,21 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
948952
}
949953

950954
/** Implements CompilerControl.askDocComment */
951-
private[interactive] def getDocComment(sym: Symbol, source: SourceFile, site: Symbol, fragments: List[(Symbol,SourceFile)],
955+
private[interactive] def getDocComment(sym: Symbol, source: SourceFile, site: Symbol,
956+
fragments: List[(Symbol, SourceFile)],
952957
response: Response[(String, String, Position)]): Unit = {
953958
informIDE(s"getDocComment $sym at $source, site $site")
954959
respond(response) {
955-
withTempUnits(fragments.unzip._2){ units =>
956-
for((sym, src) <- fragments) {
957-
val mirror = findMirrorSymbol(sym, units(src))
958-
if (mirror ne NoSymbol) forceDocComment(mirror, units(src))
960+
withTempUnits(fragments.unzip._2) { unitForSrc =>
961+
for ((sym, src) <- fragments) {
962+
val mirror = findMirrorSymbol(sym, unitForSrc(src))
963+
if (mirror ne NoSymbol) forceDocComment(mirror, unitForSrc(src))
959964
}
960-
val mirror = findMirrorSymbol(sym, units(source))
965+
val mirror = findMirrorSymbol(sym, unitForSrc(source))
961966
if (mirror eq NoSymbol)
962967
("", "", NoPosition)
963-
else {
968+
else
964969
(expandedDocComment(mirror, site), rawDocComment(mirror), docCommentPos(mirror))
965-
}
966970
}
967971
}
968972
// New typer run to remove temp units and drop per-run caches that might refer to symbols entered from temp units.

src/interactive/scala/tools/nsc/interactive/Response.scala

+9
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,13 @@ class Response[T] {
112112
complete = false
113113
cancelled = false
114114
}
115+
116+
override def toString =
117+
if (!isComplete) "Response incomplete"
118+
else if (isCancelled) "Response cancelled"
119+
else get(0L) match {
120+
case None => "Response has no provisional result"
121+
case Some(Right(t)) => s"Response failed: ${t.getMessage}"
122+
case Some(Left(data)) => s"Response data: ${data}"
123+
}
115124
}

src/interactive/scala/tools/nsc/interactive/tests/InteractiveTest.scala

+2-6
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,15 @@ abstract class InteractiveTest
7575
protected def ++(tests: PresentationCompilerTestDef*): Unit = testActions ++= tests
7676

7777
/** Test's entry point */
78-
def main(args: Array[String]): Unit = {
79-
try execute()
80-
finally askShutdown()
81-
}
78+
def main(args: Array[String]): Unit = try execute() finally askShutdown()
8279

83-
protected def execute(): Unit = {
80+
protected def execute(): Unit =
8481
util.stringFromStream { ostream =>
8582
Console.withOut(ostream) {
8683
loadSources()
8784
runDefaultTests()
8885
}
8986
}.linesIterator.filterNot(filterOutLines).map(normalize).foreach(println)
90-
}
9187

9288
protected def filterOutLines(line: String) = false
9389
protected def normalize(s: String) = s

src/interactive/scala/tools/nsc/interactive/tests/core/AskCommand.scala

+7-4
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@ trait AskParse extends AskCommand {
5353
/** `sources` need to be entirely parsed before running the test
5454
* (else commands such as `AskTypeCompletionAt` may fail simply because
5555
* the source's AST is not yet loaded).
56+
*
57+
* Submit each parse job and get the response sequentially;
58+
* otherwise, parser progress update will run the next job.
5659
*/
57-
def askParse(sources: Seq[SourceFile]): Unit = {
58-
val responses = sources map (askParse(_))
59-
responses.foreach(_.get) // force source files parsing
60-
}
60+
def askParse(sources: Seq[SourceFile]): Unit =
61+
for (source <- sources) {
62+
val _ = askParse(source).get
63+
}
6164

6265
private def askParse(src: SourceFile, keepLoaded: Boolean = true): Response[Tree] = {
6366
ask {

test/files/presentation/doc.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
reload: Base.scala, Class.scala, Derived.scala
1+
reload: Class.scala

test/files/presentation/doc/doc.scala

+15-48
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//> using options -Xlint -Werror
2-
import scala.reflect.internal.util.{ BatchSourceFile, SourceFile }
3-
import scala.tools.nsc.doc
4-
import scala.tools.nsc.doc.base._
5-
import scala.tools.nsc.doc.base.comment._
2+
import scala.reflect.internal.util.{BatchSourceFile, SourceFile}
3+
import scala.tools.nsc.doc.{ScaladocAnalyzer, ScaladocGlobalTrait}
4+
import scala.tools.nsc.doc.base.{CommentFactoryBase, LinkTo, MemberLookupBase}
5+
import scala.tools.nsc.doc.base.comment.{Body, Comment}
66
import scala.tools.nsc.interactive._
77
import scala.tools.nsc.interactive.tests._
88

@@ -38,17 +38,17 @@ object Test extends InteractiveTest {
3838
prepre + docComment(nTags) + prepost + post
3939
}
4040

41-
override lazy val compiler: Global { def getComment(sym: Symbol, source: SourceFile, fragments: List[(Symbol,SourceFile)]): Option[Comment] } = {
41+
override lazy val compiler: Global { def getComment(sym: Symbol, source: SourceFile, fragments: List[(Symbol, SourceFile)]): Option[Comment] } = {
4242
prepareSettings(settings)
43-
new Global(settings, compilerReporter) with MemberLookupBase with CommentFactoryBase with doc.ScaladocGlobalTrait {
43+
new Global(settings, compilerReporter) with MemberLookupBase with CommentFactoryBase with ScaladocGlobalTrait {
4444
outer =>
4545

4646
val global: this.type = this
4747

4848
@annotation.nowarn
4949
override lazy val analyzer = new {
5050
val global: outer.type = outer
51-
} with doc.ScaladocAnalyzer with InteractiveAnalyzer {
51+
} with ScaladocAnalyzer with InteractiveAnalyzer {
5252
override def newTyper(context: Context): InteractiveTyper with ScaladocTyper =
5353
new Typer(context) with InteractiveTyper with ScaladocTyper
5454
}
@@ -59,7 +59,7 @@ object Test extends InteractiveTest {
5959
def warnNoLink = false
6060
def findExternalLink(sym: Symbol, name: String) = None
6161

62-
def getComment(sym: Symbol, source: SourceFile, fragments: List[(Symbol,SourceFile)]): Option[Comment] = {
62+
def getComment(sym: Symbol, source: SourceFile, fragments: List[(Symbol, SourceFile)]): Option[Comment] = {
6363
val docResponse = new Response[(String, String, Position)]
6464
askDocComment(sym, source, sym.owner, fragments, docResponse)
6565
docResponse.get.swap.toOption flatMap {
@@ -74,12 +74,11 @@ object Test extends InteractiveTest {
7474
}
7575

7676
override def runDefaultTests(): Unit = {
77-
import compiler._
77+
import compiler.{NoSymbol, TermName, Tree, TypeName, askParsedEntered, getComment}
7878
def findSource(name: String) = sourceFiles.find(_.file.name == name).get
7979

8080
val className = names.head
81-
for (name <- names;
82-
i <- 1 to tags.length) {
81+
for (name <- names; i <- 1 to tags.length) {
8382
val newText = text(name, i)
8483
val source = findSource("Class.scala")
8584
val batch = new BatchSourceFile(source.file, newText.toCharArray)
@@ -100,12 +99,13 @@ object Test extends InteractiveTest {
10099
if (toplevel eq NoSymbol) {
101100
val clazz = compiler.rootMirror.EmptyPackage.info.decl(TypeName(className))
102101
val term = clazz.info.decl(TermName(name))
103-
if (term eq NoSymbol) clazz.info.decl(TypeName(name)) else
104-
if (term.isAccessor) term.accessed else term
105-
} else toplevel
102+
if (term eq NoSymbol) clazz.info.decl(TypeName(name))
103+
else if (term.isAccessor) term.accessed else term
104+
}
105+
else toplevel
106106
}
107107

108-
getComment(sym, batch, (sym,batch)::Nil) match {
108+
getComment(sym, batch, sym -> batch :: Nil) match {
109109
case None => println(s"Got no doc comment for $name")
110110
case Some(comment) =>
111111
import comment._
@@ -117,38 +117,5 @@ object Test extends InteractiveTest {
117117
}
118118
}
119119
}
120-
121-
// The remainder of this test has been found to fail intermittently on Windows
122-
// only. The problem is difficult to isolate and reproduce; see
123-
// https://github.com/scala/scala-dev/issues/72 for details.
124-
// So if we're on Windows, let's just bail out here.
125-
if (scala.util.Properties.isWin) return
126-
127-
// Check inter-classes documentation one-time retrieved ok.
128-
val baseSource = findSource("Base.scala")
129-
val derivedSource = findSource("Derived.scala")
130-
def existsText(where: Any, text: String): Boolean = where match {
131-
case s: String => s contains text
132-
case s: Seq[_] => s exists (existsText(_, text))
133-
case p: Product => p.productIterator exists (existsText(_, text))
134-
case c: Comment => existsText(c.body, text)
135-
case x => throw new MatchError(x)
136-
}
137-
val (derived, base) = compiler.ask { () =>
138-
val derived = compiler.rootMirror.RootPackage.info.decl(newTermName("p")).info.decl(newTypeName("Derived"))
139-
(derived, derived.ancestors(0))
140-
}
141-
val cmt1 = getComment(derived, derivedSource, (base, baseSource)::(derived, derivedSource)::Nil)
142-
if (!existsText(cmt1, "This is Derived comment"))
143-
println("Unexpected Derived class comment:"+cmt1)
144-
145-
val (fooDerived, fooBase) = compiler.ask { () =>
146-
val decl = derived.tpe.decl(newTermName("foo"))
147-
(decl, decl.allOverriddenSymbols(0))
148-
}
149-
150-
val cmt2 = getComment(fooDerived, derivedSource, (fooBase, baseSource)::(fooDerived, derivedSource)::Nil)
151-
if (!existsText(cmt2, "Base method has documentation"))
152-
println("Unexpected foo method comment:"+cmt2)
153120
}
154121
}

test/files/presentation/dock.check

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
reload: Base.scala, Derived.scala

0 commit comments

Comments
 (0)