@@ -5,15 +5,22 @@ import scala.quoted._
5
5
6
6
trait MemberLookup {
7
7
8
+ def memberLookupResult (using Quotes )(
9
+ symbol : quotes.reflect.Symbol ,
10
+ label : String ,
11
+ inheritingParent : Option [quotes.reflect.Symbol ] = None
12
+ ): (quotes.reflect.Symbol , String , Option [quotes.reflect.Symbol ]) =
13
+ (symbol, label, inheritingParent)
14
+
8
15
def lookup (using Quotes , DocContext )(
9
16
query : Query ,
10
17
owner : quotes.reflect.Symbol ,
11
- ): Option [(quotes.reflect.Symbol , String )] = lookupOpt(query, Some (owner))
18
+ ): Option [(quotes.reflect.Symbol , String , Option [quotes.reflect. Symbol ] )] = lookupOpt(query, Some (owner))
12
19
13
20
def lookupOpt (using Quotes , DocContext )(
14
21
query : Query ,
15
22
ownerOpt : Option [quotes.reflect.Symbol ],
16
- ): Option [(quotes.reflect.Symbol , String )] =
23
+ ): Option [(quotes.reflect.Symbol , String , Option [quotes.reflect. Symbol ] )] =
17
24
try
18
25
import quotes .reflect ._
19
26
@@ -26,7 +33,7 @@ trait MemberLookup {
26
33
def nearestMembered (sym : Symbol ): Symbol =
27
34
if sym.isClassDef || sym.flags.is(Flags .Package ) then sym else nearestMembered(sym.owner)
28
35
29
- val res : Option [(Symbol , String )] = {
36
+ val res : Option [(Symbol , String , Option [ Symbol ] )] = {
30
37
def toplevelLookup (querystrings : List [String ]) =
31
38
downwardLookup(querystrings, defn.PredefModule .moduleClass)
32
39
.orElse(downwardLookup(querystrings, defn.ScalaPackage ))
@@ -38,7 +45,7 @@ trait MemberLookup {
38
45
val nearest = nearestMembered(owner)
39
46
val nearestCls = nearestClass(owner)
40
47
val nearestPkg = nearestPackage(owner)
41
- def relativeLookup (querystrings : List [String ], owner : Symbol ): Option [Symbol ] = {
48
+ def relativeLookup (querystrings : List [String ], owner : Symbol ): Option [( Symbol , Option [ Symbol ]) ] = {
42
49
val isMeaningful =
43
50
owner.exists
44
51
// those are just an optimisation, they can be dropped if problems show up
@@ -56,20 +63,20 @@ trait MemberLookup {
56
63
57
64
query match {
58
65
case Query .StrictMemberId (id) =>
59
- localLookup(id , nearest).nextOption. map(_ -> id )
66
+ downwardLookup( List (id) , nearest).map(memberLookupResult(_, id, _) )
60
67
case Query .QualifiedId (Query .Qual .This , _, rest) =>
61
- downwardLookup(rest.asList, nearestCls).map(_ -> rest.join)
68
+ downwardLookup(rest.asList, nearestCls).map(memberLookupResult(_, rest.join, _) )
62
69
case Query .QualifiedId (Query .Qual .Package , _, rest) =>
63
- downwardLookup(rest.asList, nearestPkg).map(_ -> rest.join)
70
+ downwardLookup(rest.asList, nearestPkg).map(memberLookupResult(_, rest.join, _) )
64
71
case query =>
65
72
val ql = query.asList
66
73
toplevelLookup(ql)
67
74
.orElse(relativeLookup(ql, nearest))
68
- .map(_ -> query.join)
75
+ .map(memberLookupResult(_, query.join, _) )
69
76
}
70
77
71
78
case None =>
72
- toplevelLookup(query.asList).map(_ -> query.join)
79
+ toplevelLookup(query.asList).map(memberLookupResult(_, query.join, _) )
73
80
}
74
81
}
75
82
@@ -88,7 +95,10 @@ trait MemberLookup {
88
95
import dotty .tools .dotc
89
96
given dotc .core.Contexts .Context = quotes.asInstanceOf [scala.quoted.runtime.impl.QuotesImpl ].ctx
90
97
val sym = rsym.asInstanceOf [dotc.core.Symbols .Symbol ]
91
- val members = sym.info.decls.iterator.filter(s => hackIsNotAbsent(s.asInstanceOf [Symbol ]))
98
+ val members =
99
+ sym.info.allMembers.iterator.map(_.symbol).filter(
100
+ s => hackIsNotAbsent(s.asInstanceOf [Symbol ])
101
+ )
92
102
// println(s"members of ${sym.show} : ${members.map(_.show).mkString(", ")}")
93
103
members.asInstanceOf [Iterator [Symbol ]]
94
104
}
@@ -101,43 +111,37 @@ trait MemberLookup {
101
111
sym.isCompleted && sym.info.exists
102
112
}
103
113
104
- private def localLookup (using Quotes )(query : String , owner : quotes.reflect.Symbol ): Iterator [quotes.reflect.Symbol ] = {
114
+ private def localLookup (using Quotes )(
115
+ sel : MemberLookup .Selector ,
116
+ owner : quotes.reflect.Symbol
117
+ ): Iterator [quotes.reflect.Symbol ] = {
105
118
import quotes .reflect ._
106
119
107
120
def findMatch (syms : Iterator [Symbol ]): Iterator [Symbol ] = {
108
- // Scaladoc overloading support allows terminal * (and they're meaningless)
109
- val cleanQuery = query.stripSuffix(" *" )
110
- val (q, forceTerm, forceType) =
111
- if cleanQuery endsWith " $" then
112
- (cleanQuery.init, true , false )
113
- else if cleanQuery endsWith " !" then
114
- (cleanQuery.init, false , true )
115
- else
116
- (cleanQuery, false , false )
117
-
118
121
def matches (s : Symbol ): Boolean =
119
- s.name == q && (
120
- if forceTerm then s.isTerm
121
- else if forceType then s.isType
122
- else true
123
- )
122
+ s.name == sel.ident && sel.kind. match {
123
+ case MemberLookup . SelectorKind . ForceTerm => s.isTerm
124
+ case MemberLookup . SelectorKind . ForceType => s.isType
125
+ case MemberLookup . SelectorKind . NoForce => true
126
+ }
124
127
125
128
def hackResolveModule (s : Symbol ): Symbol =
126
129
if s.flags.is(Flags .Module ) then s.moduleClass else s
127
130
128
131
// val syms0 = syms.toList
129
132
// val matched0 = syms0.filter(matches)
130
133
// if matched0.isEmpty then
131
- // println(s"Failed to look up $q in $owner; all members: {{{")
134
+ // println(s"Failed to look up ${sel.ident} in $owner; all members: {{{")
132
135
// syms0.foreach { s => println(s"\t$s") }
133
136
// println("}}}")
134
137
// val matched = matched0.iterator
135
138
136
139
// def showMatched() = matched0.foreach { s =>
137
140
// println(s"\t $s")
138
141
// }
139
- // println(s"localLookup in class ${owner} for `$q`{forceTerm=$forceTerm}: ")
142
+ // println(s"localLookup in class ${owner} for `${sel.ident}`{kind=${sel.kind}}:{{{ ")
140
143
// showMatched()
144
+ // println("}}}")
141
145
142
146
val matched = syms.filter(matches)
143
147
matched.map(hackResolveModule)
@@ -150,8 +154,6 @@ trait MemberLookup {
150
154
})
151
155
else
152
156
owner.tree match {
153
- case tree : ClassDef =>
154
- findMatch(tree.body.iterator.collect { case t : Definition if hackIsNotAbsent(t.symbol) => t.symbol })
155
157
case tree : TypeDef =>
156
158
val tpe =
157
159
tree.rhs match {
@@ -168,14 +170,37 @@ trait MemberLookup {
168
170
}
169
171
}
170
172
171
- private def downwardLookup (using Quotes )(query : List [String ], owner : quotes.reflect.Symbol ): Option [quotes.reflect.Symbol ] = {
173
+ private def downwardLookup (using Quotes )(
174
+ query : List [String ], owner : quotes.reflect.Symbol
175
+ ): Option [(quotes.reflect.Symbol , Option [quotes.reflect.Symbol ])] = {
172
176
import quotes .reflect ._
173
177
query match {
174
178
case Nil => None
175
- case q :: Nil => localLookup(q, owner).nextOption
179
+ case q :: Nil =>
180
+ val sel = MemberLookup .Selector .fromString(q)
181
+ val res = sel.kind match {
182
+ case MemberLookup .SelectorKind .NoForce =>
183
+ val lookedUp = localLookup(sel, owner).toSeq
184
+ // note: those flag lookups are necessary b/c for objects we return their classes
185
+ lookedUp.find(s => s.isType && ! s.flags.is(Flags .Module )).orElse(
186
+ lookedUp.find(s => s.isTerm || s.flags.is(Flags .Module ))
187
+ )
188
+ case _ =>
189
+ localLookup(sel, owner).nextOption
190
+ }
191
+ res match {
192
+ case None => None
193
+ case Some (sym) =>
194
+ val externalOwner : Option [quotes.reflect.Symbol ] =
195
+ if owner eq sym.owner then None
196
+ else if owner.flags.is(Flags .Module ) then Some (owner.moduleClass)
197
+ else if owner.isClassDef then Some (owner)
198
+ else None
199
+ Some (sym -> externalOwner)
200
+ }
176
201
case q :: qs =>
177
- val lookedUp =
178
- localLookup(q , owner).toSeq
202
+ val sel = MemberLookup . Selector .fromString(q)
203
+ val lookedUp = localLookup(sel , owner).toSeq
179
204
180
205
if lookedUp.isEmpty then None else {
181
206
// tm/tp - term/type symbols which we looked up and which allow further lookup
@@ -198,4 +223,25 @@ trait MemberLookup {
198
223
}
199
224
}
200
225
201
- object MemberLookup extends MemberLookup
226
+ object MemberLookup extends MemberLookup {
227
+ enum SelectorKind {
228
+ case ForceTerm
229
+ case ForceType
230
+ case NoForce
231
+ }
232
+
233
+ case class Selector (ident : String , kind : SelectorKind )
234
+ object Selector {
235
+ def fromString (str : String ) = {
236
+ // Scaladoc overloading support allows terminal * (and they're meaningless)
237
+ val cleanStr = str.stripSuffix(" *" )
238
+
239
+ if cleanStr endsWith " $" then
240
+ Selector (cleanStr.init, SelectorKind .ForceTerm )
241
+ else if cleanStr endsWith " !" then
242
+ Selector (cleanStr.init, SelectorKind .ForceType )
243
+ else
244
+ Selector (cleanStr, SelectorKind .NoForce )
245
+ }
246
+ }
247
+ }
0 commit comments