Skip to content

Commit

Permalink
Merge pull request #13528 from KacperFKorban/scaladoc/fix-compiletime…
Browse files Browse the repository at this point in the history
…-snippets

Fix snippets in scala.compiletime and fix snippet compiler reporting line numbers in specific cases
  • Loading branch information
KacperFKorban committed Sep 21, 2021
2 parents 3a6a949 + 87db32f commit 87546c3
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 24 deletions.
6 changes: 3 additions & 3 deletions library/src/scala/compiletime/ops/int.scala
Expand Up @@ -9,7 +9,7 @@ object int:
* case 0 => 1
* case 1 => 2
* case 2 => 3
* ...
* // ...
* case 2147483646 => 2147483647
* }
* ```
Expand Down Expand Up @@ -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
*/
Expand Down
35 changes: 28 additions & 7 deletions library/src/scala/compiletime/package.scala
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -116,14 +128,23 @@ 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
* case given B => 2
* case given C => 3
* case _ => 4
* }
* //{
* }
* //}
* ```
* the returned value would be `2`.
* @syntax markdown
Expand Down
2 changes: 1 addition & 1 deletion project/Build.scala
Expand Up @@ -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") }
Expand Down
35 changes: 35 additions & 0 deletions 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
2 changes: 1 addition & 1 deletion scaladoc/src/dotty/tools/scaladoc/site/templates.scala
Expand Up @@ -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
Expand Down
Expand Up @@ -8,17 +8,18 @@ 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 {
case fcb: mda.FencedCodeBlock => fcb
}.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
Expand Down
Expand Up @@ -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(
Expand Down
Expand Up @@ -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)
case class SnippetCompilerSetting[T](setting: Setting[T], value: T)
11 changes: 6 additions & 5 deletions scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala
Expand Up @@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Up @@ -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)
Expand Down Expand Up @@ -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 =>
Expand Down Expand Up @@ -177,6 +177,7 @@ object Preparser {
hideImplicitConversions = allTags(SimpleTagKey("hideImplicitConversion")),
shortDescription = allTags(SimpleTagKey("shortDescription")),
syntax = allTags(SimpleTagKey("syntax")),
strippedLinesBeforeNo = strippedLinesBeforeNo
)

cmt
Expand Down
Expand Up @@ -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)

0 comments on commit 87546c3

Please sign in to comment.