Skip to content

Commit

Permalink
Merge pull request #3 from BarkingBad/scaladoc/snippet-compiler-line-…
Browse files Browse the repository at this point in the history
…debug

Add returning real line of error in source file of snippet for snippet scaladoc compiler
  • Loading branch information
pikinier20 committed Mar 25, 2021
2 parents 3eafba5 + 33a773c commit 70d5815
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 @@ -144,10 +144,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 70d5815

Please sign in to comment.