From b79f961ff6f911be2056a0adcbd085d79fd6f2a7 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 27 Feb 2019 12:10:39 +0100 Subject: [PATCH] IDE: Guard against out of bounds positions in requests This can happen when a line is removed at the end of a file immediately after a request on the removed line and the messages are processed out of order on the server. --- compiler/src/dotty/tools/dotc/util/SourceFile.scala | 7 +++++++ .../tools/languageserver/DottyLanguageServer.scala | 13 +++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/util/SourceFile.scala b/compiler/src/dotty/tools/dotc/util/SourceFile.scala index 4600cfcd47a3..c765999cc133 100644 --- a/compiler/src/dotty/tools/dotc/util/SourceFile.scala +++ b/compiler/src/dotty/tools/dotc/util/SourceFile.scala @@ -115,6 +115,13 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends /** Map line to offset of first character in line */ def lineToOffset(index: Int): Int = lineIndices(index) + /** Like `lineToOffset`, but doesn't crash if the index is out of bounds. */ + def lineToOffsetOpt(index: Int): Option[Int] = + if (index < 0 || index >= lineIndices.length) + None + else + Some(lineToOffset(index)) + /** A cache to speed up offsetToLine searches to similar lines */ private[this] var lastLine = 0 diff --git a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala index 34b84aa8df65..de2bc83042d8 100644 --- a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala +++ b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala @@ -651,10 +651,15 @@ object DottyLanguageServer { if (isWorksheet(uri)) toWrappedPosition(pos) else pos val source = driver.openedFiles(uri) - if (source.exists) { - val p = Spans.Span(source.lineToOffset(actualPosition.getLine) + actualPosition.getCharacter) - new SourcePosition(source, p) - } + if (source.exists) + source.lineToOffsetOpt(actualPosition.getLine).map(_ + actualPosition.getCharacter) match { + // `<=` to allow an offset to point to the end of the file + case Some(offset) if offset <= source.content().length => + val p = Spans.Span(offset) + new SourcePosition(source, p) + case _ => + NoSourcePosition + } else NoSourcePosition }