@@ -26,39 +26,52 @@ trait MemberLookup {
26
26
def nearestMembered (sym : Symbol ): Symbol =
27
27
if sym.isClassDef || sym.flags.is(Flags .Package ) then sym else nearestMembered(sym.owner)
28
28
29
- val res =
29
+ val res : Option [( Symbol , String )] = {
30
30
def toplevelLookup (querystrings : List [String ]) =
31
31
downwardLookup(querystrings, defn.PredefModule .moduleClass)
32
32
.orElse(downwardLookup(querystrings, defn.ScalaPackage ))
33
33
.orElse(downwardLookup(querystrings, defn.RootPackage ))
34
+ .orElse(downwardLookup(querystrings, defn.EmptyPackageClass ))
34
35
35
36
ownerOpt match {
36
37
case Some (owner) =>
37
38
val nearest = nearestMembered(owner)
38
39
val nearestCls = nearestClass(owner)
39
40
val nearestPkg = nearestPackage(owner)
40
- def relativeLookup (querystrings : List [String ]) =
41
- // TODO walk the owner chain?
42
- downwardLookup(querystrings, nearestPkg).orElse(toplevelLookup(querystrings))
41
+ def relativeLookup (querystrings : List [String ], owner : Symbol ): Option [Symbol ] = {
42
+ val isMeaningful =
43
+ owner.exists
44
+ // those are just an optimisation, they can be dropped if problems show up
45
+ && owner.ne(defn.ScalaPackage )
46
+ && owner.ne(defn.RootClass )
47
+ && owner.ne(defn.EmptyPackageClass )
48
+
49
+ if ! isMeaningful then None else {
50
+ downwardLookup(querystrings, owner) match {
51
+ case None => relativeLookup(querystrings, owner.owner)
52
+ case some => some
53
+ }
54
+ }
55
+ }
56
+
43
57
query match {
44
- case Query .StrictMemberId (id) => localLookup(id, nearest).map(_ -> id)
45
- case Query .Id (id) =>
46
- (localLookup(id, nearest) orElse relativeLookup(List (id))).map(_ -> id)
58
+ case Query .StrictMemberId (id) =>
59
+ localLookup(id, nearest).nextOption.map(_ -> id)
47
60
case Query .QualifiedId (Query .Qual .This , _, rest) =>
48
61
downwardLookup(rest.asList, nearestCls).map(_ -> rest.join)
49
62
case Query .QualifiedId (Query .Qual .Package , _, rest) =>
50
63
downwardLookup(rest.asList, nearestPkg).map(_ -> rest.join)
51
- case Query .QualifiedId (Query .Qual .Id (id), _, rest) if id == nearestCls.name =>
52
- downwardLookup(rest.asList, nearestCls).map(_ -> rest.join)
53
- case Query .QualifiedId (Query .Qual .Id (id), _, rest) if id == nearestPkg.name =>
54
- downwardLookup(rest.asList, nearestPkg).map(_ -> rest.join)
55
- case query : Query .QualifiedId =>
56
- relativeLookup(query.asList).map(_ -> query.join)
64
+ case query =>
65
+ val ql = query.asList
66
+ toplevelLookup(ql)
67
+ .orElse(relativeLookup(ql, nearest))
68
+ .map(_ -> query.join)
57
69
}
58
70
59
71
case None =>
60
72
toplevelLookup(query.asList).map(_ -> query.join)
61
73
}
74
+ }
62
75
63
76
// println(s"looked up `$query` in ${owner.show}[${owner.flags.show}] as ${res.map(_.show)}")
64
77
@@ -67,14 +80,15 @@ trait MemberLookup {
67
80
case e : Exception =>
68
81
// TODO (https://github.com/lampepfl/scala3doc/issues/238): proper reporting
69
82
println(s " [WARN] Unable to find a link for ${query} ${ownerOpt.fold(" " )(o => " in " + o.name)}" )
83
+ e.printStackTrace()
70
84
None
71
85
72
86
private def hackMembersOf (using Quotes )(rsym : quotes.reflect.Symbol ) = {
73
87
import quotes .reflect ._
74
88
import dotty .tools .dotc
75
89
given dotc .core.Contexts .Context = quotes.asInstanceOf [scala.quoted.runtime.impl.QuotesImpl ].ctx
76
90
val sym = rsym.asInstanceOf [dotc.core.Symbols .Symbol ]
77
- val members = sym.info.decls.iterator.filter(_.isCompleted )
91
+ val members = sym.info.decls.iterator.filter(s => hackIsNotAbsent(s. asInstanceOf [ Symbol ]) )
78
92
// println(s"members of ${sym.show} : ${members.map(_.show).mkString(", ")}")
79
93
members.asInstanceOf [Iterator [Symbol ]]
80
94
}
@@ -83,16 +97,14 @@ trait MemberLookup {
83
97
import dotty .tools .dotc
84
98
given dotc .core.Contexts .Context = quotes.asInstanceOf [scala.quoted.runtime.impl.QuotesImpl ].ctx
85
99
val sym = rsym.asInstanceOf [dotc.core.Symbols .Symbol ]
86
- sym.isCompleted
100
+ // note: Predef has .info = NoType for some reason
101
+ sym.isCompleted && sym.info.exists
87
102
}
88
103
89
- private def localLookup (using Quotes )(query : String , owner : quotes.reflect.Symbol ): Option [quotes.reflect.Symbol ] = {
104
+ private def localLookup (using Quotes )(query : String , owner : quotes.reflect.Symbol ): Iterator [quotes.reflect.Symbol ] = {
90
105
import quotes .reflect ._
91
106
92
- inline def whenExists (s : Symbol )(otherwise : => Option [Symbol ]): Option [Symbol ] =
93
- if s.exists then Some (s) else otherwise
94
-
95
- def findMatch (syms : Iterator [Symbol ]): Option [Symbol ] = {
107
+ def findMatch (syms : Iterator [Symbol ]): Iterator [Symbol ] = {
96
108
// Scaladoc overloading support allows terminal * (and they're meaningless)
97
109
val cleanQuery = query.stripSuffix(" *" )
98
110
val (q, forceTerm, forceType) =
@@ -114,23 +126,24 @@ trait MemberLookup {
114
126
if s.flags.is(Flags .Module ) then s.moduleClass else s
115
127
116
128
// val syms0 = syms.toList
117
- // val matched0 = syms0.find (matches)
129
+ // val matched0 = syms0.filter (matches)
118
130
// if matched0.isEmpty then
119
131
// println(s"Failed to look up $q in $owner; all members below:")
120
132
// syms0.foreach { s => println(s"\t$s") }
121
- // val matched = matched0
133
+ // val matched = matched0.iterator
122
134
123
135
// def showMatched() = matched.foreach { s =>
124
- // println(s">>> ${s.show} ")
136
+ // println(s">>> $s ")
125
137
// println(s">>> ${s.pos}")
126
138
// println(s">>> [${s.flags.show}]")
127
139
// println(s">>> {${if s.isTerm then "isterm" else ""};${if s.isType then "istype" else ""}}")
128
140
// println(s">>> moduleClass = ${if hackResolveModule(s) == s then hackResolveModule(s).show else "none"}")
129
141
// }
130
- // println(s"localLookup for class ${owner.show} of `$q`{forceTerm=$forceTerm}")
142
+ // println(s"localLookup in class ${owner} for `$q`{forceTerm=$forceTerm}")
143
+ // println(s"\t${matched0.mkString(", ")}")
131
144
// showMatched()
132
145
133
- val matched = syms.find (matches)
146
+ val matched = syms.filter (matches)
134
147
matched.map(hackResolveModule)
135
148
}
136
149
@@ -147,20 +160,43 @@ trait MemberLookup {
147
160
case tpt : TypeTree => tpt.tpe
148
161
}
149
162
150
- tpe.classSymbol.flatMap { s =>
151
- findMatch(hackMembersOf(s))
163
+ tpe.classSymbol match {
164
+ case Some (s) => findMatch(hackMembersOf(s))
165
+ case None => Iterator .empty
152
166
}
153
167
case _ =>
154
168
findMatch(hackMembersOf(owner))
155
169
}
156
170
}
157
171
158
- private def downwardLookup (using Quotes )(query : List [String ], owner : quotes.reflect.Symbol ): Option [quotes.reflect.Symbol ] =
172
+ private def downwardLookup (using Quotes )(query : List [String ], owner : quotes.reflect.Symbol ): Option [quotes.reflect.Symbol ] = {
173
+ import quotes .reflect ._
159
174
query match {
160
175
case Nil => None
161
- case q :: Nil => localLookup(q, owner)
162
- case q :: qs => localLookup(q, owner).flatMap(downwardLookup(qs, _))
176
+ case q :: Nil => localLookup(q, owner).nextOption
177
+ case q :: qs =>
178
+ val lookedUp =
179
+ localLookup(q, owner).toSeq
180
+
181
+ if lookedUp.isEmpty then None else {
182
+ // tm/tp - term/type symbols which we looked up and which allow further lookup
183
+ // pk - package symbol
184
+ // Note: packages collide with both term and type definitions
185
+ // Note: classes and types collide
186
+ var pk : Option [Symbol ] = None
187
+ var tp : Option [Symbol ] = None
188
+ var tm : Option [Symbol ] = None
189
+ lookedUp.foreach { s =>
190
+ if s.isPackageDef then pk = Some (s)
191
+ else if s.flags.is(Flags .Module ) then tm = Some (s)
192
+ else if s.isClassDef || s.isTypeDef then tp = Some (s)
193
+ }
194
+ pk.flatMap(downwardLookup(qs, _))
195
+ .orElse(tp.flatMap(downwardLookup(qs, _)))
196
+ .orElse(tm.flatMap(downwardLookup(qs, _)))
197
+ }
163
198
}
199
+ }
164
200
}
165
201
166
202
object MemberLookup extends MemberLookup
0 commit comments