Skip to content

Commit 87546c3

Browse files
Merge pull request #13528 from KacperFKorban/scaladoc/fix-compiletime-snippets
Fix snippets in scala.compiletime and fix snippet compiler reporting line numbers in specific cases
2 parents 3a6a949 + 87db32f commit 87546c3

File tree

11 files changed

+85
-24
lines changed

11 files changed

+85
-24
lines changed

library/src/scala/compiletime/ops/int.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ object int:
99
* case 0 => 1
1010
* case 1 => 2
1111
* case 2 => 3
12-
* ...
12+
* // ...
1313
* case 2147483646 => 2147483647
1414
* }
1515
* ```
@@ -152,8 +152,8 @@ object int:
152152

153153
/** Negation of an `Int` singleton type.
154154
* ```scala
155-
* val neg1: Neg[-1] = 1
156-
* val neg2: Neg[1] = -1
155+
* val neg1: Negate[-1] = 1
156+
* val neg2: Negate[1] = -1
157157
* ```
158158
* @syntax markdown
159159
*/

library/src/scala/compiletime/package.scala

+28-7
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,17 @@ import annotation.compileTimeOnly
77
* pattern match on it. For example, given a type `Tup <: Tuple`, one can
88
* pattern-match on it as follows:
99
* ```scala
10+
* //{
11+
* type Tup
12+
* inline def f = {
13+
* //}
1014
* inline erasedValue[Tup] match {
11-
* case _: EmptyTuple => ...
12-
* case _: h *: t => ...
15+
* case _: EmptyTuple => ???
16+
* case _: (h *: t) => ???
1317
* }
18+
* //{
19+
* }
20+
* //}
1421
* ```
1522
* This value can only be used in an inline match and the value cannot be used in
1623
* the branches.
@@ -21,7 +28,12 @@ def erasedValue[T]: T = ???
2128

2229
/** Used as the initializer of a mutable class or object field, like this:
2330
*
24-
* var x: T = uninitialized
31+
* ```scala
32+
* //{
33+
* type T
34+
* //}
35+
* var x: T = uninitialized
36+
* ```
2537
*
2638
* This signifies that the field is not initialized on its own. It is still initialized
2739
* as part of the bulk initialization of the object it belongs to, which assigns zero
@@ -33,7 +45,7 @@ def uninitialized: Nothing = ???
3345
/** The error method is used to produce user-defined compile errors during inline expansion.
3446
* If an inline expansion results in a call error(msgStr) the compiler produces an error message containing the given msgStr.
3547
*
36-
* ```scala
48+
* ```scala sc:fail
3749
* error("My error message")
3850
* ```
3951
* or
@@ -71,13 +83,13 @@ transparent inline def codeOf(arg: Any): String =
7183
* inlining and constant folding.
7284
*
7385
* Usage:
74-
* ```scala
86+
* ```scala sc:fail
7587
* inline def twice(inline n: Int): Int =
7688
* requireConst(n) // compile-time assertion that the parameter `n` is a constant
7789
* n + n
7890
*
7991
* twice(1)
80-
* val m: Int = ...
92+
* val m: Int = ???
8193
* twice(m) // error: expected a constant value but found: m
8294
* ```
8395
* @syntax markdown
@@ -116,14 +128,23 @@ end constValueTuple
116128
/** Summons first given matching one of the listed cases. E.g. in
117129
*
118130
* ```scala
119-
* given B { ... }
131+
* //{
132+
* type A
133+
* trait B
134+
* type C
135+
* inline def f = {
136+
* //}
137+
* given B with { }
120138
*
121139
* summonFrom {
122140
* case given A => 1
123141
* case given B => 2
124142
* case given C => 3
125143
* case _ => 4
126144
* }
145+
* //{
146+
* }
147+
* //}
127148
* ```
128149
* the returned value would be `2`.
129150
* @syntax markdown

project/Build.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1418,7 +1418,7 @@ object Build {
14181418
"-versions-dictionary-url",
14191419
"https://scala-lang.org/api/versions.json",
14201420
"-Ydocument-synthetic-types",
1421-
s"-snippet-compiler:${dottyLibRoot}/scala/quoted=compile"
1421+
s"-snippet-compiler:${dottyLibRoot}/scala/quoted=compile,${dottyLibRoot}/scala/compiletime=compile"
14221422
) ++ (if (justAPI) Nil else Seq("-siteroot", "docs", "-Yapi-subdirectory")))
14231423

14241424
if (dottyJars.isEmpty) Def.task { streams.value.log.error("Dotty lib wasn't found") }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package tests
2+
3+
package snippetTestcase3
4+
5+
class SnippetTestcase3:
6+
/** Text on line 0.
7+
*
8+
* SNIPPET(OUTERLINEOFFSET:11,OUTERCOLUMNOFFSET:6,INNERLINEOFFSET:5,INNERCOLUMNOFFSET:2)
9+
* ERROR(LINE:11,COLUMN:8)
10+
* ```scala sc:fail
11+
* 2 + List()
12+
* ```
13+
*/
14+
def a = 3
15+
/**
16+
* Text on line 1.
17+
*
18+
* SNIPPET(OUTERLINEOFFSET:21,OUTERCOLUMNOFFSET:6,INNERLINEOFFSET:5,INNERCOLUMNOFFSET:2)
19+
* ERROR(LINE:21,COLUMN:8)
20+
* ```scala sc:fail
21+
* 2 + List()
22+
* ```
23+
*/
24+
def b = 3
25+
/**
26+
*
27+
* Text on line 2.
28+
*
29+
* SNIPPET(OUTERLINEOFFSET:32,OUTERCOLUMNOFFSET:6,INNERLINEOFFSET:5,INNERCOLUMNOFFSET:2)
30+
* ERROR(LINE:32,COLUMN:8)
31+
* ```scala sc:fail
32+
* 2 + List()
33+
* ```
34+
*/
35+
def c = 3

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ case class TemplateFile(
106106
// Snippet compiler currently supports markdown only
107107
val parser: Parser = Parser.builder(defaultMarkdownOptions).build()
108108
val parsedMd = parser.parse(rendered)
109-
val processed = FlexmarkSnippetProcessor.processSnippets(parsedMd, snippetCheckingFunc, withContext = false)(using ssctx.outerCtx)
109+
val processed = FlexmarkSnippetProcessor.processSnippets(parsedMd, None, snippetCheckingFunc, withContext = false)(using ssctx.outerCtx)
110110
HtmlRenderer.builder(defaultMarkdownOptions).build().render(processed)
111111

112112
layoutTemplate match

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@ import com.vladsch.flexmark.util.options.MutableDataSet
88
import collection.JavaConverters._
99

1010
import dotty.tools.scaladoc.tasty.comments.markdown.ExtendedFencedCodeBlock
11+
import dotty.tools.scaladoc.tasty.comments.PreparsedComment
1112

1213
object FlexmarkSnippetProcessor:
13-
def processSnippets(root: mdu.Node, checkingFunc: => SnippetChecker.SnippetCheckingFunc, withContext: Boolean)(using CompilerContext): mdu.Node = {
14+
def processSnippets(root: mdu.Node, preparsed: Option[PreparsedComment], checkingFunc: => SnippetChecker.SnippetCheckingFunc, withContext: Boolean)(using CompilerContext): mdu.Node = {
1415
lazy val cf: SnippetChecker.SnippetCheckingFunc = checkingFunc
1516

1617
val nodes = root.getDescendants().asScala.collect {
1718
case fcb: mda.FencedCodeBlock => fcb
1819
}.toList
1920

2021
nodes.foldLeft[Map[String, String]](Map()) { (snippetMap, node) =>
21-
val lineOffset = node.getStartLineNumber
22+
val lineOffset = node.getStartLineNumber + preparsed.fold(0)(_.strippedLinesBeforeNo)
2223
val info = node.getInfo.toString.split(" ")
2324
if info.contains("scala") then {
2425
val argOverride = info

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class SnippetChecker(val args: Scaladoc.Args)(using cctx: CompilerContext):
3333

3434
// These constants were found empirically to make snippet compiler
3535
// report errors in the same position as main compiler.
36-
private val constantLineOffset = 3
36+
private val constantLineOffset = 2
3737
private val constantColumnOffset = 4
3838

3939
def checkSnippet(

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ import dotty.tools.scaladoc.DocContext
55
import dotty.tools.dotc.config.Settings._
66
import dotty.tools.dotc.config.ScalaSettings
77

8-
case class SnippetCompilerSetting[T](setting: Setting[T], value: T)
8+
case class SnippetCompilerSetting[T](setting: Setting[T], value: T)

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

+6-5
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ case class PreparsedComment(
6767
hideImplicitConversions: List[String],
6868
shortDescription: List[String],
6969
syntax: List[String],
70+
strippedLinesBeforeNo: Int,
7071
)
7172

7273
case class DokkaCommentBody(summary: Option[DocPart], body: DocPart)
@@ -78,7 +79,7 @@ abstract class MarkupConversion[T](val repr: Repr)(using dctx: DocContext) {
7879
protected def markupToDokkaCommentBody(t: T): DokkaCommentBody
7980
protected def filterEmpty(xs: List[String]): List[T]
8081
protected def filterEmpty(xs: SortedMap[String, String]): SortedMap[String, T]
81-
protected def processSnippets(t: T): T
82+
protected def processSnippets(t: T, preparsed: PreparsedComment): T
8283

8384
lazy val snippetChecker = dctx.snippetChecker
8485

@@ -141,7 +142,7 @@ abstract class MarkupConversion[T](val repr: Repr)(using dctx: DocContext) {
141142

142143
final def parse(preparsed: PreparsedComment): Comment =
143144
val markup = stringToMarkup(preparsed.body)
144-
val body = markupToDokkaCommentBody(processSnippets(markup))
145+
val body = markupToDokkaCommentBody(processSnippets(markup, preparsed))
145146
Comment(
146147
body = body.body,
147148
short = body.summary,
@@ -193,8 +194,8 @@ class MarkdownCommentParser(repr: Repr)(using dctx: DocContext)
193194
.filterNot { case (_, v) => v.isEmpty }
194195
.mapValues(stringToMarkup).to(SortedMap)
195196

196-
def processSnippets(root: mdu.Node): mdu.Node =
197-
FlexmarkSnippetProcessor.processSnippets(root, snippetCheckingFunc(owner), withContext = true)
197+
def processSnippets(root: mdu.Node, preparsed: PreparsedComment): mdu.Node =
198+
FlexmarkSnippetProcessor.processSnippets(root, Some(preparsed), snippetCheckingFunc(owner), withContext = true)
198199
}
199200

200201
class WikiCommentParser(repr: Repr)(using DocContext)
@@ -249,6 +250,6 @@ class WikiCommentParser(repr: Repr)(using DocContext)
249250
xs.view.mapValues(stringToMarkup).to(SortedMap)
250251
.filterNot { case (_, v) => v.blocks.isEmpty }
251252

252-
def processSnippets(root: wiki.Body): wiki.Body =
253+
def processSnippets(root: wiki.Body, preparsed: PreparsedComment): wiki.Body =
253254
// Currently not supported
254255
root

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

+4-3
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ object Preparser {
3232
tags: Map[TagKey, List[String]],
3333
lastTagKey: Option[TagKey],
3434
remaining: List[String],
35-
inCodeBlock: Boolean
36-
): PreparsedComment = remaining match {
35+
inCodeBlock: Boolean,
36+
)(using strippedLinesBeforeNo: Int = 0): PreparsedComment = remaining match {
3737
case CodeBlockStartRegex(before, marker, after) :: ls if !inCodeBlock =>
3838
if (!before.trim.isEmpty && !after.trim.isEmpty && marker == "```")
3939
go(docBody, tags, lastTagKey, before :: (marker + after) :: ls, inCodeBlock = false)
@@ -108,7 +108,7 @@ object Preparser {
108108
case line :: ls =>
109109
if docBody.length > 0 then docBody.append(endOfLine)
110110
docBody.append(line)
111-
go(docBody, tags, lastTagKey, ls, inCodeBlock)
111+
go(docBody, tags, lastTagKey, ls, inCodeBlock)(using strippedLinesBeforeNo + (if line.isEmpty && docBody.length == 0 then 1 else 0))
112112

113113

114114
case Nil =>
@@ -177,6 +177,7 @@ object Preparser {
177177
hideImplicitConversions = allTags(SimpleTagKey("hideImplicitConversion")),
178178
shortDescription = allTags(SimpleTagKey("shortDescription")),
179179
syntax = allTags(SimpleTagKey("syntax")),
180+
strippedLinesBeforeNo = strippedLinesBeforeNo
180181
)
181182

182183
cmt

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

+2
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ class SnippetE2eTestcase1 extends SnippetsE2eTest("snippetTestcase1", SCFlags.Co
66

77
class SnippetE2eTestcase2 extends SnippetsE2eTest("snippetTestcase2", SCFlags.Compile)
88

9+
10+
class SnippetE2eTestcase3 extends SnippetsE2eTest("snippetTestcase3", SCFlags.Compile)

0 commit comments

Comments
 (0)