Skip to content

Commit a585969

Browse files
authored
Merge pull request #13351 from pikinier20/scaladoc/snippet-compiler-reporting
Scaladoc: Refactor snippet compiler reporting
2 parents eaf321b + f8d4168 commit a585969

File tree

9 files changed

+63
-40
lines changed

9 files changed

+63
-40
lines changed

scaladoc/src/dotty/tools/scaladoc/site/templates.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ case class TemplateFile(
6868
lazy val snippetCheckingFunc: SnippetChecker.SnippetCheckingFunc =
6969
val path = Some(Paths.get(file.getAbsolutePath))
7070
val pathBasedArg = ssctx.snippetCompilerArgs.get(path)
71+
val sourceFile = dotty.tools.dotc.util.SourceFile(dotty.tools.io.AbstractFile.getFile(path.get), scala.io.Codec.UTF8)
7172
(str: String, lineOffset: SnippetChecker.LineOffset, argOverride: Option[SCFlags]) => {
7273
val arg = argOverride.fold(pathBasedArg)(pathBasedArg.overrideFlag(_))
7374
val compilerData = SnippetCompilerData(
@@ -76,10 +77,9 @@ case class TemplateFile(
7677
Nil,
7778
SnippetCompilerData.Position(configOffset - 1, 0)
7879
)
79-
ssctx.snippetChecker.checkSnippet(str, Some(compilerData), arg, lineOffset).collect {
80+
ssctx.snippetChecker.checkSnippet(str, Some(compilerData), arg, lineOffset, sourceFile).collect {
8081
case r: SnippetCompilationResult if !r.isSuccessful =>
81-
val msg = s"In static site (${file.getAbsolutePath}):\n${r.getSummary}"
82-
report.error(msg)(using ssctx.outerCtx)
82+
r.reportMessages()(using ssctx.outerCtx)
8383
r
8484
case r => r
8585
}

scaladoc/src/dotty/tools/scaladoc/snippets/SnippetChecker.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import dotty.tools.scaladoc.DocContext
55
import java.nio.file.Paths
66
import java.io.File
77

8+
import dotty.tools.dotc.util.SourceFile
89
import dotty.tools.io.AbstractFile
910
import dotty.tools.dotc.fromtasty.TastyFileUtil
1011
import dotty.tools.dotc.config.Settings._
@@ -39,7 +40,8 @@ class SnippetChecker(val args: Scaladoc.Args)(using cctx: CompilerContext):
3940
snippet: String,
4041
data: Option[SnippetCompilerData],
4142
arg: SnippetCompilerArg,
42-
lineOffset: SnippetChecker.LineOffset
43+
lineOffset: SnippetChecker.LineOffset,
44+
sourceFile: SourceFile
4345
): Option[SnippetCompilationResult] = {
4446
if arg.flag != SCFlags.NoCompile then
4547
val wrapped = WrappedSnippet(
@@ -50,7 +52,7 @@ class SnippetChecker(val args: Scaladoc.Args)(using cctx: CompilerContext):
5052
lineOffset + data.fold(0)(_.position.line) + constantLineOffset,
5153
data.fold(0)(_.position.column) + constantColumnOffset
5254
)
53-
val res = compiler.compile(wrapped, arg)
55+
val res = compiler.compile(wrapped, arg, sourceFile)
5456
Some(res)
5557
else None
5658
}

scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompilationResult.scala

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,28 @@ package dotty.tools.scaladoc
22
package snippets
33

44
import dotty.tools.io.{ AbstractFile }
5+
import dotty.tools.dotc.util.{ SourcePosition, SrcPos }
56

6-
case class Position(line: Int, column: Int, sourceLine: String, relativeLine: Int)
7+
case class Position(srcPos: SourcePosition, relativeLine: Int)
78

8-
case class SnippetCompilerMessage(position: Option[Position], message: String, level: MessageLevel):
9-
def getSummary: String =
10-
position.fold(s"${level.text}: ${message}") { pos =>
11-
s"At ${pos.line}:${pos.column}:\n${pos.sourceLine}${level.text}: ${message}"
12-
}
9+
case class SnippetCompilerMessage(position: Option[Position], message: String, level: MessageLevel)
1310

1411
case class SnippetCompilationResult(
1512
wrappedSnippet: WrappedSnippet,
1613
isSuccessful: Boolean,
1714
result: Option[AbstractFile],
1815
messages: Seq[SnippetCompilerMessage]
1916
):
20-
def getSummary: String = messages.map(_.getSummary).mkString("\n")
17+
def reportMessages()(using CompilerContext) = messages.foreach {
18+
case SnippetCompilerMessage(posOpt, msg, level) =>
19+
val pos: SrcPos = posOpt.fold(dotty.tools.dotc.util.NoSourcePosition)(_.srcPos)
20+
level match {
21+
case MessageLevel.Info => report.log(msg, pos)
22+
case MessageLevel.Warning => report.warning(msg, pos)
23+
case MessageLevel.Error => report.error(msg, pos)
24+
case MessageLevel.Debug => report.log(msg, pos)
25+
}
26+
}
2127

2228
enum MessageLevel(val text: String):
2329
case Info extends MessageLevel("Info")

scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompiler.scala

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@ import dotty.tools.dotc.Driver
66
import dotty.tools.dotc.core.Contexts.Context
77
import dotty.tools.dotc.core.Mode
88
import dotty.tools.dotc.config.Settings.Setting._
9-
import dotty.tools.dotc.interfaces.SourcePosition
9+
import dotty.tools.dotc.interfaces.{ SourcePosition => ISourcePosition }
1010
import dotty.tools.dotc.ast.Trees.Tree
1111
import dotty.tools.dotc.interfaces.{SourceFile => ISourceFile}
1212
import dotty.tools.dotc.reporting.{ Diagnostic, StoreReporter }
1313
import dotty.tools.dotc.parsing.Parsers.Parser
1414
import dotty.tools.dotc.{ Compiler, Run }
1515
import dotty.tools.io.{AbstractFile, VirtualDirectory}
1616
import dotty.tools.repl.AbstractFileClassLoader
17-
import dotty.tools.dotc.util.SourceFile
17+
import dotty.tools.dotc.util.Spans._
1818
import dotty.tools.dotc.interfaces.Diagnostic._
19+
import dotty.tools.dotc.util.{ SourcePosition, NoSourcePosition, SourceFile, NoSource }
1920

2021
import scala.util.{ Try, Success, Failure }
2122

@@ -49,17 +50,24 @@ class SnippetCompiler(
4950
private def nullableMessage(msgOrNull: String): String =
5051
if (msgOrNull == null) "" else msgOrNull
5152

52-
private def createReportMessage(wrappedSnippet: WrappedSnippet, arg: SnippetCompilerArg, diagnostics: Seq[Diagnostic]): Seq[SnippetCompilerMessage] = {
53+
private def createReportMessage(wrappedSnippet: WrappedSnippet, arg: SnippetCompilerArg, diagnostics: Seq[Diagnostic], sourceFile: SourceFile): Seq[SnippetCompilerMessage] = {
5354
val line = wrappedSnippet.outerLineOffset
5455
val column = wrappedSnippet.outerColumnOffset
5556
val innerLineOffset = wrappedSnippet.innerLineOffset
5657
val innerColumnOffset = wrappedSnippet.innerColumnOffset
5758
val infos = diagnostics.toSeq.sortBy(_.pos.source.path)
5859
val errorMessages = infos.map {
5960
case diagnostic if diagnostic.position.isPresent =>
60-
val diagPos = diagnostic.position.get
61+
val diagPos = diagnostic.position.get match
62+
case s: SourcePosition => s
63+
case _ => NoSourcePosition
64+
val offsetFromLine = sourceFile match
65+
case NoSource => 0
66+
case sf: SourceFile => sf.lineToOffset(diagPos.line + line - innerLineOffset - 1)
67+
val offsetFromColumn = diagPos.column + column - innerColumnOffset
68+
val span = Span(offsetFromLine + offsetFromColumn, offsetFromLine + offsetFromColumn)
6169
val pos = Some(
62-
Position(diagPos.line + line - innerLineOffset, diagPos.column + column - innerColumnOffset, diagPos.lineContent, diagPos.line)
70+
Position(dotty.tools.dotc.util.SourcePosition(sourceFile, span), diagPos.line)
6371
)
6472
val dmsg = Try(diagnostic.message) match {
6573
case Success(msg) => msg
@@ -75,7 +83,7 @@ class SnippetCompiler(
7583
errorMessages
7684
}
7785

78-
private def additionalMessages(wrappedSnippet: WrappedSnippet, arg: SnippetCompilerArg, context: Context): Seq[SnippetCompilerMessage] = {
86+
private def additionalMessages(wrappedSnippet: WrappedSnippet, arg: SnippetCompilerArg, sourceFile: SourceFile, context: Context): Seq[SnippetCompilerMessage] = {
7987
Option.when(arg.flag == SCFlags.Fail && !context.reporter.hasErrors)(
8088
SnippetCompilerMessage(None, "Snippet should not compile but compiled succesfully", MessageLevel.Error)
8189
).toList
@@ -88,7 +96,8 @@ class SnippetCompiler(
8896

8997
def compile(
9098
wrappedSnippet: WrappedSnippet,
91-
arg: SnippetCompilerArg
99+
arg: SnippetCompilerArg,
100+
sourceFile: SourceFile
92101
): SnippetCompilationResult = {
93102
val context = SnippetDriver.currentCtx.fresh
94103
.setSetting(
@@ -100,8 +109,8 @@ class SnippetCompiler(
100109
run.compileFromStrings(List(wrappedSnippet.snippet))
101110

102111
val messages =
103-
createReportMessage(wrappedSnippet, arg, context.reporter.pendingMessages(using context)) ++
104-
additionalMessages(wrappedSnippet, arg, context)
112+
createReportMessage(wrappedSnippet, arg, context.reporter.pendingMessages(using context), sourceFile) ++
113+
additionalMessages(wrappedSnippet, arg, sourceFile, context)
105114

106115
val t = Option.when(!context.reporter.hasErrors)(target)
107116
SnippetCompilationResult(wrappedSnippet, isSuccessful(arg, context), t, messages)

scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompilerDataCollector.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,20 @@ package snippets
44
import scala.quoted._
55
import dotty.tools.scaladoc.tasty.SymOps._
66
import dotty.tools.dotc.core._
7+
import dotty.tools.dotc.util.{ SourceFile => CSourceFile, NoSource }
78

89
class SnippetCompilerDataCollector[Q <: Quotes](val qctx: Q):
910
import qctx.reflect._
1011
given qctx.type = qctx
1112

13+
def getSourceFile(sym: Symbol): CSourceFile =
14+
given ctx: Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
15+
sym match
16+
case csym: Symbols.Symbol => csym.source(using ctx)
17+
case _ =>
18+
report.warning(s"Can't cast symbol $sym to compiler symbol. This is a bug of snippet compiler, please create an issue on dotty repository.")
19+
NoSource
20+
1221
def getSnippetCompilerData(sym: Symbol, originalSym: Symbol): SnippetCompilerData =
1322
val packageName = sym.packageName
1423
if !sym.isPackageDef then sym.tree match {

scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,14 @@ abstract class MarkupConversion[T](val repr: Repr)(using dctx: DocContext) {
128128
(s: qctx.reflect.Symbol) => {
129129
val path = s.source.map(_.path)
130130
val pathBasedArg = dctx.snippetCompilerArgs.get(path)
131-
val data = SnippetCompilerDataCollector[qctx.type](qctx).getSnippetCompilerData(s, s)
131+
val scDataCollector = SnippetCompilerDataCollector[qctx.type](qctx)
132+
val data = scDataCollector.getSnippetCompilerData(s, s)
133+
val sourceFile = scDataCollector.getSourceFile(s)
132134
(str: String, lineOffset: SnippetChecker.LineOffset, argOverride: Option[SCFlags]) => {
133135
val arg = argOverride.fold(pathBasedArg)(pathBasedArg.overrideFlag(_))
134-
135-
snippetChecker.checkSnippet(str, Some(data), arg, lineOffset).collect {
136-
case r: SnippetCompilationResult if !r.isSuccessful =>
137-
val msg = s"In member ${s.name} (${s.dri.location}):\n${r.getSummary}"
138-
report.error(msg)(using dctx.compilerContext)
139-
r
140-
case r => r
141-
}
136+
val res = snippetChecker.checkSnippet(str, Some(data), arg, lineOffset, sourceFile)
137+
res.filter(r => !r.isSuccessful).foreach(_.reportMessages()(using compilerContext))
138+
res
142139
}
143140
}
144141

scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SnippetRenderer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ object SnippetRenderer:
125125
else
126126
val content = messages
127127
.map { msg =>
128-
s"""<span class="${compileMessageCSSClass(msg)}">${msg.getSummary}</span>"""
128+
s"""<span class="${compileMessageCSSClass(msg)}">${msg.message}</span>"""
129129
}
130130
.mkString("<br>")
131131
s"""<hr>$content"""

scaladoc/test/dotty/tools/scaladoc/snippets/SnippetCompilerTest.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ class SnippetCompilerTest {
1818
0
1919
)
2020

21-
def runTest(str: String) = compiler.compile(wrapFn(str), SnippetCompilerArg(SCFlags.Compile))
21+
def runTest(str: String) = compiler.compile(wrapFn(str), SnippetCompilerArg(SCFlags.Compile), dotty.tools.dotc.util.SourceFile.virtual("test", str))
2222

2323
private def assertSuccessfulCompilation(res: SnippetCompilationResult): Unit = res match {
24-
case r @ SnippetCompilationResult(_, isSuccessful, _, messages) => assert(isSuccessful, r.getSummary)
24+
case r @ SnippetCompilationResult(_, isSuccessful, _, messages) => assert(isSuccessful, r.messages.map(_.message).mkString("\n"))
2525
}
2626

2727
private def assertFailedCompilation(res: SnippetCompilationResult): Unit = res match {
28-
case r @ SnippetCompilationResult(_, isSuccessful, _, messages) => assert(!isSuccessful, r.getSummary)
28+
case r @ SnippetCompilationResult(_, isSuccessful, _, messages) => assert(!isSuccessful, r.messages.map(_.message).mkString("\n"))
2929
}
3030

3131
def assertSuccessfulCompilation(str: String): Unit = assertSuccessfulCompilation(runTest(str))
@@ -36,7 +36,7 @@ class SnippetCompilerTest {
3636

3737
def assertMessageLevelPresent(res: SnippetCompilationResult, level: MessageLevel): Unit = res match {
3838
case r @ SnippetCompilationResult(_, isSuccessful, _, messages) => assertTrue(
39-
s"Expected message with level: ${level.text}. Got result ${r.getSummary}",
39+
s"Expected message with level: ${level.text}. Got result ${r.messages.map(_.message).mkString("\n")}",
4040
messages.exists(_.level == level)
4141
)
4242
}

scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTest.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,19 @@ abstract class SnippetsE2eTest(testName: String, flag: SCFlags) extends Scaladoc
7878
case m @ SnippetCompilerMessage(Some(_), _, _) => m
7979
}.toList
8080
def isSamePosition(msg: Message, cmsg: SnippetCompilerMessage): Boolean =
81-
cmsg.level == msg.level && cmsg.position.get.line == msg.offset.line && cmsg.position.get.column == msg.offset.column
81+
cmsg.level == msg.level && (cmsg.position.get.srcPos.line + 1) == msg.offset.line && cmsg.position.get.srcPos.column == msg.offset.column
8282

8383
def checkRelativeLines(msg: Message, cmsg: SnippetCompilerMessage): Seq[String] =
8484
val pos = cmsg.position.get
85-
if !(pos.relativeLine == pos.line - ws.outerLineOffset + ws.innerLineOffset) then Seq(
86-
s"Expected ${msg.level.text} message at relative line: ${pos.line - ws.outerLineOffset + ws.innerLineOffset} " +
85+
if !(pos.relativeLine == pos.srcPos.line + ws.innerLineOffset - ws.outerLineOffset + 1) then Seq(
86+
s"Expected ${msg.level.text} message at relative line: ${pos.srcPos.line + ws.innerLineOffset - ws.outerLineOffset + 1} " +
8787
s"but found at ${pos.relativeLine}"
8888
) else Nil
8989

9090
val mResult = compilationMessagesWithPos.flatMap { cmsg =>
9191
messages
9292
.find(msg => isSamePosition(msg, cmsg))
93-
.fold(Seq(s"Unexpected compilation message: ${cmsg.message} at relative line: ${cmsg.position.fold(-1)(_.line)}"))(_ => Seq())
93+
.fold(Seq(s"Unexpected compilation message: ${cmsg.message} at relative line: ${cmsg.position.fold(-1)(_.relativeLine)}"))(_ => Seq())
9494
}
9595

9696
val result = mResult ++ messages.flatMap { msg =>
@@ -103,7 +103,7 @@ abstract class SnippetsE2eTest(testName: String, flag: SCFlags) extends Scaladoc
103103

104104
if !result.isEmpty then {
105105
val errors = result.mkString("\n")
106-
val foundMessages = compilationMessages.map(m => s"${m.level} at ${m.position.get.line}:${m.position.get.column}").mkString("\n")
106+
val foundMessages = compilationMessages.map(m => s"${m.level} at ${m.position.get.srcPos.line}:${m.position.get.srcPos.column}").mkString("\n")
107107
throw AssertionError(Seq("Errors:", errors,"Found:", foundMessages).mkString("\n", "\n", "\n"))
108108
}
109109
}

0 commit comments

Comments
 (0)