Skip to content

Commit

Permalink
Add returning real line of error in source file of snippet for snippe…
Browse files Browse the repository at this point in the history
…t scaladoc compiler
  • Loading branch information
BarkingBad authored and pikinier20 committed Mar 25, 2021
1 parent 51120be commit 61a74c5
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 57 deletions.
3 changes: 2 additions & 1 deletion scaladoc-testcases/src/tests/snippetCompilerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package snippetCompiler
/**
* ```scala sc:compile
* def a = 2
* val x = 1 + List()
* a
* ```
*
Expand All @@ -11,4 +12,4 @@ package snippetCompiler
* a()
* ```
*/
class A { }
class A { }
14 changes: 9 additions & 5 deletions scaladoc/src/dotty/tools/scaladoc/api.scala
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,15 @@ extension (s: Signature)
case l: Link => l.name
}.mkString

case class TastyMemberSource(val path: java.nio.file.Path, val lineNumber: Int)
case class TastyMemberSource(path: java.nio.file.Path, lineNumber: Int)

object SnippetCompilerData:
case class Position(line: Int, column: Int)

case class SnippetCompilerData(
val packageName: String,
val classType: Option[String],
val classGenerics: Option[String],
val imports: List[String]
packageName: String,
classType: Option[String],
classGenerics: Option[String],
imports: List[String],
position: SnippetCompilerData.Position
)
32 changes: 16 additions & 16 deletions scaladoc/src/dotty/tools/scaladoc/renderers/WikiDocRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ import dotty.tools.scaladoc.snippets._

class DocRender(signatureRenderer: SignatureRenderer, snippetChecker: SnippetChecker)(using ctx: DocContext):

private val snippetCheckingFunc: Member => (String, Option[SnippetCompilerArg]) => Unit =
(m: Member) => {
(str: String, argOverride: Option[SnippetCompilerArg]) => {
val arg = argOverride.fold(
ctx.snippetCompilerArgs.get(m).fold(SnippetCompilerArg.default)(p => p)
)(p => p)

snippetChecker.checkSnippet(str, m.docs.map(_.snippetCompilerData), arg).foreach { _ match {
case r @ SnippetCompilationResult(None, _) =>
println(s"In member ${m.name} (${m.dri.location}):")
println(r.getSummary)
case _ =>
private val snippetCheckingFuncFromMember: Member => SnippetChecker.SnippetCheckingFunc =
(m: Member) => {
(str: String, lineOffset: SnippetChecker.LineOffset, argOverride: Option[SnippetCompilerArg]) => {
val arg = argOverride.getOrElse(
ctx.snippetCompilerArgs.get(m).getOrElse(SnippetCompilerArg.default)
)

snippetChecker.checkSnippet(str, m.docs.map(_.snippetCompilerData), arg, lineOffset).foreach { _ match {
case r @ SnippetCompilationResult(None, _) =>
println(s"In member ${m.name} (${m.dri.location}):")
println(r.getSummary)
case _ =>
}
}
}
}
}
}

def renderDocPart(doc: DocPart)(using Member): AppliedTag = doc match
case md: MdNode => renderMarkdown(md)
Expand All @@ -37,7 +37,7 @@ class DocRender(signatureRenderer: SignatureRenderer, snippetChecker: SnippetChe
raw(DocFlexmarkRenderer.render(el)(
(link,name) =>
renderLink(link, default => text(if name.isEmpty then default else name)).toString,
snippetCheckingFunc(m)
snippetCheckingFuncFromMember(m)
))

private def listItems(items: Seq[WikiDocElement])(using m: Member) =
Expand Down Expand Up @@ -67,7 +67,7 @@ class DocRender(signatureRenderer: SignatureRenderer, snippetChecker: SnippetChe
case 6 => h6(content)
case Paragraph(text) => p(renderElement(text))
case Code(data: String) =>
snippetCheckingFunc(m)(data, None)
snippetCheckingFuncFromMember(m)(data, 0, None)
pre(code(raw(data))) // TODO add classes
case HorizontalRule => hr

Expand Down
17 changes: 12 additions & 5 deletions scaladoc/src/dotty/tools/scaladoc/snippets/SnippetChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,25 @@ class SnippetChecker()(using ctx: DocContext):
Paths.get(ctx.args.classpath).toAbsolutePath + sep +
ctx.args.tastyDirs.map(_.getAbsolutePath()).mkString(sep)
private val compiler: SnippetCompiler = SnippetCompiler(classpath = cp)
private val wrapper: SnippetWrapper = SnippetWrapper()

var warningsCount = 0
var errorsCount = 0

def checkSnippet(
snippet: String,
data: Option[SnippetCompilerData],
arg: SnippetCompilerArg
arg: SnippetCompilerArg,
lineOffset: SnippetChecker.LineOffset
): Option[SnippetCompilationResult] = {
if arg.is(SCFlags.Compile) then
val wrapped = wrapper.wrap(
val wrapped = WrappedSnippet(
snippet,
data.map(_.packageName),
data.flatMap(_.classType),
data.flatMap(_.classGenerics),
data.map(_.imports).getOrElse(Nil)
data.map(_.imports).getOrElse(Nil),
lineOffset + data.fold(0)(_.position.line) + 1,
data.fold(0)(_.position.column)
)
val res = compiler.compile(wrapped)
if !res.messages.filter(_.level == MessageLevel.Error).isEmpty then errorsCount = errorsCount + 1
Expand All @@ -38,4 +41,8 @@ class SnippetChecker()(using ctx: DocContext):
|Snippet compiler summary:
| Found $warningsCount warnings
| Found $errorsCount errors
|""".stripMargin
|""".stripMargin

object SnippetChecker:
type LineOffset = Int
type SnippetCheckingFunc = (String, LineOffset, Option[SnippetCompilerArg]) => Unit
16 changes: 5 additions & 11 deletions scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ class SnippetCompiler(
private def nullableMessage(msgOrNull: String): String =
if (msgOrNull == null) "" else msgOrNull

private def createReportMessage(diagnostics: Seq[Diagnostic]): Seq[SnippetCompilerMessage] = {
private def createReportMessage(diagnostics: Seq[Diagnostic], line: Int, column: Int): Seq[SnippetCompilerMessage] = {
val infos = diagnostics.toSeq.sortBy(_.pos.source.path)
val errorMessages = infos.map {
case diagnostic if diagnostic.position.isPresent =>
val pos = diagnostic.position.get
val msg = nullableMessage(diagnostic.message)
val level = MessageLevel.fromOrdinal(diagnostic.level)
SnippetCompilerMessage(pos.line, pos.column, pos.lineContent, msg, level)
SnippetCompilerMessage(pos.line + line, pos.column + column, pos.lineContent, msg, level)
case d =>
val level = MessageLevel.fromOrdinal(d.level)
SnippetCompilerMessage(-1, -1, "", nullableMessage(d.message), level)
Expand All @@ -58,7 +58,7 @@ class SnippetCompiler(
}

def compile(
snippets: List[String]
wrappedSnippet: WrappedSnippet
): SnippetCompilationResult = {
val context = driver.currentCtx.fresh
.setSetting(
Expand All @@ -67,14 +67,8 @@ class SnippetCompiler(
)
.setReporter(new StoreReporter)
val run = newRun(using context)
run.compileFromStrings(snippets)
val messages = createReportMessage(context.reporter.pendingMessages(using context))
run.compileFromStrings(List(wrappedSnippet.snippet))
val messages = createReportMessage(context.reporter.pendingMessages(using context), wrappedSnippet.lineOffset, wrappedSnippet.columnOffset)
val targetIfSuccessful = Option.when(!context.reporter.hasErrors)(target)
SnippetCompilationResult(targetIfSuccessful, messages)
}

def compile(
snippet: String
): SnippetCompilationResult = compile(List(snippet))


Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,4 @@ object SnippetCompilerArgParser extends ArgParser[SnippetCompilerArg]:
}.toList

if !allErrors.isEmpty then Left(allErrors.mkString("\n")) else Right(SnippetCompilerArg(checkedFlags))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,40 @@ package snippets
import java.io.ByteArrayOutputStream
import java.io.PrintStream

class SnippetWrapper:
extension (ps: PrintStream) private def printlnWithIndent(indent: Int, str: String) =
ps.println((" " * indent) + str)
def wrap(str: String): String =
case class WrappedSnippet(snippet: String, lineOffset: Int, columnOffset: Int)

object WrappedSnippet:
private val lineOffset = 2
private val columnOffset = 2

def apply(str: String): WrappedSnippet =
val baos = new ByteArrayOutputStream()
val ps = new PrintStream(baos)
ps.println("package snippets")
ps.println("object Snippet {")
str.split('\n').foreach(ps.printlnWithIndent(2, _))
ps.println("}")
baos.toString
WrappedSnippet(baos.toString, lineOffset, columnOffset)

def wrap(str:String, packageName: Option[String], className: Option[String], classGenerics: Option[String], imports: List[String]) =
def apply(
str: String,
packageName: Option[String],
className: Option[String],
classGenerics: Option[String],
imports: List[String],
lineOffset: Int,
columnOffset: Int
): WrappedSnippet =
val baos = new ByteArrayOutputStream()
val ps = new PrintStream(baos)
ps.println(s"package ${packageName.getOrElse("snippets")}")
imports.foreach(i => ps.println(s"import $i"))
ps.println(s"trait Snippet${classGenerics.getOrElse("")} { ${className.fold("")(cn => s"self: $cn =>")}")
str.split('\n').foreach(ps.printlnWithIndent(2, _))
ps.println("}")
baos.toString
WrappedSnippet(baos.toString, lineOffset, columnOffset)

extension (ps: PrintStream) private def printlnWithIndent(indent: Int, str: String) =
ps.println((" " * indent) + str)


object SnippetWrapper:
private val lineOffset = 2
private val columnOffset = 2
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,3 @@ trait SyntheticsSupport:
typeForClass(c).asInstanceOf[dotc.core.Types.Type]
.memberInfo(symbol.asInstanceOf[dotc.core.Symbols.Symbol])
.asInstanceOf[TypeRepr]

27 changes: 24 additions & 3 deletions scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,31 @@ abstract class MarkupConversion[T](val repr: Repr)(using DocContext) {
createTypeConstructor(t.asInstanceOf[dotc.core.Types.TypeRef].underlying)
).mkString("[",", ","]")
)
SnippetCompilerData(packageName, classType, classGenerics, Nil)
SnippetCompilerData(packageName, classType, classGenerics, Nil, position(hackGetPositionOfDocstring(using qctx)(sym)))
case _ => getSnippetCompilerData(sym.maybeOwner)
} else SnippetCompilerData(packageName, None, None, Nil)

} else SnippetCompilerData(packageName, None, None, Nil, position(hackGetPositionOfDocstring(using qctx)(sym)))

private def position(p: Option[qctx.reflect.Position]): SnippetCompilerData.Position =
p.fold(SnippetCompilerData.Position(0, 0))(p => SnippetCompilerData.Position(p.startLine, p.startColumn))

private def hackGetPositionOfDocstring(using Quotes)(s: qctx.reflect.Symbol): Option[qctx.reflect.Position] =
import dotty.tools.dotc.core.Comments.CommentsContext
import dotty.tools.dotc
given ctx: dotc.core.Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
val docCtx = ctx.docCtx.getOrElse {
throw new RuntimeException(
"DocCtx could not be found and documentations are unavailable. This is a compiler-internal error."
)
}
val span = docCtx.docstring(s.asInstanceOf[dotc.core.Symbols.Symbol]).span
s.pos.flatMap { pos =>
docCtx.docstring(s.asInstanceOf[dotc.core.Symbols.Symbol]).map { docstring =>
dotty.tools.dotc.util.SourcePosition(
pos.sourceFile.asInstanceOf[dotty.tools.dotc.util.SourceFile],
docstring.span
).asInstanceOf[qctx.reflect.Position]
}
}

final def parse(preparsed: PreparsedComment): Comment =
val body = markupToDokkaCommentBody(stringToMarkup(preparsed.body))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import com.vladsch.flexmark.util.options._
import com.vladsch.flexmark.util.sequence.BasedSequence
import com.vladsch.flexmark._

import dotty.tools.scaladoc.snippets.SnippetChecker

class DocLinkNode(
val target: DocLink,
val body: String,
Expand Down Expand Up @@ -45,7 +47,7 @@ object DocFlexmarkParser {
}
}

case class DocFlexmarkRenderer(renderLink: (DocLink, String) => String, snippetCheckingFunc: (String, Option[snippets.SnippetCompilerArg]) => Unit)
case class DocFlexmarkRenderer(renderLink: (DocLink, String) => String, snippetCheckingFunc: SnippetChecker.SnippetCheckingFunc)
extends HtmlRenderer.HtmlRendererExtension:

def rendererOptions(opt: MutableDataHolder): Unit = () // noop
Expand All @@ -59,7 +61,7 @@ case class DocFlexmarkRenderer(renderLink: (DocLink, String) => String, snippetC
.map(_.stripPrefix("sc:"))
.map(snippets.SnippetCompilerArgParser.parse)
.flatMap(_.toOption)
snippetCheckingFunc(node.getContentChars.toString, argOverride)
snippetCheckingFunc(node.getContentChars.toString, node.getStartLineNumber, argOverride)
c.delegateRender()

object Handler extends CustomNodeRenderer[DocLinkNode]:
Expand All @@ -80,6 +82,6 @@ case class DocFlexmarkRenderer(renderLink: (DocLink, String) => String, snippetC
htmlRendererBuilder.nodeRendererFactory(Factory)

object DocFlexmarkRenderer:
def render(node: Node)(renderLink: (DocLink, String) => String, snippetCheckingFunc: (String, Option[snippets.SnippetCompilerArg]) => Unit) =
def render(node: Node)(renderLink: (DocLink, String) => String, snippetCheckingFunc: SnippetChecker.SnippetCheckingFunc) =
val opts = MarkdownParser.mkMarkdownOptions(Seq(DocFlexmarkRenderer(renderLink, snippetCheckingFunc)))
HtmlRenderer.builder(opts).build().render(node)
HtmlRenderer.builder(opts).build().render(node)

0 comments on commit 61a74c5

Please sign in to comment.