From 310769a04c02c39984aac63cc9777d17afde22cd Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Tue, 14 Sep 2021 15:29:25 +0200 Subject: [PATCH 1/3] Fix snippets in scala.compiletime --- library/src/scala/compiletime/ops/int.scala | 6 ++-- library/src/scala/compiletime/package.scala | 35 ++++++++++++++++----- project/Build.scala | 2 +- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/library/src/scala/compiletime/ops/int.scala b/library/src/scala/compiletime/ops/int.scala index 0aa390ce29fd..70339eed12e7 100644 --- a/library/src/scala/compiletime/ops/int.scala +++ b/library/src/scala/compiletime/ops/int.scala @@ -9,7 +9,7 @@ object int: * case 0 => 1 * case 1 => 2 * case 2 => 3 - * ... + * // ... * case 2147483646 => 2147483647 * } * ``` @@ -152,8 +152,8 @@ object int: /** Negation of an `Int` singleton type. * ```scala - * val neg1: Neg[-1] = 1 - * val neg2: Neg[1] = -1 + * val neg1: Negate[-1] = 1 + * val neg2: Negate[1] = -1 * ``` * @syntax markdown */ diff --git a/library/src/scala/compiletime/package.scala b/library/src/scala/compiletime/package.scala index b465a8b5e01e..d65fd235e0c3 100644 --- a/library/src/scala/compiletime/package.scala +++ b/library/src/scala/compiletime/package.scala @@ -7,10 +7,17 @@ import annotation.compileTimeOnly * pattern match on it. For example, given a type `Tup <: Tuple`, one can * pattern-match on it as follows: * ```scala + * //{ + * type Tup + * inline def f = { + * //} * inline erasedValue[Tup] match { - * case _: EmptyTuple => ... - * case _: h *: t => ... + * case _: EmptyTuple => ??? + * case _: (h *: t) => ??? * } + * //{ + * } + * //} * ``` * This value can only be used in an inline match and the value cannot be used in * the branches. @@ -21,7 +28,12 @@ def erasedValue[T]: T = ??? /** Used as the initializer of a mutable class or object field, like this: * - * var x: T = uninitialized + * ```scala + * //{ + * type T + * //} + * var x: T = uninitialized + * ``` * * This signifies that the field is not initialized on its own. It is still initialized * as part of the bulk initialization of the object it belongs to, which assigns zero @@ -33,7 +45,7 @@ def uninitialized: Nothing = ??? /** The error method is used to produce user-defined compile errors during inline expansion. * If an inline expansion results in a call error(msgStr) the compiler produces an error message containing the given msgStr. * - * ```scala + * ```scala sc:fail * error("My error message") * ``` * or @@ -71,13 +83,13 @@ transparent inline def codeOf(arg: Any): String = * inlining and constant folding. * * Usage: - * ```scala + * ```scala sc:fail * inline def twice(inline n: Int): Int = * requireConst(n) // compile-time assertion that the parameter `n` is a constant * n + n * * twice(1) - * val m: Int = ... + * val m: Int = ??? * twice(m) // error: expected a constant value but found: m * ``` * @syntax markdown @@ -116,7 +128,13 @@ end constValueTuple /** Summons first given matching one of the listed cases. E.g. in * * ```scala - * given B { ... } + * //{ + * type A + * trait B + * type C + * inline def f = { + * //} + * given B with { } * * summonFrom { * case given A => 1 @@ -124,6 +142,9 @@ end constValueTuple * case given C => 3 * case _ => 4 * } + * //{ + * } + * //} * ``` * the returned value would be `2`. * @syntax markdown diff --git a/project/Build.scala b/project/Build.scala index 791047dbacb8..ff2592836ed9 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1418,7 +1418,7 @@ object Build { "-versions-dictionary-url", "https://scala-lang.org/api/versions.json", "-Ydocument-synthetic-types", - s"-snippet-compiler:${dottyLibRoot}/scala/quoted=compile" + s"-snippet-compiler:${dottyLibRoot}/scala/quoted=compile,${dottyLibRoot}/scala/compiletime=compile" ) ++ (if (justAPI) Nil else Seq("-siteroot", "docs", "-Yapi-subdirectory"))) if (dottyJars.isEmpty) Def.task { streams.value.log.error("Dotty lib wasn't found") } From 39e7fec63083b44d8dbaf5be3b754a9e66108482 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Tue, 14 Sep 2021 15:30:27 +0200 Subject: [PATCH 2/3] Fix snippet compiler reporting line numbers in specific cases --- .../src/dotty/tools/scaladoc/site/templates.scala | 2 +- .../scaladoc/snippets/FlexmarkSnippetProcessor.scala | 5 +++-- .../tools/scaladoc/snippets/SnippetChecker.scala | 2 +- .../scaladoc/snippets/SnippetCompilerSetting.scala | 2 +- .../tools/scaladoc/tasty/comments/Comments.scala | 11 ++++++----- .../tools/scaladoc/tasty/comments/Preparser.scala | 7 ++++--- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/site/templates.scala b/scaladoc/src/dotty/tools/scaladoc/site/templates.scala index 6a796cc8adef..6d59a87e5f85 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/templates.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/templates.scala @@ -106,7 +106,7 @@ case class TemplateFile( // Snippet compiler currently supports markdown only val parser: Parser = Parser.builder(defaultMarkdownOptions).build() val parsedMd = parser.parse(rendered) - val processed = FlexmarkSnippetProcessor.processSnippets(parsedMd, snippetCheckingFunc, withContext = false)(using ssctx.outerCtx) + val processed = FlexmarkSnippetProcessor.processSnippets(parsedMd, None, snippetCheckingFunc, withContext = false)(using ssctx.outerCtx) HtmlRenderer.builder(defaultMarkdownOptions).build().render(processed) layoutTemplate match diff --git a/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala b/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala index cd017633b0f0..499e1c55a17c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala +++ b/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala @@ -8,9 +8,10 @@ import com.vladsch.flexmark.util.options.MutableDataSet import collection.JavaConverters._ import dotty.tools.scaladoc.tasty.comments.markdown.ExtendedFencedCodeBlock +import dotty.tools.scaladoc.tasty.comments.PreparsedComment object FlexmarkSnippetProcessor: - def processSnippets(root: mdu.Node, checkingFunc: => SnippetChecker.SnippetCheckingFunc, withContext: Boolean)(using CompilerContext): mdu.Node = { + def processSnippets(root: mdu.Node, preparsed: Option[PreparsedComment], checkingFunc: => SnippetChecker.SnippetCheckingFunc, withContext: Boolean)(using CompilerContext): mdu.Node = { lazy val cf: SnippetChecker.SnippetCheckingFunc = checkingFunc val nodes = root.getDescendants().asScala.collect { @@ -18,7 +19,7 @@ object FlexmarkSnippetProcessor: }.toList nodes.foldLeft[Map[String, String]](Map()) { (snippetMap, node) => - val lineOffset = node.getStartLineNumber + val lineOffset = node.getStartLineNumber + preparsed.fold(0)(_.strippedLinesBeforeNo) val info = node.getInfo.toString.split(" ") if info.contains("scala") then { val argOverride = info diff --git a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetChecker.scala b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetChecker.scala index 46869b7114b3..906c0d2434b0 100644 --- a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetChecker.scala +++ b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetChecker.scala @@ -33,7 +33,7 @@ class SnippetChecker(val args: Scaladoc.Args)(using cctx: CompilerContext): // These constants were found empirically to make snippet compiler // report errors in the same position as main compiler. - private val constantLineOffset = 3 + private val constantLineOffset = 2 private val constantColumnOffset = 4 def checkSnippet( diff --git a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompilerSetting.scala b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompilerSetting.scala index 4c5ef500c83a..8f3ef6498fec 100644 --- a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompilerSetting.scala +++ b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompilerSetting.scala @@ -5,4 +5,4 @@ import dotty.tools.scaladoc.DocContext import dotty.tools.dotc.config.Settings._ import dotty.tools.dotc.config.ScalaSettings -case class SnippetCompilerSetting[T](setting: Setting[T], value: T) \ No newline at end of file +case class SnippetCompilerSetting[T](setting: Setting[T], value: T) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala index 04afe4f8f07a..9f3cbdbcf384 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala @@ -67,6 +67,7 @@ case class PreparsedComment( hideImplicitConversions: List[String], shortDescription: List[String], syntax: List[String], + strippedLinesBeforeNo: Int, ) case class DokkaCommentBody(summary: Option[DocPart], body: DocPart) @@ -78,7 +79,7 @@ abstract class MarkupConversion[T](val repr: Repr)(using dctx: DocContext) { protected def markupToDokkaCommentBody(t: T): DokkaCommentBody protected def filterEmpty(xs: List[String]): List[T] protected def filterEmpty(xs: SortedMap[String, String]): SortedMap[String, T] - protected def processSnippets(t: T): T + protected def processSnippets(t: T, preparsed: PreparsedComment): T lazy val snippetChecker = dctx.snippetChecker @@ -141,7 +142,7 @@ abstract class MarkupConversion[T](val repr: Repr)(using dctx: DocContext) { final def parse(preparsed: PreparsedComment): Comment = val markup = stringToMarkup(preparsed.body) - val body = markupToDokkaCommentBody(processSnippets(markup)) + val body = markupToDokkaCommentBody(processSnippets(markup, preparsed)) Comment( body = body.body, short = body.summary, @@ -193,8 +194,8 @@ class MarkdownCommentParser(repr: Repr)(using dctx: DocContext) .filterNot { case (_, v) => v.isEmpty } .mapValues(stringToMarkup).to(SortedMap) - def processSnippets(root: mdu.Node): mdu.Node = - FlexmarkSnippetProcessor.processSnippets(root, snippetCheckingFunc(owner), withContext = true) + def processSnippets(root: mdu.Node, preparsed: PreparsedComment): mdu.Node = + FlexmarkSnippetProcessor.processSnippets(root, Some(preparsed), snippetCheckingFunc(owner), withContext = true) } class WikiCommentParser(repr: Repr)(using DocContext) @@ -249,6 +250,6 @@ class WikiCommentParser(repr: Repr)(using DocContext) xs.view.mapValues(stringToMarkup).to(SortedMap) .filterNot { case (_, v) => v.blocks.isEmpty } - def processSnippets(root: wiki.Body): wiki.Body = + def processSnippets(root: wiki.Body, preparsed: PreparsedComment): wiki.Body = // Currently not supported root diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Preparser.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Preparser.scala index a311b006f437..89c538d8d32d 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Preparser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Preparser.scala @@ -32,8 +32,8 @@ object Preparser { tags: Map[TagKey, List[String]], lastTagKey: Option[TagKey], remaining: List[String], - inCodeBlock: Boolean - ): PreparsedComment = remaining match { + inCodeBlock: Boolean, + )(using strippedLinesBeforeNo: Int = 0): PreparsedComment = remaining match { case CodeBlockStartRegex(before, marker, after) :: ls if !inCodeBlock => if (!before.trim.isEmpty && !after.trim.isEmpty && marker == "```") go(docBody, tags, lastTagKey, before :: (marker + after) :: ls, inCodeBlock = false) @@ -108,7 +108,7 @@ object Preparser { case line :: ls => if docBody.length > 0 then docBody.append(endOfLine) docBody.append(line) - go(docBody, tags, lastTagKey, ls, inCodeBlock) + go(docBody, tags, lastTagKey, ls, inCodeBlock)(using strippedLinesBeforeNo + (if line.isEmpty && docBody.length == 0 then 1 else 0)) case Nil => @@ -177,6 +177,7 @@ object Preparser { hideImplicitConversions = allTags(SimpleTagKey("hideImplicitConversion")), shortDescription = allTags(SimpleTagKey("shortDescription")), syntax = allTags(SimpleTagKey("syntax")), + strippedLinesBeforeNo = strippedLinesBeforeNo ) cmt From 87db32fb171a81c69542af07a2ce8bf0edb445ef Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Wed, 15 Sep 2021 11:20:20 +0200 Subject: [PATCH 3/3] Add snippet compiler reporting tests --- .../src/tests/snippetTestcase3.scala | 35 +++++++++++++++++++ .../snippets/SnippetsE2eTestcases.scala | 2 ++ 2 files changed, 37 insertions(+) create mode 100644 scaladoc-testcases/src/tests/snippetTestcase3.scala diff --git a/scaladoc-testcases/src/tests/snippetTestcase3.scala b/scaladoc-testcases/src/tests/snippetTestcase3.scala new file mode 100644 index 000000000000..bb7def9a4b4c --- /dev/null +++ b/scaladoc-testcases/src/tests/snippetTestcase3.scala @@ -0,0 +1,35 @@ +package tests + +package snippetTestcase3 + +class SnippetTestcase3: + /** Text on line 0. + * + * SNIPPET(OUTERLINEOFFSET:11,OUTERCOLUMNOFFSET:6,INNERLINEOFFSET:5,INNERCOLUMNOFFSET:2) + * ERROR(LINE:11,COLUMN:8) + * ```scala sc:fail + * 2 + List() + * ``` + */ + def a = 3 + /** + * Text on line 1. + * + * SNIPPET(OUTERLINEOFFSET:21,OUTERCOLUMNOFFSET:6,INNERLINEOFFSET:5,INNERCOLUMNOFFSET:2) + * ERROR(LINE:21,COLUMN:8) + * ```scala sc:fail + * 2 + List() + * ``` + */ + def b = 3 + /** + * + * Text on line 2. + * + * SNIPPET(OUTERLINEOFFSET:32,OUTERCOLUMNOFFSET:6,INNERLINEOFFSET:5,INNERCOLUMNOFFSET:2) + * ERROR(LINE:32,COLUMN:8) + * ```scala sc:fail + * 2 + List() + * ``` + */ + def c = 3 \ No newline at end of file diff --git a/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTestcases.scala b/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTestcases.scala index 5d97e049e2df..7ec71600c68a 100644 --- a/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTestcases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTestcases.scala @@ -6,3 +6,5 @@ class SnippetE2eTestcase1 extends SnippetsE2eTest("snippetTestcase1", SCFlags.Co class SnippetE2eTestcase2 extends SnippetsE2eTest("snippetTestcase2", SCFlags.Compile) + +class SnippetE2eTestcase3 extends SnippetsE2eTest("snippetTestcase3", SCFlags.Compile)