Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SI-7026: parseTree should never return a typed one
This commit fixes ticket SI-7026. This makes it safe to use parseTree outside of the presentation compiler thread. Solved it with an implementation that just parses the source every time without trying to memorize anything. Added tests that checks that 1. You get a new parse tree every time you ask for one. 2. You always get a parse tree, never a typed tree. 3. A parse tree should never contain any symbols or types [1]. 4. If you ask for a parse tree and then ask for a typed tree it shouldn't change the parse tree you originally asked for, i.e. property 3 still holds. Additionally the parser is now only interruptible when running on the presentation compiler thread. [1] There is an exception to this though. Some of the nodes that the compiler generates will actually contain symbols. I've chosen to just ignore these special cases for now.
- Loading branch information
1 parent
5d65772
commit 79e774f
Showing
5 changed files
with
110 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,4 @@ | |||
Unique OK | |||
Unattributed OK | |||
NeverModify OK | |||
AlwaysParseTree OK |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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) | |||
} | |||
} | |||
|
|||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,5 @@ | |||
package a | |||
|
|||
class A { | |||
def foo(s: String) = s + s | |||
} |