Skip to content

Commit b9f219b

Browse files
Fix #5895: REPL autocompletion crushes in certain cases
The crashes happen due to the fact that in certain cases, certain trees get mispositioned during code completion. E.g. this happens with `opaque type T = Int` or `object Foo { type T = Int`. The tree spans are set incorrectly because repl doesn't set the sources of these trees correctly. Precisely, when typechecking on code completion, a virtual source is used. However, when parsing for autocompletion, a line source is used which is created by the parsing logic. This commit makes sure that REPL is consistent in its source choices when computing code completions.
1 parent cab020d commit b9f219b

File tree

3 files changed

+14
-8
lines changed

3 files changed

+14
-8
lines changed

compiler/src/dotty/tools/repl/ParseResult.scala

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,15 @@ object ParseResult {
108108

109109
@sharable private[this] val CommandExtract = """(:[\S]+)\s*(.*)""".r
110110

111-
private def parseStats(sourceCode: String)(implicit ctx: Context): List[untpd.Tree] = {
111+
private def parseStats(implicit ctx: Context): List[untpd.Tree] = {
112112
val parser = new Parser(ctx.source)
113113
val stats = parser.blockStatSeq()
114114
parser.accept(Tokens.EOF)
115115
stats
116116
}
117117

118-
/** Extract a `ParseResult` from the string `sourceCode` */
119-
def apply(sourceCode: String)(implicit state: State): ParseResult =
118+
def apply(source: SourceFile)(implicit state: State): ParseResult = {
119+
val sourceCode = source.content().mkString
120120
sourceCode match {
121121
case "" => Newline
122122
case CommandExtract(cmd, arg) => cmd match {
@@ -132,10 +132,8 @@ object ParseResult {
132132
case _ =>
133133
implicit val ctx: Context = state.context
134134

135-
val source = SourceFile.virtual(str.REPL_SESSION_LINE + (state.objectIndex + 1), sourceCode)
136-
137135
val reporter = newStoreReporter
138-
val stats = parseStats(sourceCode)(state.context.fresh.setReporter(reporter).withSource(source))
136+
val stats = parseStats(state.context.fresh.setReporter(reporter).withSource(source))
139137

140138
if (reporter.hasErrors)
141139
SyntaxErrors(
@@ -145,6 +143,10 @@ object ParseResult {
145143
else
146144
Parsed(source, stats)
147145
}
146+
}
147+
148+
def apply(sourceCode: String)(implicit state: State): ParseResult =
149+
apply(SourceFile.virtual(str.REPL_SESSION_LINE + (state.objectIndex + 1), sourceCode))
148150

149151
/** Check if the input is incomplete
150152
*
@@ -160,7 +162,7 @@ object ParseResult {
160162
val localCtx = ctx.fresh.setSource(source).setReporter(reporter)
161163
var needsMore = false
162164
reporter.withIncompleteHandler((_, _) => needsMore = true) {
163-
parseStats(sourceCode)(localCtx)
165+
parseStats(localCtx)
164166
}
165167
!reporter.hasErrors && needsMore
166168
}

compiler/src/dotty/tools/repl/ReplCompiler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ class ReplCompiler extends Compiler {
241241
PackageDef(Ident(nme.EMPTY_PACKAGE), List(wrapper))
242242
}
243243

244-
ParseResult(expr)(state) match {
244+
ParseResult(sourceFile)(state) match {
245245
case Parsed(_, trees) =>
246246
wrap(trees).result
247247
case SyntaxErrors(_, reported, trees) =>

compiler/test/dotty/tools/repl/TabcompleteTests.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,8 @@ class TabcompleteTests extends ReplTest {
124124
val comp = tabComplete("???.")
125125
assertEquals(Nil, comp)
126126
}
127+
128+
@Test def moduleCompletion = fromInitialState { implicit s =>
129+
assertEquals(List("Predef"), tabComplete("object Foo { type T = Pre"))
130+
}
127131
}

0 commit comments

Comments
 (0)