@@ -60,6 +60,33 @@ package final class CheckedIndex {
60
60
private var checker : IndexOutOfDateChecker
61
61
private let index : IndexStoreDB
62
62
63
+ /// Maps the USR of a symbol’s container the name of that container as well as the name of all containers the
64
+ /// container might itself be contained in.
65
+ ///
66
+ /// It is important that we cache this because we might find a lot of symbols in the same container for eg. workspace
67
+ /// symbols (eg. consider many symbols in the same C++ namespace). If we didn't cache this value, then we would need
68
+ /// to perform a `primaryDefinitionOrDeclarationOccurrence` lookup for all of these containers, which is expensive.
69
+ ///
70
+ /// Since we don't expect `CheckedIndex` to be outlive a single request it is acceptable to cache these results
71
+ /// without having any invalidation logic (similar to how we don't invalide results cached in
72
+ /// `IndexOutOfDateChecker`).
73
+ ///
74
+ /// ### Examples
75
+ /// If we have
76
+ /// ```swift
77
+ /// struct Foo {}
78
+ /// ``` then
79
+ /// `containerNamesCache[<usr of Foo>]` will be `["Foo"]`.
80
+ ///
81
+ /// If we have
82
+ /// ```swift
83
+ /// struct Bar {
84
+ /// struct Foo {}
85
+ /// }
86
+ /// ```, then
87
+ /// `containerNamesCache[<usr of Foo>]` will be `["Bar", "Foo"]`.
88
+ private var containerNamesCache : [ String : [ String ] ] = [ : ]
89
+
63
90
fileprivate init ( index: IndexStoreDB , checkLevel: IndexCheckLevel ) {
64
91
self . index = index
65
92
self . checker = IndexOutOfDateChecker ( checkLevel: checkLevel)
@@ -183,6 +210,68 @@ package final class CheckedIndex {
183
210
}
184
211
return result
185
212
}
213
+
214
+ /// The names of all containers the symbol is contained in, from outermost to innermost.
215
+ ///
216
+ /// ### Examples
217
+ /// In the following, the container names of `test` are `["Foo"]`.
218
+ /// ```swift
219
+ /// struct Foo {
220
+ /// func test() {}
221
+ /// }
222
+ /// ```
223
+ ///
224
+ /// In the following, the container names of `test` are `["Bar", "Foo"]`.
225
+ /// ```swift
226
+ /// struct Bar {
227
+ /// struct Foo {
228
+ /// func test() {}
229
+ /// }
230
+ /// }
231
+ /// ```
232
+ package func containerNames( of symbol: SymbolOccurrence ) -> [ String ] {
233
+ // The container name of accessors is the container of the surrounding variable.
234
+ let accessorOf = symbol. relations. filter { $0. roles. contains ( . accessorOf) }
235
+ if let primaryVariable = accessorOf. sorted ( ) . first {
236
+ if accessorOf. count > 1 {
237
+ logger. fault ( " Expected an occurrence to an accessor of at most one symbol, not multiple " )
238
+ }
239
+ if let primaryVariable = primaryDefinitionOrDeclarationOccurrence ( ofUSR: primaryVariable. symbol. usr) {
240
+ return containerNames ( of: primaryVariable)
241
+ }
242
+ }
243
+
244
+ let containers = symbol. relations. filter { $0. roles. contains ( . childOf) }
245
+ if containers. count > 1 {
246
+ logger. fault ( " Expected an occurrence to a child of at most one symbol, not multiple " )
247
+ }
248
+ let container = containers. filter {
249
+ switch $0. symbol. kind {
250
+ case . module, . namespace, . enum, . struct, . class, . protocol, . extension, . union:
251
+ return true
252
+ case . unknown, . namespaceAlias, . macro, . typealias, . function, . variable, . field, . enumConstant,
253
+ . instanceMethod, . classMethod, . staticMethod, . instanceProperty, . classProperty, . staticProperty, . constructor,
254
+ . destructor, . conversionFunction, . parameter, . using, . concept, . commentTag:
255
+ return false
256
+ }
257
+ } . sorted ( ) . first
258
+
259
+ if let container {
260
+ if let cached = containerNamesCache [ container. symbol. usr] {
261
+ return cached
262
+ }
263
+ let result : [ String ]
264
+ if let containerDefinition = primaryDefinitionOrDeclarationOccurrence ( ofUSR: container. symbol. usr) {
265
+ result = self . containerNames ( of: containerDefinition) + [ container. symbol. name]
266
+ } else {
267
+ result = [ container. symbol. name]
268
+ }
269
+ containerNamesCache [ container. symbol. usr] = result
270
+ return result
271
+ } else {
272
+ return [ ]
273
+ }
274
+ }
186
275
}
187
276
188
277
/// A wrapper around `IndexStoreDB` that allows the retrieval of a `CheckedIndex` with a specified check level or the
0 commit comments