Skip to content

Commit 37d8d6c

Browse files
committed
Minimum Singature information for Semanticdb
1 parent 076ab0a commit 37d8d6c

File tree

6 files changed

+520
-222
lines changed

6 files changed

+520
-222
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package dotty.tools
2+
package dotc
3+
package semanticdb
4+
5+
import dotty.tools.dotc.{semanticdb => s}
6+
7+
import core.Contexts.Context
8+
import core.Constants._
9+
10+
object ConstantOps:
11+
extension (const: Constant)
12+
def toSemanticConst(using Context): s.Constant = const.tag match {
13+
case UnitTag => s.UnitConstant()
14+
case BooleanTag => s.BooleanConstant(const.booleanValue)
15+
case ByteTag => s.ByteConstant(const.byteValue)
16+
case ShortTag => s.ShortConstant(const.shortValue)
17+
case CharTag => s.CharConstant(const.charValue)
18+
case IntTag => s.IntConstant(const.intValue)
19+
case LongTag => s.LongConstant(const.longValue)
20+
case FloatTag => s.FloatConstant(const.floatValue)
21+
case DoubleTag => s.DoubleConstant(const.doubleValue)
22+
case StringTag => s.StringConstant(const.stringValue)
23+
case NullTag => s.NullConstant()
24+
// ConstantType(_: Type, ClazzTag) should be converted as it's type
25+
// NoTag => it shouldn't happen
26+
case _ => throw new Error(s"Constant ${const} can't be converted to Semanticdb Constant.")
27+
}

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

Lines changed: 15 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import scala.collection.mutable
2222
import scala.annotation.{ threadUnsafe => tu, tailrec }
2323
import scala.PartialFunction.condOpt
2424

25+
import SemanticSymbolBuilder._
26+
import SymbolInformationOps._
2527

2628
/** Extract symbol references and uses to semanticdb files.
2729
* See https://scalameta.org/docs/semanticdb/specification.html#symbol-1
@@ -49,18 +51,9 @@ class ExtractSemanticDB extends Phase:
4951
/** Extractor of symbol occurrences from trees */
5052
class Extractor extends TreeTraverser:
5153

52-
private var nextLocalIdx: Int = 0
53-
54-
/** The index of a local symbol */
55-
private val locals = mutable.HashMap[Symbol, Int]()
56-
5754
/** The bodies of synthetic locals */
5855
private val localBodies = mutable.HashMap[Symbol, Tree]()
5956

60-
/** The local symbol(s) starting at given offset */
61-
private val symsAtOffset = new mutable.HashMap[Int, Set[Symbol]]():
62-
override def default(key: Int) = Set[Symbol]()
63-
6457
/** The extracted symbol occurrences */
6558
val occurrences = new mutable.ListBuffer[SymbolOccurrence]()
6659

@@ -142,12 +135,12 @@ class ExtractSemanticDB extends Phase:
142135
if !tree.symbol.isAllOf(ModuleValCreationFlags) then
143136
if !excludeDef(tree.symbol)
144137
&& tree.span.hasLength then
145-
registerDefinition(tree.symbol, tree.nameSpan, symbolKinds(tree), tree.source)
138+
registerDefinition(tree.symbol, tree.nameSpan, tree.symbolKinds, tree.source)
146139
val privateWithin = tree.symbol.privateWithin
147140
if privateWithin.exists then
148141
registerUseGuarded(None, privateWithin, spanOfSymbol(privateWithin, tree.span, tree.source), tree.source)
149142
else if !excludeSymbol(tree.symbol) then
150-
registerSymbol(tree.symbol, symbolName(tree.symbol), symbolKinds(tree))
143+
registerSymbol(tree.symbol, tree.symbolKinds)
151144
tree match
152145
case tree: ValDef
153146
if tree.symbol.isAllOf(EnumValue) =>
@@ -248,14 +241,6 @@ class ExtractSemanticDB extends Phase:
248241

249242
end traverse
250243

251-
private def funParamSymbol(funSym: Symbol)(using Context): Name => String =
252-
if funSym.isGlobal then
253-
val funSymbol = symbolName(funSym)
254-
name => s"$funSymbol($name)"
255-
else
256-
name => locals.keys.find(local => local.isTerm && local.owner == funSym && local.name == name)
257-
.fold("<?>")(Symbols.LocalPrefix + _)
258-
259244
private object PatternValDef:
260245

261246
def unapply(tree: ValDef)(using Context): Option[(Tree, Tree)] = tree.rhs match
@@ -289,185 +274,24 @@ class ExtractSemanticDB extends Phase:
289274

290275
end PatternValDef
291276

292-
/** Add semanticdb name of the given symbol to string builder */
293-
private def addSymName(b: StringBuilder, sym: Symbol)(using Context): Unit =
294-
295-
def addName(name: Name) =
296-
val str = name.toString.unescapeUnicode
297-
if str.isJavaIdent then b append str
298-
else b append '`' append str append '`'
299-
300-
def addOwner(owner: Symbol): Unit =
301-
if !owner.isRoot then addSymName(b, owner)
302-
303-
def addOverloadIdx(sym: Symbol): Unit =
304-
val decls =
305-
val decls0 = sym.owner.info.decls.lookupAll(sym.name)
306-
if sym.owner.isAllOf(JavaModule) then
307-
decls0 ++ sym.owner.companionClass.info.decls.lookupAll(sym.name)
308-
else
309-
decls0
310-
end decls
311-
val alts = decls.filter(_.isOneOf(Method | Mutable)).toList.reverse
312-
def find(filter: Symbol => Boolean) = alts match
313-
case notSym :: rest if !filter(notSym) =>
314-
val idx = rest.indexWhere(filter).ensuring(_ >= 0)
315-
b.append('+').append(idx + 1)
316-
case _ =>
317-
end find
318-
val sig = sym.signature
319-
find(_.signature == sig)
320-
321-
def addDescriptor(sym: Symbol): Unit =
322-
if sym.is(ModuleClass) then
323-
addDescriptor(sym.sourceModule)
324-
else if sym.is(TypeParam) then
325-
b.append('['); addName(sym.name); b.append(']')
326-
else if sym.is(Param) then
327-
b.append('('); addName(sym.name); b.append(')')
328-
else if sym.isRoot then
329-
b.append(Symbols.RootPackage)
330-
else if sym.isEmptyPackage then
331-
b.append(Symbols.EmptyPackage)
332-
else if (sym.isScala2PackageObject) then
333-
b.append(Symbols.PackageObjectDescriptor)
334-
else
335-
addName(sym.name)
336-
if sym.is(Package) then b.append('/')
337-
else if sym.isType || sym.isAllOf(JavaModule) then b.append('#')
338-
else if sym.isOneOf(Method | Mutable)
339-
&& (!sym.is(StableRealizable) || sym.isConstructor) then
340-
b.append('('); addOverloadIdx(sym); b.append(").")
341-
else b.append('.')
342-
343-
/** The index of local symbol `sym`. Symbols with the same name and
344-
* the same starting position have the same index.
345-
*/
346-
def localIdx(sym: Symbol)(using Context): Int =
347-
val startPos =
348-
assert(sym.span.exists, s"$sym should have a span")
349-
sym.span.start
350-
@tailrec
351-
def computeLocalIdx(sym: Symbol): Int = locals get sym match
352-
case Some(idx) => idx
353-
case None => symsAtOffset(startPos).find(_.name == sym.name) match
354-
case Some(other) => computeLocalIdx(other)
355-
case None =>
356-
val idx = nextLocalIdx
357-
nextLocalIdx += 1
358-
locals(sym) = idx
359-
symsAtOffset(startPos) += sym
360-
idx
361-
end computeLocalIdx
362-
computeLocalIdx(sym)
363-
end localIdx
364-
365-
if sym.exists then
366-
if sym.isGlobal then
367-
addOwner(sym.owner); addDescriptor(sym)
368-
else
369-
b.append(Symbols.LocalPrefix).append(localIdx(sym))
370-
371-
end addSymName
372-
373-
/** The semanticdb name of the given symbol */
374-
private def symbolName(sym: Symbol)(using Context): String =
375-
val b = StringBuilder(20)
376-
addSymName(b, sym)
377-
b.toString
378277

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

385-
private def symbolKind(sym: Symbol, symkinds: Set[SymbolKind])(using Context): SymbolInformation.Kind =
386-
if sym.isTypeParam then
387-
SymbolInformation.Kind.TYPE_PARAMETER
388-
else if sym.is(TermParam) then
389-
SymbolInformation.Kind.PARAMETER
390-
else if sym.isTerm && sym.owner.isTerm then
391-
SymbolInformation.Kind.LOCAL
392-
else if sym.isInlineMethod || sym.is(Macro) then
393-
SymbolInformation.Kind.MACRO
394-
else if sym.isConstructor then
395-
SymbolInformation.Kind.CONSTRUCTOR
396-
else if sym.isSelfSym then
397-
SymbolInformation.Kind.SELF_PARAMETER
398-
else if sym.isOneOf(Method) || symkinds.exists(_.isVarOrVal) then
399-
SymbolInformation.Kind.METHOD
400-
else if sym.isPackageObject then
401-
SymbolInformation.Kind.PACKAGE_OBJECT
402-
else if sym.is(Module) then
403-
SymbolInformation.Kind.OBJECT
404-
else if sym.is(Package) then
405-
SymbolInformation.Kind.PACKAGE
406-
else if sym.isAllOf(JavaInterface) then
407-
SymbolInformation.Kind.INTERFACE
408-
else if sym.is(Trait) then
409-
SymbolInformation.Kind.TRAIT
410-
else if sym.isClass then
411-
SymbolInformation.Kind.CLASS
412-
else if sym.isType then
413-
SymbolInformation.Kind.TYPE
414-
else if sym.is(ParamAccessor) then
415-
SymbolInformation.Kind.FIELD
416-
else
417-
SymbolInformation.Kind.UNKNOWN_KIND
418-
419-
private def symbolProps(sym: Symbol, symkinds: Set[SymbolKind])(using Context): Int =
420-
if sym.is(ModuleClass) then
421-
return symbolProps(sym.sourceModule, symkinds)
422-
var props = 0
423-
if sym.isPrimaryConstructor then
424-
props |= SymbolInformation.Property.PRIMARY.value
425-
if sym.is(Abstract) || symkinds.contains(SymbolKind.Abstract) then
426-
props |= SymbolInformation.Property.ABSTRACT.value
427-
if sym.is(Final) then
428-
props |= SymbolInformation.Property.FINAL.value
429-
if sym.is(Sealed) then
430-
props |= SymbolInformation.Property.SEALED.value
431-
if sym.isOneOf(GivenOrImplicit) then
432-
props |= SymbolInformation.Property.IMPLICIT.value
433-
if sym.is(Lazy, butNot=Module) then
434-
props |= SymbolInformation.Property.LAZY.value
435-
if sym.isAllOf(Case | Module) || sym.is(CaseClass) || sym.isAllOf(EnumCase) then
436-
props |= SymbolInformation.Property.CASE.value
437-
if sym.is(Covariant) then
438-
props |= SymbolInformation.Property.COVARIANT.value
439-
if sym.is(Contravariant) then
440-
props |= SymbolInformation.Property.CONTRAVARIANT.value
441-
if sym.isAllOf(DefaultMethod | JavaDefined) || sym.is(Accessor) && sym.name.is(NameKinds.DefaultGetterName) then
442-
props |= SymbolInformation.Property.DEFAULT.value
443-
if symkinds.exists(_.isVal) then
444-
props |= SymbolInformation.Property.VAL.value
445-
if symkinds.exists(_.isVar) then
446-
props |= SymbolInformation.Property.VAR.value
447-
if sym.is(JavaStatic) then
448-
props |= SymbolInformation.Property.STATIC.value
449-
if sym.is(Enum) then
450-
props |= SymbolInformation.Property.ENUM.value
451-
props
452-
453-
private def symbolInfo(sym: Symbol, symbolName: String, symkinds: Set[SymbolKind])(using Context): SymbolInformation =
454-
SymbolInformation(
455-
symbol = symbolName,
456-
language = Language.SCALA,
457-
kind = symbolKind(sym, symkinds),
458-
properties = symbolProps(sym, symkinds),
459-
displayName = Symbols.displaySymbol(sym)
460-
)
461284

462-
private def registerSymbol(sym: Symbol, symbolName: String, symkinds: Set[SymbolKind])(using Context): Unit =
463-
val isLocal = symbolName.isLocal
464-
if !isLocal || !localNames.contains(symbolName) then
285+
private def registerSymbol(sym: Symbol, symkinds: Set[SymbolKind])(using Context): Unit =
286+
val sname = symbolName(sym)
287+
val isLocal = sname.isLocal
288+
if !isLocal || !localNames.contains(sname) then
465289
if isLocal then
466-
localNames += symbolName
467-
symbolInfos += symbolInfo(sym, symbolName, symkinds)
290+
localNames += sname
291+
symbolInfos += sym.symbolInfo(symkinds)(using LinkMode.SymlinkChildren)
468292

469293
private def registerSymbolSimple(sym: Symbol)(using Context): Unit =
470-
registerSymbol(sym, symbolName(sym), Set.empty)
294+
registerSymbol(sym, Set.empty)
471295

472296
private def registerOccurrence(symbol: String, span: Span, role: SymbolOccurrence.Role, treeSource: SourceFile)(using Context): Unit =
473297
val occ = SymbolOccurrence(range(span, treeSource), symbol, role)
@@ -486,15 +310,15 @@ class ExtractSemanticDB extends Phase:
486310
registerOccurrence(symbol, span, SymbolOccurrence.Role.REFERENCE, treeSource)
487311

488312
private def registerDefinition(sym: Symbol, span: Span, symkinds: Set[SymbolKind], treeSource: SourceFile)(using Context) =
489-
val symbol = symbolName(sym)
313+
val sname = symbolName(sym)
490314
val finalSpan = if !span.hasLength || !sym.is(Given) || namePresentInSource(sym, span, treeSource) then
491315
span
492316
else
493317
Span(span.start)
494318

495-
registerOccurrence(symbol, finalSpan, SymbolOccurrence.Role.DEFINITION, treeSource)
319+
registerOccurrence(sname, finalSpan, SymbolOccurrence.Role.DEFINITION, treeSource)
496320
if !sym.is(Package) then
497-
registerSymbol(sym, symbol, symkinds)
321+
registerSymbol(sym, symkinds)
498322

499323
private def namePresentInSource(sym: Symbol, span: Span, source:SourceFile)(using Context): Boolean =
500324
val content = source.content()
@@ -507,15 +331,6 @@ class ExtractSemanticDB extends Phase:
507331
val start = if idx >= 0 then idx else span.start
508332
Span(start, start + sym.name.show.length, start)
509333

510-
extension (list: List[List[ValDef]])
511-
private inline def isSingleArg = list match
512-
case (_::Nil)::Nil => true
513-
case _ => false
514-
515-
extension (tree: DefDef)
516-
private def isSetterDef(using Context): Boolean =
517-
tree.name.isSetterName && tree.mods.is(Accessor) && tree.termParamss.isSingleArg
518-
519334
private def findGetters(ctorParams: Set[Names.TermName], body: List[Tree])(using Context): Map[Names.TermName, ValDef] =
520335
if ctorParams.isEmpty || body.isEmpty then
521336
Map.empty
@@ -562,28 +377,6 @@ class ExtractSemanticDB extends Phase:
562377
private inline def matchingMemberType(ctorTypeParam: Symbol, classSym: Symbol)(using Context) =
563378
classSym.info.member(ctorTypeParam.name).symbol
564379

565-
/**Necessary because not all of the eventual flags are propagated from the Tree to the symbol yet.
566-
*/
567-
private def symbolKinds(tree: NamedDefTree)(using Context): Set[SymbolKind] =
568-
if tree.symbol.isSelfSym then
569-
Set.empty
570-
else
571-
val symkinds = mutable.HashSet.empty[SymbolKind]
572-
tree match
573-
case tree: ValDef =>
574-
if !tree.symbol.is(Param) then
575-
symkinds += (if tree.mods is Mutable then SymbolKind.Var else SymbolKind.Val)
576-
if tree.rhs.isEmpty && !tree.symbol.isOneOf(TermParam | CaseAccessor | ParamAccessor) then
577-
symkinds += SymbolKind.Abstract
578-
case tree: DefDef =>
579-
if tree.isSetterDef then
580-
symkinds += SymbolKind.Setter
581-
else if tree.rhs.isEmpty then
582-
symkinds += SymbolKind.Abstract
583-
case tree: Bind =>
584-
symkinds += SymbolKind.Val
585-
case _ =>
586-
symkinds.toSet
587380

588381
private def ctorParams(
589382
vparamss: List[List[ValDef]], body: List[Tree])(using Context): Unit =
@@ -597,7 +390,7 @@ class ExtractSemanticDB extends Phase:
597390
val symkinds =
598391
getters.get(vparam.name).fold(SymbolKind.emptySet)(getter =>
599392
if getter.mods.is(Mutable) then SymbolKind.VarSet else SymbolKind.ValSet)
600-
registerSymbol(vparam.symbol, symbolName(vparam.symbol), symkinds)
393+
registerSymbol(vparam.symbol, symkinds)
601394
traverse(vparam.tpt)
602395

603396
object ExtractSemanticDB:
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package dotty.tools.dotc.semanticdb
2+
3+
enum LinkMode:
4+
case SymlinkChildren, HardlinkChildren

0 commit comments

Comments
 (0)