Skip to content

Commit 9c6015e

Browse files
committed
PrettyPrint signature and update metac.expect
1 parent d21fe59 commit 9c6015e

File tree

6 files changed

+1590
-1272
lines changed

6 files changed

+1590
-1272
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package dotty.tools.dotc.semanticdb
2+
3+
import java.lang.System.{lineSeparator => EOL}
4+
import dotty.tools.dotc.semanticdb.{Descriptor => d}
5+
6+
class DescriptorParser(s: String) {
7+
var i = s.length
8+
def fail() = {
9+
val message = "invalid symbol format"
10+
val caret = " " * i + "^"
11+
sys.error(s"$message$EOL$s$EOL$caret")
12+
}
13+
14+
val BOF = '\u0000'
15+
val EOF = '\u001A'
16+
var currChar = EOF
17+
def readChar(): Char = {
18+
if (i <= 0) {
19+
if (i == 0) {
20+
i -= 1
21+
currChar = BOF
22+
currChar
23+
} else {
24+
fail()
25+
}
26+
} else {
27+
i -= 1
28+
currChar = s(i)
29+
currChar
30+
}
31+
}
32+
33+
def parseValue(): String = {
34+
if (currChar == '`') {
35+
val end = i
36+
while (readChar() != '`') {}
37+
readChar()
38+
s.substring(i + 2, end)
39+
} else {
40+
val end = i + 1
41+
if (!Character.isJavaIdentifierPart(currChar)) fail()
42+
while (Character.isJavaIdentifierPart(readChar()) && currChar != BOF) {}
43+
s.substring(i + 1, end)
44+
}
45+
}
46+
47+
def parseDisambiguator(): String = {
48+
val end = i + 1
49+
if (currChar != ')') fail()
50+
while (readChar() != '(') {}
51+
readChar()
52+
s.substring(i + 1, end)
53+
}
54+
55+
def parseDescriptor(): Descriptor = {
56+
if (currChar == '.') {
57+
readChar()
58+
if (currChar == ')') {
59+
val disambiguator = parseDisambiguator()
60+
val value = parseValue()
61+
d.Method(value, disambiguator)
62+
} else {
63+
d.Term(parseValue())
64+
}
65+
} else if (currChar == '#') {
66+
readChar()
67+
d.Type(parseValue())
68+
} else if (currChar == '/') {
69+
readChar()
70+
d.Package(parseValue())
71+
} else if (currChar == ')') {
72+
readChar()
73+
val value = parseValue()
74+
if (currChar != '(') fail()
75+
else readChar()
76+
d.Parameter(value)
77+
} else if (currChar == ']') {
78+
readChar()
79+
val value = parseValue()
80+
if (currChar != '[') fail()
81+
else readChar()
82+
d.TypeParameter(value)
83+
} else {
84+
fail()
85+
}
86+
}
87+
88+
def entryPoint(): (Descriptor, String) = {
89+
readChar()
90+
val desc = parseDescriptor()
91+
(desc, s.substring(0, i + 1))
92+
}
93+
}
94+
95+
object DescriptorParser {
96+
def apply(symbol: String): (Descriptor, String) = {
97+
val parser = new DescriptorParser(symbol)
98+
parser.entryPoint()
99+
}
100+
}
101+
102+
sealed trait Descriptor {
103+
def isNone: Boolean = this == d.None
104+
def isTerm: Boolean = this.isInstanceOf[d.Term]
105+
def isMethod: Boolean = this.isInstanceOf[d.Method]
106+
def isType: Boolean = this.isInstanceOf[d.Type]
107+
def isPackage: Boolean = this.isInstanceOf[d.Package]
108+
def isParameter: Boolean = this.isInstanceOf[d.Parameter]
109+
def isTypeParameter: Boolean = this.isInstanceOf[d.TypeParameter]
110+
def value: String
111+
}
112+
object Descriptor {
113+
case object None extends Descriptor { def value: String = "" }
114+
final case class Term(value: String) extends Descriptor
115+
final case class Method(value: String, disambiguator: String) extends Descriptor
116+
final case class Type(value: String) extends Descriptor
117+
final case class Package(value: String) extends Descriptor
118+
final case class Parameter(value: String) extends Descriptor
119+
final case class TypeParameter(value: String) extends Descriptor
120+
}
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
package dotty.tools.dotc.semanticdb
2+
3+
import dotty.tools.dotc.{semanticdb => s}
4+
5+
import scala.collection.mutable
6+
import dotty.tools.dotc.semanticdb.Scala3.{_, given}
7+
import SymbolInformation.Kind._
8+
9+
class SymbolInfomationPrinter (symtab: PrinterSymtab):
10+
val notes = InfoNotes()
11+
val infoPrinter = InfoPrinter(notes)
12+
13+
def pprintSymbolInformation(info: SymbolInformation): String =
14+
val sb = new StringBuilder()
15+
sb.append(info.symbol).append(" => ")
16+
sb.append(infoPrinter.pprint(info))
17+
sb.toString
18+
19+
class InfoNotes:
20+
private val noteSymtab = mutable.Map[String, SymbolInformation]()
21+
def enter(info: SymbolInformation) =
22+
if (symtab.info(info.symbol).isEmpty && info.kind != UNKNOWN_KIND)
23+
noteSymtab(info.symbol) = info
24+
25+
def visit(sym: String): SymbolInformation =
26+
val symtabInfo = noteSymtab.get(sym).orElse(symtab.info(sym))
27+
symtabInfo.getOrElse {
28+
val displayName = if sym.isGlobal then sym.desc.value else sym
29+
SymbolInformation(symbol = sym, displayName = displayName)
30+
}
31+
32+
class InfoPrinter(notes: InfoNotes) {
33+
private enum SymbolStyle:
34+
case Reference, Definition
35+
def pprint(info: SymbolInformation): String =
36+
val sb = new StringBuilder()
37+
if info.isAbstract then sb.append("abstract ")
38+
if info.isFinal then sb.append("final ")
39+
if info.isSealed then sb.append("sealed ")
40+
if info.isImplicit then sb.append("implicit ")
41+
if info.isLazy then sb.append("lazy ")
42+
if info.isCase then sb.append("case ")
43+
if info.isCovariant then sb.append("covariant ")
44+
if info.isContravariant then sb.append("contravariant ")
45+
if info.isVal then sb.append("val ")
46+
if info.isVar then sb.append("var ")
47+
if info.isStatic then sb.append("static ")
48+
if info.isPrimary then sb.append("primary ")
49+
if info.isEnum then sb.append("enum ")
50+
if info.isDefault then sb.append("default ")
51+
info.kind match
52+
case LOCAL => sb.append("local ")
53+
case FIELD => sb.append("field ")
54+
case METHOD => sb.append("method ")
55+
case CONSTRUCTOR => sb.append("ctor ")
56+
case MACRO => sb.append("macro ")
57+
case TYPE => sb.append("type ")
58+
case PARAMETER => sb.append("param ")
59+
case SELF_PARAMETER => sb.append("selfparam ")
60+
case TYPE_PARAMETER => sb.append("typeparam ")
61+
case OBJECT => sb.append("object ")
62+
case PACKAGE => sb.append("package ")
63+
case PACKAGE_OBJECT => sb.append("package object ")
64+
case CLASS => sb.append("class ")
65+
case TRAIT => sb.append("trait ")
66+
case INTERFACE => sb.append("interface ")
67+
case UNKNOWN_KIND | Unrecognized(_) => sb.append("unknown ")
68+
sb.append(s"${info.displayName}${info.prefixBeforeTpe}${pprint(info.signature)}")
69+
sb.toString
70+
71+
private def pprintDef(info: SymbolInformation) =
72+
notes.enter(info)
73+
pprint(info.symbol, SymbolStyle.Definition)
74+
private def pprintRef(sym: String): String = pprint(sym, SymbolStyle.Reference)
75+
private def pprintDef(sym: String): String = pprint(sym, SymbolStyle.Definition)
76+
private def pprint(sym: String, style: SymbolStyle): String =
77+
val info = notes.visit(sym)
78+
style match
79+
case SymbolStyle.Reference =>
80+
info.displayName
81+
case SymbolStyle.Definition =>
82+
pprint(info)
83+
84+
85+
private def pprint(sig: Signature): String =
86+
sig match
87+
case ClassSignature(tparams, parents, self, decls) =>
88+
val sb = new StringBuilder()
89+
if (tparams.infos.nonEmpty)
90+
sb.append(tparams.infos.map(pprintDef).mkString("[", ", ", "] "))
91+
if (parents.nonEmpty)
92+
sb.append(parents.map(pprint).mkString("extends ", " with ", " "))
93+
if (self.isDefined || decls.infos.nonEmpty) {
94+
val selfStr = if (self.isDefined) s"self: ${pprint(self)} =>" else ""
95+
val declsStr = if (decls.infos.nonEmpty) s"+${decls.infos.length} decls" else ""
96+
sb.append(s"{ ${selfStr} ${declsStr} }")
97+
}
98+
sb.toString
99+
case MethodSignature(tparams, paramss, res) =>
100+
val sb = new StringBuilder()
101+
if (tparams.infos.nonEmpty)
102+
sb.append(tparams.infos.map(pprintDef).mkString("[", ", ", "]"))
103+
paramss.foreach { params =>
104+
val paramsStr = params.infos.map(pprintDef).mkString("(", ", ", ")")
105+
sb.append(paramsStr)
106+
}
107+
sb.append(s": ${pprint(res)}")
108+
sb.toString
109+
case TypeSignature(tparams, lo, hi) =>
110+
val sb = new StringBuilder()
111+
if (tparams.infos.nonEmpty)
112+
sb.append(tparams.infos.map(pprintDef).mkString("[", ", ", "]"))
113+
if (lo == hi) {
114+
sb.append(s" = ${pprint(lo)}")
115+
} else {
116+
lo match
117+
case TypeRef(Type.Empty, "scala/Nothing#", Nil) => ()
118+
case lo => sb.append(s" >: ${pprint(lo)}")
119+
hi match
120+
case TypeRef(Type.Empty, "scala/Any#", Nil) => ()
121+
case TypeRef(Type.Empty, "java/lang/Object#", Nil) => ()
122+
case hi => s" <: ${pprint(hi)}"
123+
}
124+
sb.toString
125+
case ValueSignature(tpe) =>
126+
pprint(tpe)
127+
case _ =>
128+
"<?>"
129+
130+
private def pprint(tpe: Type): String = {
131+
def prefix(tpe: Type): String = tpe match
132+
case TypeRef(pre, sym, args) =>
133+
val preStr = pre match {
134+
case _: SingleType | _: ThisType | _: SuperType =>
135+
s"${prefix(pre)}."
136+
case Type.Empty => ""
137+
case _ =>
138+
s"${prefix(pre)}#"
139+
}
140+
val argsStr = if (args.nonEmpty) args.map(normal).mkString("[", ", ", "]") else ""
141+
s"${preStr}${pprintRef(sym)}${argsStr}"
142+
case SingleType(pre, sym) =>
143+
s"${prefix(pre)}.${pprintRef(sym)}"
144+
case ThisType(sym) =>
145+
s"${pprintRef(sym)}.this"
146+
case SuperType(pre, sym) =>
147+
s"${prefix(pre)}.super[${pprintRef(sym)}]"
148+
case ConstantType(const) =>
149+
pprint(const)
150+
case IntersectionType(types) =>
151+
types.map(normal).mkString(" & ")
152+
case UnionType(types) =>
153+
types.map(normal).mkString(" | ")
154+
case WithType(types) =>
155+
types.map(normal).mkString(" with ")
156+
case StructuralType(utpe, decls) =>
157+
val declsStr =
158+
if (decls.infos.nonEmpty)
159+
s"{ ${decls.infos.map(pprintDef).mkString("; ")} }"
160+
else "{}"
161+
s"${normal(utpe)} ${declsStr}"
162+
case AnnotatedType(anns, utpe) =>
163+
s"${normal(utpe)} ${anns.map(pprint).mkString(" ")}"
164+
// case ExistentialType(utpe, decls) => // Scala3 shouldn't emit ExistentialType
165+
case UniversalType(tparams, utpe) =>
166+
val params = tparams.infos.map(_.displayName).mkString("[", ", ", "]")
167+
val resType = normal(utpe)
168+
s"${params} => ${resType}"
169+
case ByNameType(utpe) =>
170+
s"=> ${normal(utpe)}"
171+
case RepeatedType(utpe) =>
172+
s"${normal(utpe)}*"
173+
case _ =>
174+
"<?>"
175+
176+
def normal(tpe: Type): String = tpe match
177+
case _: SingleType | _: ThisType | _: SuperType =>
178+
s"${prefix(tpe)}.type"
179+
case _ =>
180+
prefix(tpe)
181+
normal(tpe)
182+
}
183+
184+
private def pprint(ann: Annotation): String =
185+
ann.tpe match {
186+
case Type.Empty => s"@<?>"
187+
case tpe => s"@${pprint(tpe)}"
188+
}
189+
190+
private def pprint(const: Constant): String = const match {
191+
case Constant.Empty =>
192+
"<?>"
193+
case UnitConstant() =>
194+
"()"
195+
case BooleanConstant(true) =>
196+
"true"
197+
case BooleanConstant(false) =>
198+
"false"
199+
case ByteConstant(value) =>
200+
value.toByte.toString
201+
case ShortConstant(value) =>
202+
value.toShort.toString
203+
case CharConstant(value) =>
204+
s"'${value.toChar.toString}'"
205+
case IntConstant(value) =>
206+
value.toString
207+
case LongConstant(value) =>
208+
s"${value.toString}L"
209+
case FloatConstant(value) =>
210+
s"${value.toString}f"
211+
case DoubleConstant(value) =>
212+
value.toString
213+
case StringConstant(value) =>
214+
"\"" + value + "\""
215+
case NullConstant() =>
216+
"null"
217+
}
218+
219+
extension (scope: Scope)
220+
private def infos: List[SymbolInformation] =
221+
if (scope.symlinks.nonEmpty)
222+
scope.symlinks.map(symbol => SymbolInformation(symbol = symbol)).toList
223+
else
224+
scope.hardlinks.toList
225+
226+
extension (scope: Option[Scope])
227+
private def infos: List[SymbolInformation] = scope match {
228+
case Some(s) => s.infos
229+
case None => Nil
230+
}
231+
}
232+
end SymbolInfomationPrinter
233+
234+
extension (info: SymbolInformation)
235+
def prefixBeforeTpe: String = {
236+
info.kind match {
237+
case LOCAL | FIELD | PARAMETER | SELF_PARAMETER | UNKNOWN_KIND | Unrecognized(_) =>
238+
": "
239+
case METHOD | CONSTRUCTOR | MACRO | TYPE | TYPE_PARAMETER | OBJECT | PACKAGE |
240+
PACKAGE_OBJECT | CLASS | TRAIT | INTERFACE =>
241+
" "
242+
}
243+
}
244+
245+
trait PrinterSymtab:
246+
def info(symbol: String): Option[SymbolInformation]
247+
object PrinterSymtab:
248+
def fromTextDocument(doc: TextDocument): PrinterSymtab =
249+
val map = doc.symbols.map(info => (info.symbol, info)).toMap
250+
new PrinterSymtab {
251+
override def info(symbol: String): Option[SymbolInformation] = map.get(symbol)
252+
}

compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ object Scala3:
164164
def isTypeParameter: Boolean = !symbol.isEmpty && !symbol.isMulti && symbol.last == ']'
165165
def isParameter: Boolean = !symbol.isEmpty && !symbol.isMulti && symbol.last == ')'
166166

167+
def desc: Descriptor =
168+
if isGlobal then DescriptorParser(symbol)._1
169+
else Descriptor.None
170+
167171
def unescapeUnicode =
168172
unicodeEscape.replaceAllIn(symbol, m => String.valueOf(Integer.parseInt(m.group(1), 16).toChar))
169173

compiler/src/dotty/tools/dotc/semanticdb/SymbolOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ object SymbolOps:
1515
extension (sym: Symbol)
1616
def sig(using LinkMode, Context, SemanticSymbolBuilder): s.Signature =
1717
import TypeOps._
18-
// println("")
1918
val sig = sym.info.toSemanticSig(sym)
19+
// println("")
2020
// println(sym.toString)
2121
// println(s"=========sym.info================")
2222
// pprint.pprintln(sym.info)

0 commit comments

Comments
 (0)