Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add TypeApplication synthetics #5

Open
wants to merge 9 commits into
base: synthetic-implicit-conversion
Choose a base branch
from
66 changes: 35 additions & 31 deletions compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import NameOps._
import util.Spans.Span
import util.{SourceFile, SourcePosition}
import transform.SymUtils._
import SymbolInformation.{Kind => k}

import scala.jdk.CollectionConverters._
import scala.collection.mutable
Expand Down Expand Up @@ -46,12 +45,13 @@ class ExtractSemanticDB extends Phase:
val unit = ctx.compilationUnit
val extractor = Extractor()
extractor.extract(unit.tpdTree)
ExtractSemanticDB.write(unit.source, extractor.occurrences.toList, extractor.symbolInfos.toList)
ExtractSemanticDB.write(unit.source, extractor.occurrences.toList, extractor.symbolInfos.toList, extractor.synthetics.toList)

/** Extractor of symbol occurrences from trees */
class Extractor extends TreeTraverser:
given s.SemanticSymbolBuilder = s.SemanticSymbolBuilder()
val converter = s.TypeOps()
given builder: s.SemanticSymbolBuilder = s.SemanticSymbolBuilder()
val synth = SyntheticsExtractor()
given converter: s.TypeOps = s.TypeOps()

/** The bodies of synthetic locals */
private val localBodies = mutable.HashMap[Symbol, Tree]()
Expand All @@ -62,6 +62,8 @@ class ExtractSemanticDB extends Phase:
/** The extracted symbol infos */
val symbolInfos = new mutable.ListBuffer[SymbolInformation]()

val synthetics = new mutable.ListBuffer[s.Synthetic]()

/** A cache of localN names */
val localNames = new mutable.HashSet[String]()

Expand Down Expand Up @@ -155,11 +157,22 @@ class ExtractSemanticDB extends Phase:
tree match
case tree: DefDef =>
tree.paramss.foreach(_.foreach(param => registerSymbolSimple(param.symbol)))
case tree: ValDef if tree.symbol.is(Given) => traverse(tree.tpt)
case tree: ValDef if tree.symbol.is(Given) =>
synth.tryFindSynthetic(tree).foreach { synth =>
synthetics += synth
}
traverse(tree.tpt)
case _ =>
if !tree.symbol.isGlobal then
localBodies(tree.symbol) = tree.rhs
// ignore rhs

// `given Int` (syntax sugar of `given given_Int: Int`)
case tree: ValDef if isInventedGiven(tree) =>
synth.tryFindSynthetic(tree).foreach { synth =>
synthetics += synth
}
traverse(tree.tpt)
case PatternValDef(pat, rhs) =>
traverse(rhs)
PatternValDef.collectPats(pat).foreach(traverse)
Expand Down Expand Up @@ -192,6 +205,10 @@ class ExtractSemanticDB extends Phase:
case tree: Apply =>
@tu lazy val genParamSymbol: Name => String = tree.fun.symbol.funParamSymbol
traverse(tree.fun)
synth.tryFindSynthetic(tree).foreach { synth =>
synthetics += synth
}

for arg <- tree.args do
arg match
case tree @ NamedArg(name, arg) =>
Expand Down Expand Up @@ -231,6 +248,11 @@ class ExtractSemanticDB extends Phase:
registerUseGuarded(None, alt.symbol.companionClass, sel.imported.span, tree.source)
case tree: Inlined =>
traverse(tree.call)
case tree: TypeApply =>
synth.tryFindSynthetic(tree).foreach { synth =>
synthetics += synth
}
traverseChildren(tree)
case _ =>
traverseChildren(tree)

Expand Down Expand Up @@ -277,12 +299,6 @@ class ExtractSemanticDB extends Phase:
end PatternValDef


private def range(span: Span, treeSource: SourceFile)(using Context): Option[Range] =
def lineCol(offset: Int) = (treeSource.offsetToLine(offset), treeSource.column(offset))
val (startLine, startCol) = lineCol(span.start)
val (endLine, endCol) = lineCol(span.end)
Some(Range(startLine, startCol, endLine, endCol))


private def registerSymbol(sym: Symbol, symkinds: Set[SymbolKind])(using Context): Unit =
val sname = sym.symbolName
Expand Down Expand Up @@ -323,24 +339,6 @@ class ExtractSemanticDB extends Phase:
if !sym.is(Package) then
registerSymbol(sym, symkinds)

private def namePresentInSource(sym: Symbol, span: Span, source:SourceFile)(using Context): Boolean =
if !span.exists then false
else
val content = source.content()
val (start, end) =
if content.lift(span.end - 1).exists(_ == '`') then
(span.start + 1, span.end - 1)
else (span.start, span.end)
val nameInSource = content.slice(start, end).mkString
// for secondary constructors `this`
if sym.isConstructor && nameInSource == nme.THISkw.toString then
true
else
val target =
if sym.isPackageObject then sym.owner
else sym
nameInSource == target.name.stripModuleClassSuffix.lastPart.toString

private def spanOfSymbol(sym: Symbol, span: Span, treeSource: SourceFile)(using Context): Span =
val contents = if treeSource.exists then treeSource.content() else Array.empty[Char]
val idx = contents.indexOfSlice(sym.name.show, span.start)
Expand Down Expand Up @@ -448,7 +446,12 @@ object ExtractSemanticDB:

val name: String = "extractSemanticDB"

def write(source: SourceFile, occurrences: List[SymbolOccurrence], symbolInfos: List[SymbolInformation])(using Context): Unit =
def write(
source: SourceFile,
occurrences: List[SymbolOccurrence],
symbolInfos: List[SymbolInformation],
synthetics: List[Synthetic],
)(using Context): Unit =
def absolutePath(path: Path): Path = path.toAbsolutePath.normalize
val semanticdbTarget =
val semanticdbTargetSetting = ctx.settings.semanticdbTarget.value
Expand All @@ -470,7 +473,8 @@ object ExtractSemanticDB:
text = "",
md5 = internal.MD5.compute(String(source.content)),
symbols = symbolInfos,
occurrences = occurrences
occurrences = occurrences,
synthetics = synthetics,
)
val docs = TextDocuments(List(doc))
val out = Files.newOutputStream(outpath)
Expand Down
125 changes: 116 additions & 9 deletions compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import dotty.tools.dotc.{semanticdb => s}
import scala.collection.mutable
import dotty.tools.dotc.semanticdb.Scala3.{_, given}
import SymbolInformation.Kind._

class SymbolInfomationPrinter (symtab: PrinterSymtab):
import dotty.tools.dotc.util.SourceFile
class SymbolInformationPrinter (symtab: PrinterSymtab):
val notes = InfoNotes()
val infoPrinter = InfoPrinter(notes)

Expand All @@ -28,8 +28,9 @@ class SymbolInfomationPrinter (symtab: PrinterSymtab):
val displayName = if sym.isGlobal then sym.desc.value else sym
SymbolInformation(symbol = sym, displayName = displayName)
}
end InfoNotes

class InfoPrinter(notes: InfoNotes) {
class InfoPrinter(notes: InfoNotes):
private enum SymbolStyle:
case Reference, Definition
def pprint(info: SymbolInformation): String =
Expand Down Expand Up @@ -78,7 +79,7 @@ class SymbolInfomationPrinter (symtab: PrinterSymtab):
private def pprintDef(info: SymbolInformation) =
notes.enter(info)
pprint(info.symbol, SymbolStyle.Definition)
private def pprintRef(sym: String): String = pprint(sym, SymbolStyle.Reference)
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)
Expand Down Expand Up @@ -134,7 +135,7 @@ class SymbolInfomationPrinter (symtab: PrinterSymtab):
case _ =>
"<?>"

private def pprint(tpe: Type): String = {
protected def pprint(tpe: Type): String = {
def prefix(tpe: Type): String = tpe match
case TypeRef(pre, sym, args) =>
val preStr = pre match {
Expand Down Expand Up @@ -201,7 +202,7 @@ class SymbolInfomationPrinter (symtab: PrinterSymtab):
case tpe => s"@${pprint(tpe)}"
}

private def pprint(const: Constant): String = const match {
protected def pprint(const: Constant): String = const match {
case Constant.Empty =>
"<?>"
case UnitConstant() =>
Expand Down Expand Up @@ -242,7 +243,6 @@ class SymbolInfomationPrinter (symtab: PrinterSymtab):
s"private[${ssym}] "
case ProtectedWithinAccess(ssym) =>
s"protected[${ssym}] "

extension (scope: Scope)
private def infos: List[SymbolInformation] =
if (scope.symlinks.nonEmpty)
Expand All @@ -255,8 +255,8 @@ class SymbolInfomationPrinter (symtab: PrinterSymtab):
case Some(s) => s.infos
case None => Nil
}
}
end SymbolInfomationPrinter
end InfoPrinter
end SymbolInformationPrinter

extension (info: SymbolInformation)
def prefixBeforeTpe: String = {
Expand All @@ -277,3 +277,110 @@ object PrinterSymtab:
new PrinterSymtab {
override def info(symbol: String): Option[SymbolInformation] = map.get(symbol)
}

def processRange(sb: StringBuilder, range: Range): Unit =
sb.append('[')
.append(range.startLine).append(':').append(range.startCharacter)
.append("..")
.append(range.endLine).append(':').append(range.endCharacter)
.append("):")



class SyntheticPrinter(symtab: PrinterSymtab, source: SourceFile) extends SymbolInformationPrinter(symtab):

def pprint(synth: Synthetic): String =
val sb = new StringBuilder()
val notes = InfoNotes()
val treePrinter = TreePrinter(source, synth.range, notes)

synth.range match
case Some(range) =>
processRange(sb, range)
sb.append(source.substring(range))
case None =>
sb.append("[):")
sb.append(" => ")
sb.append(treePrinter.pprint(synth.tree))
sb.toString

extension (source: SourceFile)
private def substring(range: Option[s.Range]): String =
range match
case Some(range) => source.substring(range)
case None => ""
private def substring(range: s.Range): String =
/** get the line length of a given line */
def lineLength(line: Int): Int =
val isLastLine = source.lineToOffsetOpt(line).nonEmpty && source.lineToOffsetOpt(line + 1).isEmpty
if isLastLine then source.content.length - source.lineToOffset(line) - 1
else source.lineToOffset(line + 1) - source.lineToOffset(line) - 1 // -1 for newline char

val start = source.lineToOffset(range.startLine) +
math.min(range.startCharacter, lineLength(range.startLine))
val end = source.lineToOffset(range.endLine) +
math.min(range.endCharacter, lineLength(range.endLine))
new String(source.content, start, end - start)


// def pprint(tree: s.Tree, range: Option[Range]): String =
class TreePrinter(source: SourceFile, originalRange: Option[Range], notes: InfoNotes) extends InfoPrinter(notes):
def pprint(tree: Tree): String =
val sb = new StringBuilder()
processTree(tree)(using sb)
sb.toString


private def rep[T](xs: Seq[T], seq: String)(f: T => Unit)(using sb: StringBuilder): Unit =
xs.zipWithIndex.foreach { (x, i) =>
if i != 0 then sb.append(seq)
f(x)
}

private def processTree(tree: Tree)(using sb: StringBuilder): Unit =
tree match {
case tree: ApplyTree =>
processTree(tree.function)
sb.append("(")
rep(tree.arguments, ", ")(processTree)
sb.append(")")
case tree: FunctionTree =>
sb.append("{")
sb.append("(")
rep(tree.parameters, ", ")(processTree)
sb.append(") =>")
processTree(tree.body)
sb.append("}")
case tree: IdTree =>
sb.append(pprintRef(tree.symbol))
case tree: LiteralTree =>
sb.append(pprint(tree.constant))
case tree: MacroExpansionTree =>
sb.append("(`macro-expandee` : `")
sb.append(pprint(tree.tpe))
sb.append(")")
case tree: OriginalTree =>
if (tree.range == originalRange && originalRange.nonEmpty) then
sb.append("*")
else
sb.append("orig(")
sb.append(source.substring(tree.range))
sb.append(")")
case tree: SelectTree =>
processTree(tree.qualifier)
sb.append(".")
tree.id match
case Some(tree) => processTree(tree)
case None => ()
case tree: TypeApplyTree =>
processTree(tree.function)
sb.append("[")
rep(tree.typeArguments, ", ")((t) => sb.append(pprint(t)))
sb.append("]")

case _ =>
sb.append("<?>")
}


end SyntheticPrinter
Loading