-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathSemanticSymbolBuilder.scala
151 lines (133 loc) · 5.14 KB
/
SemanticSymbolBuilder.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package dotty.tools
package dotc
package semanticdb
import core.*
import Contexts.*
import Symbols.*
import Flags.*
import Names.Name
import scala.annotation.tailrec
import scala.collection.mutable
class SemanticSymbolBuilder:
import Scala3.{_, given}
private var nextLocalIdx: Int = 0
/** The index of a local symbol */
private val locals = mutable.HashMap[Symbol, Int]()
/** The local symbol(s) starting at given offset */
private val symsAtOffset = new mutable.HashMap[Int, Set[Symbol]]().withDefault(_ => Set[Symbol]())
def symbolName(sym: Symbol)(using Context): String =
val b = StringBuilder(20)
addSymName(b, sym)
b.toString
def symbolName(sym: FakeSymbol)(using Context): String =
sym match
case sym: WildcardTypeSymbol =>
val b = StringBuilder(20)
addSymName(b, sym.owner)
b.append('['); addName(b, sym.name); b.append(']')
b.toString
case sym: TermParamRefSymbol =>
val b = StringBuilder(20)
addSymName(b, sym.owner)
b.append('('); addName(b, sym.name); b.append(')')
b.toString
case sym: TypeParamRefSymbol =>
val b = StringBuilder(20)
addSymName(b, sym.owner)
b.append('['); addName(b, sym.name); b.append(']')
b.toString
case sym: RefinementSymbol =>
val b = StringBuilder(20)
addLocalSymName(b)
b.toString
def funParamSymbol(sym: Symbol)(using Context): Name => String =
if sym.isGlobal then
val funSymbol = symbolName(sym)
name => s"$funSymbol($name)"
else
name => locals.keys.find(local => local.isTerm && local.owner == sym && local.name == name)
.fold("<?>")(Symbols.LocalPrefix + locals(_))
private def addName(b: StringBuilder, name: Name): Unit =
val str = name.toString.unescapeUnicode
if str.isJavaIdent then b append str
else b append '`' append str append '`'
private def addLocalSymName(b: StringBuilder): Unit =
val idx = nextLocalIdx
nextLocalIdx += 1
b.append(Symbols.LocalPrefix).append(idx)
/** Add semanticdb name of the given symbol to string builder */
private def addSymName(b: StringBuilder, sym: Symbol)(using Context): Unit =
def addOwner(owner: Symbol): Unit =
if !owner.isRoot then addSymName(b, owner)
def addOverloadIdx(initSym: Symbol): Unit =
// revert from the compiler-generated overload of the signature polymorphic method
val sym = initSym.originalSignaturePolymorphic.symbol.orElse(initSym)
val decls =
val decls0 = sym.owner.info.decls.lookupAll(sym.name)
if sym.owner.isAllOf(JavaModule) then
decls0 ++ sym.owner.companionClass.info.decls.lookupAll(sym.name)
else
decls0
end decls
val alts = decls.filter(_.isOneOf(Method | Mutable)).toList.reverse.partition(!_.is(Synthetic)).toList.flatten
def find(filter: Symbol => Boolean) = alts match
case notSym :: rest if !filter(notSym) =>
val idx = rest.indexWhere(filter).ensuring(_ >= 0)
b.append('+').append(idx + 1)
case _ =>
end find
val sig = sym.signature
find(_.signature == sig)
def addDescriptor(sym: Symbol): Unit =
if sym.is(ModuleClass) then
addDescriptor(sym.sourceModule)
else if sym.is(TypeParam) then
b.append('['); addName(b, sym.name); b.append(']')
else if sym.is(Param) then
b.append('('); addName(b, sym.name); b.append(')')
else if sym.isRoot then
b.append(Symbols.RootPackage)
else if sym.isEmptyPackage then
b.append(Symbols.EmptyPackage)
else if (sym.isScala2PackageObject) then
b.append(Symbols.PackageObjectDescriptor)
else
addName(b, sym.name)
if sym.is(Package) then b.append('/')
else if sym.isType || sym.isAllOf(JavaModule) then b.append('#')
else if sym.isOneOf(Method | Mutable)
&& (!sym.is(StableRealizable) || sym.isConstructor) then
b.append('('); addOverloadIdx(sym); b.append(").")
else b.append('.')
/** The index of local symbol `sym`. Symbols with the same name and
* the same starting position have the same index.
*/
def localIdx(sym: Symbol)(using Context): Int =
val startPos =
// assert(sym.span.exists, s"$sym should have a span")
if (sym.span.exists) Some(sym.span.start) else None
@tailrec
def computeLocalIdx(sym: Symbol): Int = locals get sym match
case Some(idx) => idx
case None =>
(for {
pos <- startPos
syms <- symsAtOffset.get(pos)
found <- syms.find(_.name == sym.name)
} yield found) match
case Some(other) => computeLocalIdx(other)
case None =>
val idx = nextLocalIdx
nextLocalIdx += 1
locals(sym) = idx
startPos.foreach(pos => symsAtOffset(pos) += sym)
idx
end computeLocalIdx
computeLocalIdx(sym)
end localIdx
if sym.exists then
if sym.isGlobal then
addOwner(sym.owner); addDescriptor(sym)
else
b.append(Symbols.LocalPrefix).append(localIdx(sym))
end addSymName