Skip to content

Commit 159da44

Browse files
committed
Clean up SourceFile creation and error handling
1 parent 6f3fe05 commit 159da44

File tree

9 files changed

+32
-44
lines changed

9 files changed

+32
-44
lines changed

compiler/src/dotty/tools/dotc/CompilationUnit.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ object CompilationUnit {
104104
/** Make a compilation unit for top class `clsd` with the contents of the `unpickled` tree */
105105
def apply(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(using Context): CompilationUnit =
106106
val file = clsd.symbol.associatedFile.nn
107-
apply(new SourceFile(file, Array.empty[Char]), unpickled, forceTrees)
107+
apply(SourceFile(file, Array.empty[Char]), unpickled, forceTrees)
108108

109109
/** Make a compilation unit, given picked bytes and unpickled tree */
110110
def apply(source: SourceFile, unpickled: Tree, forceTrees: Boolean)(using Context): CompilationUnit = {

compiler/src/dotty/tools/dotc/Run.scala

+3-6
Original file line numberDiff line numberDiff line change
@@ -307,12 +307,9 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
307307
def compileFromStrings(scalaSources: List[String], javaSources: List[String] = Nil): Unit = {
308308
def sourceFile(source: String, isJava: Boolean): SourceFile = {
309309
val uuid = java.util.UUID.randomUUID().toString
310-
val ext = if (isJava) ".java" else ".scala"
311-
val virtualFile = new VirtualFile(s"compileFromString-$uuid.$ext")
312-
val writer = new BufferedWriter(new OutputStreamWriter(virtualFile.output, StandardCharsets.UTF_8.nn.name)) // buffering is still advised by javadoc
313-
writer.write(source)
314-
writer.close()
315-
new SourceFile(virtualFile, Codec.UTF8)
310+
val ext = if (isJava) "java" else "scala"
311+
val name = s"compileFromString-$uuid.$ext"
312+
SourceFile.virtual(name, source)
316313
}
317314
val sources =
318315
scalaSources.map(sourceFile(_, isJava = false)) ++

compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala

+2-8
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,8 @@ class InteractiveDriver(val settings: List[String]) extends Driver {
297297
cleanupTree(tree)
298298
}
299299

300-
private def toSource(uri: URI, sourceCode: String): SourceFile = {
301-
val path = Paths.get(uri)
302-
val virtualFile = new VirtualFile(path.getFileName.toString, path.toString)
303-
val writer = new BufferedWriter(new OutputStreamWriter(virtualFile.output, StandardCharsets.UTF_8.name))
304-
writer.write(sourceCode)
305-
writer.close()
306-
new SourceFile(virtualFile, Codec.UTF8)
307-
}
300+
private def toSource(uri: URI, sourceCode: String): SourceFile =
301+
SourceFile.virtual(Paths.get(uri).toString, sourceCode)
308302

309303
/**
310304
* Initialize this driver and compiler.

compiler/src/dotty/tools/dotc/util/SourceFile.scala

+13-19
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import Chars._
1313
import scala.annotation.internal.sharable
1414
import scala.collection.mutable
1515
import scala.collection.mutable.ArrayBuffer
16+
import scala.util.chaining.given
1617

17-
import java.io.IOException
1818
import java.nio.charset.StandardCharsets
19+
import java.nio.file.{FileSystemException, NoSuchFileException}
1920
import java.util.Optional
2021
import java.util.concurrent.atomic.AtomicInteger
2122
import java.util.regex.Pattern
@@ -71,15 +72,6 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends
7172

7273
def maybeIncomplete: Boolean = _maybeInComplete
7374

74-
def this(file: AbstractFile, codec: Codec) =
75-
// It would be cleaner to check if the file exists instead of catching
76-
// an exception, but it turns out that Files.exists is remarkably slow,
77-
// at least on Java 8 (https://rules.sonarsource.com/java/tag/performance/RSPEC-3725),
78-
// this is significant enough to show up in our benchmarks.
79-
this(file,
80-
try new String(file.toByteArray, codec.charSet).toCharArray
81-
catch case _: java.nio.file.NoSuchFileException => Array[Char]())
82-
8375
/** Tab increment; can be overridden */
8476
def tabInc: Int = 8
8577

@@ -226,9 +218,8 @@ object SourceFile {
226218
implicit def fromContext(using Context): SourceFile = ctx.source
227219

228220
def virtual(name: String, content: String, maybeIncomplete: Boolean = false) =
229-
val src = new SourceFile(new VirtualFile(name, content.getBytes(StandardCharsets.UTF_8)), scala.io.Codec.UTF8)
230-
src._maybeInComplete = maybeIncomplete
231-
src
221+
SourceFile(new VirtualFile(name, content.getBytes(StandardCharsets.UTF_8)), content.toCharArray)
222+
.tap(_._maybeInComplete = maybeIncomplete)
232223

233224
/** Returns the relative path of `source` within the `reference` path
234225
*
@@ -273,17 +264,20 @@ object SourceFile {
273264
ScriptSourceFile.hasScriptHeader(content)
274265

275266
def apply(file: AbstractFile | Null, codec: Codec): SourceFile =
276-
// see note above re: Files.exists is remarkably slow
267+
// Files.exists is slow on Java 8 (https://rules.sonarsource.com/java/tag/performance/RSPEC-3725),
268+
// so cope with failure; also deal with path prefix "Not a directory".
277269
val chars =
278-
try
279-
new String(file.toByteArray, codec.charSet).toCharArray
280-
catch
281-
case _: java.nio.file.NoSuchFileException => Array[Char]()
270+
try new String(file.toByteArray, codec.charSet).toCharArray
271+
catch
272+
case _: NoSuchFileException => Array.empty[Char]
273+
case fse: FileSystemException if fse.getMessage.endsWith("Not a directory") => Array.empty[Char]
282274

283275
if isScript(file, chars) then
284276
ScriptSourceFile(file, chars)
285277
else
286-
new SourceFile(file, chars)
278+
SourceFile(file, chars)
279+
280+
def apply(file: AbstractFile | Null, computeContent: => Array[Char]): SourceFile = new SourceFile(file, computeContent)
287281
}
288282

289283
@sharable object NoSource extends SourceFile(NoAbstractFile, Array[Char]()) {

compiler/src/dotty/tools/io/VirtualFile.scala

+9-5
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF
2828
def this(name: String) = this(name, name)
2929

3030
/**
31-
* Initializes this instance with the specified name and an
32-
* identical path.
31+
* Initializes this instance with the specified path
32+
* and a name taken from the last path element.
3333
*
34-
* @param name the name of the virtual file to be created
34+
* @param path the path of the virtual file to be created
3535
* @param content the initial contents of the virtual file
3636
* @return the created virtual file
3737
*/
38-
def this(name: String, content: Array[Byte]) = {
39-
this(name)
38+
def this(path: String, content: Array[Byte]) = {
39+
this(VirtualFile.nameOf(path), path)
4040
this.content = content
4141
}
4242

@@ -104,3 +104,7 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF
104104
*/
105105
def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = unsupported()
106106
}
107+
object VirtualFile:
108+
private def nameOf(path: String): String =
109+
val i = path.lastIndexOf('/')
110+
if i >= 0 && i < path.length - 1 then path.substring(i + 1) else path

compiler/test/dotty/tools/dotc/parsing/ParserTest.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class ParserTest extends DottyTest {
2121
parsedTrees.clear()
2222
}
2323

24-
def parse(file: PlainFile): Tree = parseSource(new SourceFile(file, Codec.UTF8))
24+
def parse(file: PlainFile): Tree = parseSource(SourceFile(file, Codec.UTF8))
2525

2626
private def parseSource(source: SourceFile): Tree = {
2727
//println("***** parsing " + source.file)

compiler/test/dotty/tools/dotc/parsing/ScannerTest.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ScannerTest extends DottyTest {
1919

2020
def scan(file: PlainFile): Unit = {
2121
//println("***** scanning " + file)
22-
val source = new SourceFile(file, Codec.UTF8)
22+
val source = SourceFile(file, Codec.UTF8)
2323
val scanner = new Scanner(source)
2424
var i = 0
2525
while (scanner.token != EOF) {

language-server/test/dotty/tools/languageserver/util/CodeRange.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package dotty.tools.languageserver.util
33
import dotty.tools.languageserver.util.embedded.{CodeInRange, CodeMarker}
44
import dotty.tools.languageserver.util.server.TestFile
55

6-
import org.eclipse.lsp4j._
6+
import org.eclipse.lsp4j.{Location, Range, SymbolKind}
77

88
import PositionContext._
99

scaladoc/src/dotty/tools/scaladoc/DocContext.scala

+1-2
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ def throwableToString(t: Throwable)(using CompilerContext): String =
4040

4141
private def sourcePostionFor(f: File)(using CompilerContext) =
4242
val relPath = relativePath(f.toPath)
43-
val virtualFile = new VirtualFile(relPath.toString, relPath.toString)
44-
val sourceFile = new SourceFile(virtualFile, Codec.UTF8)
43+
val sourceFile = SourceFile.virtual(relPath.toString, content = "")
4544
SourcePosition(sourceFile, Spans.NoSpan)
4645

4746
// TODO (https://github.com/lampepfl/scala3doc/issues/238): provide proper error handling

0 commit comments

Comments
 (0)