Skip to content

Commit c8f03db

Browse files
committed
Further enhancements of snippet contexts
1 parent c118f8d commit c8f03db

File tree

8 files changed

+214
-67
lines changed

8 files changed

+214
-67
lines changed

project/Build.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1713,7 +1713,6 @@ object Build {
17131713
s"docs=github://lampepfl/dotty/master#docs",
17141714
"-doc-root-content", docRootFile.toString,
17151715
"-snippet-compiler:" +
1716-
s"$dottyLibRoot/scala/quoted=nocompile," +
17171716
s"$dottyLibRoot=compile",
17181717
"-Ydocument-synthetic-types"
17191718
)

scaladoc-testcases/src/tests/snippetCompilerTest.scala

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,83 @@ package snippetCompiler
4242
* a()
4343
* ```
4444
*/
45-
class A { }
45+
class A {
46+
trait B
47+
val a = new B {
48+
/**
49+
* ```scala sc:compile
50+
* 2 + List()
51+
* ```
52+
*
53+
*/
54+
def a = 3
55+
}
56+
}
4657

4758
/**
4859
* ```scala sc:compile
4960
* val c: Int = 4.5
5061
* ```
5162
*/
52-
class B { }
63+
class B { }
64+
65+
trait Quotes {
66+
val reflect: reflectModule = ???
67+
trait reflectModule { self: reflect.type =>
68+
/**
69+
* ```scala sc:compile
70+
* 2 + List()
71+
* ```
72+
*
73+
*/
74+
def a = 3
75+
}
76+
}
77+
78+
trait Quotes2[A] {
79+
val r1: r1Module[_] = ???
80+
trait r1Module[A] {
81+
type X
82+
object Y {
83+
/**
84+
* ```scala sc:compile
85+
* 2 + List()
86+
* ```
87+
*
88+
*/
89+
type YY
90+
}
91+
val z: zModule = ???
92+
trait zModule {
93+
/**
94+
* ```scala sc:compile
95+
* 2 + List()
96+
* ```
97+
*
98+
*/
99+
type ZZ
100+
}
101+
}
102+
object r2 {
103+
type X
104+
object Y {
105+
/**
106+
* ```scala sc:compile
107+
* 2 + List()
108+
* ```
109+
*
110+
*/
111+
type YY
112+
}
113+
val z: zModule = ???
114+
trait zModule {
115+
/**
116+
* ```scala sc:compile
117+
* 2 + List()
118+
* ```
119+
*
120+
*/
121+
type ZZ
122+
}
123+
}
124+
}

scaladoc/src/dotty/tools/scaladoc/api.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,11 @@ case class TastyMemberSource(path: java.nio.file.Path, lineNumber: Int)
236236

237237
object SnippetCompilerData:
238238
case class Position(line: Int, column: Int)
239+
case class ClassInfo(tpe: Option[String], names: Seq[String], generics: Option[String])
239240

240241
case class SnippetCompilerData(
241242
packageName: String,
242-
classType: Option[String],
243-
classGenerics: Option[String],
243+
classInfos: Seq[SnippetCompilerData.ClassInfo],
244244
imports: List[String],
245245
position: SnippetCompilerData.Position
246246
)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package dotty.tools.scaladoc
2+
package snippets
3+
4+
import dotty.tools.dotc.printing.RefinedPrinter
5+
import dotty.tools.dotc.core._
6+
import dotty.tools.dotc.printing.Texts._
7+
import dotty.tools.dotc.core.Types._
8+
import dotty.tools.dotc.core.Flags._
9+
import dotty.tools.dotc.core.Names._
10+
import dotty.tools.dotc.core.Symbols._
11+
import dotty.tools.dotc.core.NameOps._
12+
import dotty.tools.dotc.core.TypeErasure.ErasedValueType
13+
import dotty.tools.dotc.core.Contexts._
14+
import dotty.tools.dotc.core.Annotations.Annotation
15+
import dotty.tools.dotc.core.Denotations._
16+
import dotty.tools.dotc.core.SymDenotations._
17+
import dotty.tools.dotc.core.StdNames.{nme, tpnme}
18+
import dotty.tools.dotc.ast.{Trees, untpd}
19+
import dotty.tools.dotc.typer.{Implicits, Namer, Applications}
20+
import dotty.tools.dotc.typer.ProtoTypes._
21+
import dotty.tools.dotc.ast.Trees._
22+
import dotty.tools.dotc.core.TypeApplications._
23+
import dotty.tools.dotc.core.Decorators._
24+
import dotty.tools.dotc.util.Chars.isOperatorPart
25+
import dotty.tools.dotc.transform.TypeUtils._
26+
import dotty.tools.dotc.transform.SymUtils._
27+
28+
import language.implicitConversions
29+
import dotty.tools.dotc.util.{NameTransformer, SourcePosition}
30+
import dotty.tools.dotc.ast.untpd.{MemberDef, Modifiers, PackageDef, RefTree, Template, TypeDef, ValOrDefDef}
31+
32+
class SelfTypePrinter(using _ctx: Context) extends RefinedPrinter(_ctx):
33+
34+
override def toTextSingleton(tp: SingletonType): Text =
35+
tp match
36+
case ConstantType(value) =>
37+
if value.tag == Constants.ByteTag || value.tag == Constants.ShortTag then
38+
toText(value) ~ s" /*${value.tpe.show}*/"
39+
else
40+
toText(value)
41+
case _: TermRef => toTextRef(tp) ~ ".type /*" ~ toTextGlobal(tp.underlying) ~ "*/"
42+
case _ => "(" ~ toTextRef(tp) ~ ": " ~ toTextGlobal(tp.underlying) ~ ")"

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ class SnippetChecker(val classpath: String, val tastyDirs: Seq[File]):
2525
val wrapped = WrappedSnippet(
2626
snippet,
2727
data.map(_.packageName),
28-
data.flatMap(_.classType),
29-
data.flatMap(_.classGenerics),
28+
data.fold(Nil)(_.classInfos),
3029
data.map(_.imports).getOrElse(Nil),
3130
lineOffset + data.fold(0)(_.position.line) + 1,
3231
data.fold(0)(_.position.column)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package dotty.tools.scaladoc
2+
package snippets
3+
4+
import scala.quoted._
5+
import dotty.tools.scaladoc.tasty.SymOps
6+
import dotty.tools.dotc.core._
7+
8+
class SnippetCompilerDataCollector[Q <: Quotes](val qctx: Q):
9+
import qctx.reflect._
10+
object SymOps extends SymOps[qctx.type](qctx)
11+
export SymOps._
12+
13+
def getSnippetCompilerData(sym: Symbol): SnippetCompilerData =
14+
val packageName = sym.packageName
15+
if !sym.isPackageDef then sym.tree match {
16+
case c: qctx.reflect.ClassDef =>
17+
import dotty.tools.dotc
18+
import dotty.tools.dotc.core.Decorators._
19+
given dotc.core.Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
20+
val printer: dotc.printing.Printer = SelfTypePrinter()
21+
val classSym = c.symbol.asInstanceOf[Symbols.ClassSymbol]
22+
23+
def getOwners(sym: Symbols.ClassSymbol): Seq[Symbols.ClassSymbol] = Seq(sym) ++
24+
(if sym.owner.isClass && !sym.owner.is(dotc.core.Flags.Package) then getOwners(sym.owner.asInstanceOf[Symbols.ClassSymbol]) else Nil)
25+
26+
def collectNames(tp: Types.Type): Seq[String] = tp match {
27+
case Types.AndType(t1, t2) => collectNames(t1) ++ collectNames(t2)
28+
case Types.AppliedType(tpe, _) => collectNames(tpe)
29+
case Types.AnnotatedType(tpe, _) => collectNames(tpe)
30+
case t: Types.NamedType => if t.symbol.is(dotc.core.Flags.Module) then Seq() else Seq(t.symbol.name.show)
31+
case t: Types.ThisType => Seq(t.cls.name.show)
32+
case _ => Seq()
33+
}
34+
35+
val allSyms = getOwners(classSym)
36+
37+
val allProcessed = allSyms.map { cSym =>
38+
def createTypeConstructor(tpe: Types.Type, topLevel: Boolean = true): String = tpe match {
39+
case t @ Types.TypeBounds(upper, lower) => lower match {
40+
case l: Types.HKTypeLambda =>
41+
(if topLevel then "" else "?") + l.paramInfos.map(p => createTypeConstructor(p, false)).mkString("[",", ","]")
42+
case _ => (if topLevel then "" else "_")
43+
}
44+
}
45+
val classType =
46+
val ct = cSym.classInfo.selfType.toText(printer).show.replace(".this","").stripPrefix(s"$packageName.")
47+
Some(ct)
48+
val classNames = collectNames(cSym.classInfo.selfType)
49+
val classGenerics = Option.when(
50+
!cSym.typeParams.isEmpty
51+
)(
52+
cSym.typeParams.map(_.typeRef).map(t =>
53+
t.show +
54+
createTypeConstructor(t.asInstanceOf[Types.TypeRef].underlying)
55+
).mkString("[",", ","]")
56+
)
57+
SnippetCompilerData.ClassInfo(classType, classNames, classGenerics)
58+
}
59+
val firstProcessed = allProcessed.head
60+
SnippetCompilerData(packageName, allProcessed.reverse, Nil, position(hackGetPositionOfDocstring(using qctx)(sym)))
61+
case _ => getSnippetCompilerData(sym.maybeOwner)
62+
} else SnippetCompilerData(packageName, Nil, Nil, position(hackGetPositionOfDocstring(using qctx)(sym)))
63+
64+
private def position(p: Option[qctx.reflect.Position]): SnippetCompilerData.Position =
65+
p.fold(SnippetCompilerData.Position(0, 0))(p => SnippetCompilerData.Position(p.startLine, p.startColumn))
66+
67+
private def hackGetPositionOfDocstring(using Quotes)(s: qctx.reflect.Symbol): Option[qctx.reflect.Position] =
68+
import dotty.tools.dotc.core.Comments.CommentsContext
69+
import dotty.tools.dotc
70+
given ctx: Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
71+
val docCtx = ctx.docCtx.getOrElse {
72+
throw new RuntimeException(
73+
"DocCtx could not be found and documentations are unavailable. This is a compiler-internal error."
74+
)
75+
}
76+
val span = docCtx.docstring(s.asInstanceOf[Symbols.Symbol]).span
77+
s.pos.flatMap { pos =>
78+
docCtx.docstring(s.asInstanceOf[Symbols.Symbol]).map { docstring =>
79+
dotty.tools.dotc.util.SourcePosition(
80+
pos.sourceFile.asInstanceOf[dotty.tools.dotc.util.SourceFile],
81+
docstring.span
82+
).asInstanceOf[qctx.reflect.Position]
83+
}
84+
}

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ object WrappedSnippet:
2222
def apply(
2323
str: String,
2424
packageName: Option[String],
25-
className: Option[String],
26-
classGenerics: Option[String],
25+
classInfos: Seq[SnippetCompilerData.ClassInfo],
2726
imports: List[String],
2827
lineOffset: Int,
2928
columnOffset: Int
@@ -32,10 +31,15 @@ object WrappedSnippet:
3231
val ps = new PrintStream(baos)
3332
ps.println(s"package ${packageName.getOrElse("snippets")}")
3433
imports.foreach(i => ps.println(s"import $i"))
35-
ps.println(s"trait Snippet${classGenerics.getOrElse("")} { ${className.fold("")(cn => s"self: $cn =>")}")
36-
str.split('\n').foreach(ps.printlnWithIndent(2, _))
37-
ps.println("}")
38-
WrappedSnippet(baos.toString, lineOffset, columnOffset, lineBoilerplate, columnBoilerplate)
34+
classInfos.zipWithIndex.foreach { (info, i) =>
35+
ps.printlnWithIndent(2 * i, s"trait Snippet$i${info.generics.getOrElse("")} { ${info.tpe.fold("")(cn => s"self: $cn =>")}")
36+
info.names.foreach{ name =>
37+
ps.printlnWithIndent(2 * i + 2, s"val $name = self")
38+
}
39+
}
40+
str.split('\n').foreach(ps.printlnWithIndent(classInfos.size * 2, _))
41+
(0 to classInfos.size -1).reverse.foreach( i => ps.printlnWithIndent(i * 2, "}"))
42+
WrappedSnippet(baos.toString, lineOffset, columnOffset, classInfos.size + classInfos.flatMap(_.names).size, classInfos.size * 2 + 2)
3943

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

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

Lines changed: 1 addition & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -124,64 +124,11 @@ abstract class MarkupConversion[T](val repr: Repr)(using dctx: DocContext) {
124124
case _ => None
125125
}
126126

127-
private def getSnippetCompilerData(sym: qctx.reflect.Symbol): SnippetCompilerData =
128-
val packageName = sym.packageName
129-
if !sym.isPackageDef then sym.tree match {
130-
case c: qctx.reflect.ClassDef =>
131-
import qctx.reflect._
132-
import dotty.tools.dotc
133-
given dotc.core.Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
134-
val cSym = c.symbol.asInstanceOf[dotc.core.Symbols.ClassSymbol]
135-
136-
def createTypeConstructor(tpe: dotc.core.Types.Type, topLevel: Boolean = true): String = tpe match {
137-
case t @ dotc.core.Types.TypeBounds(upper, lower) => lower match {
138-
case l: dotc.core.Types.HKTypeLambda =>
139-
(if topLevel then "" else "?") + l.paramInfos.map(p => createTypeConstructor(p, false)).mkString("[",", ","]")
140-
case _ => (if topLevel then "" else "_")
141-
}
142-
}
143-
val classType =
144-
val ct = cSym.classInfo.selfType.show.replace(".this.",".")
145-
Some(ct)
146-
val classGenerics = Option.when(
147-
!cSym.typeParams.isEmpty
148-
)(
149-
cSym.typeParams.map(_.typeRef).map(t =>
150-
t.show +
151-
createTypeConstructor(t.asInstanceOf[dotc.core.Types.TypeRef].underlying)
152-
).mkString("[",", ","]")
153-
)
154-
SnippetCompilerData(packageName, classType, classGenerics, Nil, position(hackGetPositionOfDocstring(using qctx)(sym)))
155-
case _ => getSnippetCompilerData(sym.maybeOwner)
156-
} else SnippetCompilerData(packageName, None, None, Nil, position(hackGetPositionOfDocstring(using qctx)(sym)))
157-
158-
private def position(p: Option[qctx.reflect.Position]): SnippetCompilerData.Position =
159-
p.fold(SnippetCompilerData.Position(0, 0))(p => SnippetCompilerData.Position(p.startLine, p.startColumn))
160-
161-
private def hackGetPositionOfDocstring(using Quotes)(s: qctx.reflect.Symbol): Option[qctx.reflect.Position] =
162-
import dotty.tools.dotc.core.Comments.CommentsContext
163-
import dotty.tools.dotc
164-
given ctx: dotc.core.Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
165-
val docCtx = ctx.docCtx.getOrElse {
166-
throw new RuntimeException(
167-
"DocCtx could not be found and documentations are unavailable. This is a compiler-internal error."
168-
)
169-
}
170-
val span = docCtx.docstring(s.asInstanceOf[dotc.core.Symbols.Symbol]).span
171-
s.pos.flatMap { pos =>
172-
docCtx.docstring(s.asInstanceOf[dotc.core.Symbols.Symbol]).map { docstring =>
173-
dotty.tools.dotc.util.SourcePosition(
174-
pos.sourceFile.asInstanceOf[dotty.tools.dotc.util.SourceFile],
175-
docstring.span
176-
).asInstanceOf[qctx.reflect.Position]
177-
}
178-
}
179-
180127
def snippetCheckingFunc: qctx.reflect.Symbol => SnippetChecker.SnippetCheckingFunc =
181128
(s: qctx.reflect.Symbol) => {
182129
val path = s.source.map(_.path)
183130
val pathBasedArg = dctx.snippetCompilerArgs.get(path)
184-
val data = getSnippetCompilerData(s)
131+
val data = SnippetCompilerDataCollector[qctx.type](qctx).getSnippetCompilerData(s)
185132
(str: String, lineOffset: SnippetChecker.LineOffset, argOverride: Option[SCFlags]) => {
186133
val arg = argOverride.fold(pathBasedArg)(pathBasedArg.overrideFlag(_))
187134

0 commit comments

Comments
 (0)