Skip to content

Commit 50cdf55

Browse files
committed
Snippet compiler PoC
1 parent 0195dff commit 50cdf55

File tree

5 files changed

+163
-0
lines changed

5 files changed

+163
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package dotty.tools.scaladoc
2+
package snippets
3+
4+
import dotty.tools.scaladoc.DocContext
5+
6+
class SnippetChecker(
7+
private val compiler: SnippetCompiler = SnippetCompiler(),
8+
private val wrapper: SnippetWrapper = SnippetWrapper()
9+
):
10+
def checkSnippet(snippet: String): SnippetCompilationResult = {
11+
val wrapped = wrapper.wrap(snippet)
12+
compiler.compile(wrapped)
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package dotty.tools.scaladoc
2+
package snippets
3+
4+
import dotty.tools.io.{ AbstractFile }
5+
6+
case class SnippetCompilerMessage(line: Int, column: Int, sourceLine: String, message: String)
7+
8+
case class SnippetCompilationResult(result: Option[AbstractFile], messages: Seq[SnippetCompilerMessage]):
9+
def getSummary: String = messages.map(m => s"At ${m.line}:${m.column}:\n${m.sourceLine}Compiler: ${m.message}").mkString("\n")
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package dotty.tools.scaladoc
2+
package snippets
3+
4+
import dotty.tools.io.{AbstractFile, VirtualDirectory}
5+
import dotty.tools.dotc.interactive.InteractiveDriver
6+
import dotty.tools.dotc.interactive.Interactive
7+
import dotty.tools.dotc.interactive.InteractiveCompiler
8+
import dotty.tools.dotc.core.Contexts.Context
9+
import dotty.tools.dotc.config.Settings.Setting._
10+
import dotty.tools.dotc.interfaces.SourcePosition
11+
import dotty.tools.dotc.ast.Trees.Tree
12+
import dotty.tools.dotc.interfaces.{SourceFile => ISourceFile}
13+
import dotty.tools.dotc.reporting.{ Diagnostic, StoreReporter }
14+
import dotty.tools.dotc.parsing.Parsers.Parser
15+
import dotty.tools.dotc.{ Compiler, Run }
16+
import dotty.tools.io.{AbstractFile, VirtualDirectory}
17+
import dotty.tools.repl.AbstractFileClassLoader
18+
import dotty.tools.dotc.util.SourceFile
19+
20+
class SnippetCompiler(
21+
classpath: String = System.getProperty("java.class.path"), //Probably needs to be done better
22+
val scalacOptions: String = "",
23+
target: AbstractFile = new VirtualDirectory("(memory)")
24+
):
25+
26+
private def newDriver: InteractiveDriver = {
27+
val defaultFlags =
28+
List("-color:never", "-unchecked", "-deprecation", "-Ximport-suggestion-timeout", "0")
29+
val options = scalacOptions.split("\\s+").toList
30+
val settings =
31+
options ::: defaultFlags ::: "-classpath" :: classpath :: Nil
32+
new InteractiveDriver(settings)
33+
}
34+
35+
private val driver = newDriver
36+
37+
private val scala3Compiler = new Compiler
38+
39+
private def newRun(using ctx: Context): Run = scala3Compiler.newRun
40+
41+
private def nullableMessage(msgOrNull: String): String =
42+
if (msgOrNull == null) "" else msgOrNull
43+
44+
private def createReportMessage(diagnostics: Seq[Diagnostic]): Seq[SnippetCompilerMessage] = {
45+
val infos = diagnostics.toSeq.sortBy(_.pos.source.path)
46+
val errorMessages = infos.map {
47+
case diagnostic if diagnostic.position.isPresent =>
48+
val pos = diagnostic.position.get
49+
val msg = nullableMessage(diagnostic.message)
50+
SnippetCompilerMessage(pos.line, pos.column, pos.lineContent, msg)
51+
case d => SnippetCompilerMessage(-1, -1, "", nullableMessage(d.message))
52+
}
53+
errorMessages
54+
}
55+
56+
def compile(
57+
snippets: List[String]
58+
): SnippetCompilationResult = {
59+
val context = driver.currentCtx.fresh
60+
.setSetting(
61+
driver.currentCtx.settings.outputDir,
62+
target
63+
)
64+
.setReporter(new StoreReporter)
65+
val run = newRun(using context)
66+
run.compileFromStrings(snippets)
67+
//Currently no warnings because of reporter implementation
68+
val messages = createReportMessage(context.reporter.allErrors)
69+
val targetIfSuccessful = Option.when(!context.reporter.hasErrors)(target)
70+
SnippetCompilationResult(targetIfSuccessful, messages)
71+
}
72+
73+
def compile(
74+
snippet: String
75+
): SnippetCompilationResult = compile(List(snippet))
76+
77+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package dotty.tools.scaladoc
2+
package snippets
3+
4+
class SnippetWrapper:
5+
def wrap(str: String): String = s"""
6+
|package snippets
7+
|object Snippet {
8+
| $str
9+
|}
10+
|""".stripMargin
11+
12+
object SnippetWrapper:
13+
val lineOffset = 2
14+
val columnOffset = 2
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package dotty.tools.scaladoc
2+
package snippets
3+
4+
import org.junit.Test
5+
import org.junit.Assert._
6+
import dotty.tools.io.{AbstractFile, VirtualDirectory}
7+
8+
class SnippetCompilerTest {
9+
val compiler = SnippetCompiler()
10+
def runTest(str: String) = compiler.compile(str)
11+
12+
def runTest(str: List[String]) = compiler.compile(str)
13+
14+
private def assertSuccessfulCompilation(res: SnippetCompilationResult): Unit = res match {
15+
case SnippetCompilationResult(Some(target), _) => assert(true)
16+
case r @ SnippetCompilationResult(None, _) => assert(false, r.getSummary)
17+
}
18+
19+
private def assertFailedCompilation(res: SnippetCompilationResult): Unit = res match {
20+
case SnippetCompilationResult(Some(target), _) => assert(false, "Expected compilation failure")
21+
case r @ SnippetCompilationResult(None, _) => assert(true)
22+
}
23+
24+
def assertSuccessfulCompilation(str: String): Unit = assertSuccessfulCompilation(runTest(str))
25+
26+
def assertFailedCompilation(str: String): Unit = assertFailedCompilation(runTest(str))
27+
28+
def assertSuccessfulCompilation(str: List[String]): Unit = assertSuccessfulCompilation(runTest(str))
29+
30+
def assertFailedCompilation(str: List[String]): Unit = assertFailedCompilation(runTest(str))
31+
32+
33+
@Test
34+
def snippetCompilerTest: Unit = {
35+
val simpleCorrectSnippet = s"""
36+
|package asd
37+
|class A:
38+
| val b: String = "asd"
39+
|""".stripMargin
40+
41+
val simpleIncorrectSnippet = s"""
42+
|package asd
43+
|class A:
44+
| val b: String
45+
|""".stripMargin
46+
assertSuccessfulCompilation(simpleCorrectSnippet)
47+
assertFailedCompilation(simpleIncorrectSnippet)
48+
assertFailedCompilation(List(simpleCorrectSnippet, simpleCorrectSnippet))
49+
}
50+
}

0 commit comments

Comments
 (0)