Skip to content

Commit

Permalink
PrettyPrint signature and update metac.expect
Browse files Browse the repository at this point in the history
  • Loading branch information
tanishiking committed Jun 24, 2021
1 parent d21fe59 commit 9c6015e
Show file tree
Hide file tree
Showing 6 changed files with 1,590 additions and 1,272 deletions.
120 changes: 120 additions & 0 deletions compiler/src/dotty/tools/dotc/semanticdb/Descriptor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package dotty.tools.dotc.semanticdb

import java.lang.System.{lineSeparator => EOL}
import dotty.tools.dotc.semanticdb.{Descriptor => d}

class DescriptorParser(s: String) {
var i = s.length
def fail() = {
val message = "invalid symbol format"
val caret = " " * i + "^"
sys.error(s"$message$EOL$s$EOL$caret")
}

val BOF = '\u0000'
val EOF = '\u001A'
var currChar = EOF
def readChar(): Char = {
if (i <= 0) {
if (i == 0) {
i -= 1
currChar = BOF
currChar
} else {
fail()
}
} else {
i -= 1
currChar = s(i)
currChar
}
}

def parseValue(): String = {
if (currChar == '`') {
val end = i
while (readChar() != '`') {}
readChar()
s.substring(i + 2, end)
} else {
val end = i + 1
if (!Character.isJavaIdentifierPart(currChar)) fail()
while (Character.isJavaIdentifierPart(readChar()) && currChar != BOF) {}
s.substring(i + 1, end)
}
}

def parseDisambiguator(): String = {
val end = i + 1
if (currChar != ')') fail()
while (readChar() != '(') {}
readChar()
s.substring(i + 1, end)
}

def parseDescriptor(): Descriptor = {
if (currChar == '.') {
readChar()
if (currChar == ')') {
val disambiguator = parseDisambiguator()
val value = parseValue()
d.Method(value, disambiguator)
} else {
d.Term(parseValue())
}
} else if (currChar == '#') {
readChar()
d.Type(parseValue())
} else if (currChar == '/') {
readChar()
d.Package(parseValue())
} else if (currChar == ')') {
readChar()
val value = parseValue()
if (currChar != '(') fail()
else readChar()
d.Parameter(value)
} else if (currChar == ']') {
readChar()
val value = parseValue()
if (currChar != '[') fail()
else readChar()
d.TypeParameter(value)
} else {
fail()
}
}

def entryPoint(): (Descriptor, String) = {
readChar()
val desc = parseDescriptor()
(desc, s.substring(0, i + 1))
}
}

object DescriptorParser {
def apply(symbol: String): (Descriptor, String) = {
val parser = new DescriptorParser(symbol)
parser.entryPoint()
}
}

sealed trait Descriptor {
def isNone: Boolean = this == d.None
def isTerm: Boolean = this.isInstanceOf[d.Term]
def isMethod: Boolean = this.isInstanceOf[d.Method]
def isType: Boolean = this.isInstanceOf[d.Type]
def isPackage: Boolean = this.isInstanceOf[d.Package]
def isParameter: Boolean = this.isInstanceOf[d.Parameter]
def isTypeParameter: Boolean = this.isInstanceOf[d.TypeParameter]
def value: String
}
object Descriptor {
case object None extends Descriptor { def value: String = "" }
final case class Term(value: String) extends Descriptor
final case class Method(value: String, disambiguator: String) extends Descriptor
final case class Type(value: String) extends Descriptor
final case class Package(value: String) extends Descriptor
final case class Parameter(value: String) extends Descriptor
final case class TypeParameter(value: String) extends Descriptor
}
252 changes: 252 additions & 0 deletions compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
package dotty.tools.dotc.semanticdb

import dotty.tools.dotc.{semanticdb => s}

import scala.collection.mutable
import dotty.tools.dotc.semanticdb.Scala3.{_, given}
import SymbolInformation.Kind._

class SymbolInfomationPrinter (symtab: PrinterSymtab):
val notes = InfoNotes()
val infoPrinter = InfoPrinter(notes)

def pprintSymbolInformation(info: SymbolInformation): String =
val sb = new StringBuilder()
sb.append(info.symbol).append(" => ")
sb.append(infoPrinter.pprint(info))
sb.toString

class InfoNotes:
private val noteSymtab = mutable.Map[String, SymbolInformation]()
def enter(info: SymbolInformation) =
if (symtab.info(info.symbol).isEmpty && info.kind != UNKNOWN_KIND)
noteSymtab(info.symbol) = info

def visit(sym: String): SymbolInformation =
val symtabInfo = noteSymtab.get(sym).orElse(symtab.info(sym))
symtabInfo.getOrElse {
val displayName = if sym.isGlobal then sym.desc.value else sym
SymbolInformation(symbol = sym, displayName = displayName)
}

class InfoPrinter(notes: InfoNotes) {
private enum SymbolStyle:
case Reference, Definition
def pprint(info: SymbolInformation): String =
val sb = new StringBuilder()
if info.isAbstract then sb.append("abstract ")
if info.isFinal then sb.append("final ")
if info.isSealed then sb.append("sealed ")
if info.isImplicit then sb.append("implicit ")
if info.isLazy then sb.append("lazy ")
if info.isCase then sb.append("case ")
if info.isCovariant then sb.append("covariant ")
if info.isContravariant then sb.append("contravariant ")
if info.isVal then sb.append("val ")
if info.isVar then sb.append("var ")
if info.isStatic then sb.append("static ")
if info.isPrimary then sb.append("primary ")
if info.isEnum then sb.append("enum ")
if info.isDefault then sb.append("default ")
info.kind match
case LOCAL => sb.append("local ")
case FIELD => sb.append("field ")
case METHOD => sb.append("method ")
case CONSTRUCTOR => sb.append("ctor ")
case MACRO => sb.append("macro ")
case TYPE => sb.append("type ")
case PARAMETER => sb.append("param ")
case SELF_PARAMETER => sb.append("selfparam ")
case TYPE_PARAMETER => sb.append("typeparam ")
case OBJECT => sb.append("object ")
case PACKAGE => sb.append("package ")
case PACKAGE_OBJECT => sb.append("package object ")
case CLASS => sb.append("class ")
case TRAIT => sb.append("trait ")
case INTERFACE => sb.append("interface ")
case UNKNOWN_KIND | Unrecognized(_) => sb.append("unknown ")
sb.append(s"${info.displayName}${info.prefixBeforeTpe}${pprint(info.signature)}")
sb.toString

private def pprintDef(info: SymbolInformation) =
notes.enter(info)
pprint(info.symbol, SymbolStyle.Definition)
private def pprintRef(sym: String): String = pprint(sym, SymbolStyle.Reference)
private def pprintDef(sym: String): String = pprint(sym, SymbolStyle.Definition)
private def pprint(sym: String, style: SymbolStyle): String =
val info = notes.visit(sym)
style match
case SymbolStyle.Reference =>
info.displayName
case SymbolStyle.Definition =>
pprint(info)


private def pprint(sig: Signature): String =
sig match
case ClassSignature(tparams, parents, self, decls) =>
val sb = new StringBuilder()
if (tparams.infos.nonEmpty)
sb.append(tparams.infos.map(pprintDef).mkString("[", ", ", "] "))
if (parents.nonEmpty)
sb.append(parents.map(pprint).mkString("extends ", " with ", " "))
if (self.isDefined || decls.infos.nonEmpty) {
val selfStr = if (self.isDefined) s"self: ${pprint(self)} =>" else ""
val declsStr = if (decls.infos.nonEmpty) s"+${decls.infos.length} decls" else ""
sb.append(s"{ ${selfStr} ${declsStr} }")
}
sb.toString
case MethodSignature(tparams, paramss, res) =>
val sb = new StringBuilder()
if (tparams.infos.nonEmpty)
sb.append(tparams.infos.map(pprintDef).mkString("[", ", ", "]"))
paramss.foreach { params =>
val paramsStr = params.infos.map(pprintDef).mkString("(", ", ", ")")
sb.append(paramsStr)
}
sb.append(s": ${pprint(res)}")
sb.toString
case TypeSignature(tparams, lo, hi) =>
val sb = new StringBuilder()
if (tparams.infos.nonEmpty)
sb.append(tparams.infos.map(pprintDef).mkString("[", ", ", "]"))
if (lo == hi) {
sb.append(s" = ${pprint(lo)}")
} else {
lo match
case TypeRef(Type.Empty, "scala/Nothing#", Nil) => ()
case lo => sb.append(s" >: ${pprint(lo)}")
hi match
case TypeRef(Type.Empty, "scala/Any#", Nil) => ()
case TypeRef(Type.Empty, "java/lang/Object#", Nil) => ()
case hi => s" <: ${pprint(hi)}"
}
sb.toString
case ValueSignature(tpe) =>
pprint(tpe)
case _ =>
"<?>"

private def pprint(tpe: Type): String = {
def prefix(tpe: Type): String = tpe match
case TypeRef(pre, sym, args) =>
val preStr = pre match {
case _: SingleType | _: ThisType | _: SuperType =>
s"${prefix(pre)}."
case Type.Empty => ""
case _ =>
s"${prefix(pre)}#"
}
val argsStr = if (args.nonEmpty) args.map(normal).mkString("[", ", ", "]") else ""
s"${preStr}${pprintRef(sym)}${argsStr}"
case SingleType(pre, sym) =>
s"${prefix(pre)}.${pprintRef(sym)}"
case ThisType(sym) =>
s"${pprintRef(sym)}.this"
case SuperType(pre, sym) =>
s"${prefix(pre)}.super[${pprintRef(sym)}]"
case ConstantType(const) =>
pprint(const)
case IntersectionType(types) =>
types.map(normal).mkString(" & ")
case UnionType(types) =>
types.map(normal).mkString(" | ")
case WithType(types) =>
types.map(normal).mkString(" with ")
case StructuralType(utpe, decls) =>
val declsStr =
if (decls.infos.nonEmpty)
s"{ ${decls.infos.map(pprintDef).mkString("; ")} }"
else "{}"
s"${normal(utpe)} ${declsStr}"
case AnnotatedType(anns, utpe) =>
s"${normal(utpe)} ${anns.map(pprint).mkString(" ")}"
// case ExistentialType(utpe, decls) => // Scala3 shouldn't emit ExistentialType
case UniversalType(tparams, utpe) =>
val params = tparams.infos.map(_.displayName).mkString("[", ", ", "]")
val resType = normal(utpe)
s"${params} => ${resType}"
case ByNameType(utpe) =>
s"=> ${normal(utpe)}"
case RepeatedType(utpe) =>
s"${normal(utpe)}*"
case _ =>
"<?>"

def normal(tpe: Type): String = tpe match
case _: SingleType | _: ThisType | _: SuperType =>
s"${prefix(tpe)}.type"
case _ =>
prefix(tpe)
normal(tpe)
}

private def pprint(ann: Annotation): String =
ann.tpe match {
case Type.Empty => s"@<?>"
case tpe => s"@${pprint(tpe)}"
}

private def pprint(const: Constant): String = const match {
case Constant.Empty =>
"<?>"
case UnitConstant() =>
"()"
case BooleanConstant(true) =>
"true"
case BooleanConstant(false) =>
"false"
case ByteConstant(value) =>
value.toByte.toString
case ShortConstant(value) =>
value.toShort.toString
case CharConstant(value) =>
s"'${value.toChar.toString}'"
case IntConstant(value) =>
value.toString
case LongConstant(value) =>
s"${value.toString}L"
case FloatConstant(value) =>
s"${value.toString}f"
case DoubleConstant(value) =>
value.toString
case StringConstant(value) =>
"\"" + value + "\""
case NullConstant() =>
"null"
}

extension (scope: Scope)
private def infos: List[SymbolInformation] =
if (scope.symlinks.nonEmpty)
scope.symlinks.map(symbol => SymbolInformation(symbol = symbol)).toList
else
scope.hardlinks.toList

extension (scope: Option[Scope])
private def infos: List[SymbolInformation] = scope match {
case Some(s) => s.infos
case None => Nil
}
}
end SymbolInfomationPrinter

extension (info: SymbolInformation)
def prefixBeforeTpe: String = {
info.kind match {
case LOCAL | FIELD | PARAMETER | SELF_PARAMETER | UNKNOWN_KIND | Unrecognized(_) =>
": "
case METHOD | CONSTRUCTOR | MACRO | TYPE | TYPE_PARAMETER | OBJECT | PACKAGE |
PACKAGE_OBJECT | CLASS | TRAIT | INTERFACE =>
" "
}
}

trait PrinterSymtab:
def info(symbol: String): Option[SymbolInformation]
object PrinterSymtab:
def fromTextDocument(doc: TextDocument): PrinterSymtab =
val map = doc.symbols.map(info => (info.symbol, info)).toMap
new PrinterSymtab {
override def info(symbol: String): Option[SymbolInformation] = map.get(symbol)
}
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ object Scala3:
def isTypeParameter: Boolean = !symbol.isEmpty && !symbol.isMulti && symbol.last == ']'
def isParameter: Boolean = !symbol.isEmpty && !symbol.isMulti && symbol.last == ')'

def desc: Descriptor =
if isGlobal then DescriptorParser(symbol)._1
else Descriptor.None

def unescapeUnicode =
unicodeEscape.replaceAllIn(symbol, m => String.valueOf(Integer.parseInt(m.group(1), 16).toChar))

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/semanticdb/SymbolOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ object SymbolOps:
extension (sym: Symbol)
def sig(using LinkMode, Context, SemanticSymbolBuilder): s.Signature =
import TypeOps._
// println("")
val sig = sym.info.toSemanticSig(sym)
// println("")
// println(sym.toString)
// println(s"=========sym.info================")
// pprint.pprintln(sym.info)
Expand Down
Loading

0 comments on commit 9c6015e

Please sign in to comment.