-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PrettyPrint signature and update metac.expect
- Loading branch information
1 parent
d21fe59
commit 9c6015e
Showing
6 changed files
with
1,590 additions
and
1,272 deletions.
There are no files selected for viewing
120 changes: 120 additions & 0 deletions
120
compiler/src/dotty/tools/dotc/semanticdb/Descriptor.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.