1
1
//! Attributes & documentation for hir types.
2
2
3
+ use std:: ops:: ControlFlow ;
4
+
3
5
use base_db:: FileId ;
4
6
use hir_def:: {
5
7
attr:: AttrsWithOwner ,
@@ -13,13 +15,13 @@ use hir_expand::{
13
15
name:: Name ,
14
16
span_map:: { RealSpanMap , SpanMapRef } ,
15
17
} ;
16
- use hir_ty:: db:: HirDatabase ;
18
+ use hir_ty:: { db:: HirDatabase , method_resolution } ;
17
19
use syntax:: { ast, AstNode } ;
18
20
19
21
use crate :: {
20
22
Adt , AsAssocItem , AssocItem , BuiltinType , Const , ConstParam , DocLinkDef , Enum , ExternCrateDecl ,
21
- Field , Function , GenericParam , Impl , LifetimeParam , Macro , Module , ModuleDef , Static , Struct ,
22
- Trait , TraitAlias , TypeAlias , TypeParam , Union , Variant , VariantDef ,
23
+ Field , Function , GenericParam , HasCrate , Impl , LifetimeParam , Macro , Module , ModuleDef , Static ,
24
+ Struct , Trait , TraitAlias , Type , TypeAlias , TypeParam , Union , Variant , VariantDef ,
23
25
} ;
24
26
25
27
pub trait HasAttrs {
@@ -205,8 +207,14 @@ fn resolve_assoc_or_field(
205
207
}
206
208
} ;
207
209
208
- // FIXME: Resolve associated items here, e.g. `Option::map`. Note that associated items take
209
- // precedence over fields.
210
+ // Resolve inherent items first, then trait items, then fields.
211
+ if let Some ( assoc_item_def) = resolve_assoc_item ( db, & ty, & name, ns) {
212
+ return Some ( assoc_item_def) ;
213
+ }
214
+
215
+ if let Some ( impl_trait_item_def) = resolve_impl_trait_item ( db, resolver, & ty, & name, ns) {
216
+ return Some ( impl_trait_item_def) ;
217
+ }
210
218
211
219
let variant_def = match ty. as_adt ( ) ? {
212
220
Adt :: Struct ( it) => it. into ( ) ,
@@ -216,6 +224,69 @@ fn resolve_assoc_or_field(
216
224
resolve_field ( db, variant_def, name, ns)
217
225
}
218
226
227
+ fn resolve_assoc_item (
228
+ db : & dyn HirDatabase ,
229
+ ty : & Type ,
230
+ name : & Name ,
231
+ ns : Option < Namespace > ,
232
+ ) -> Option < DocLinkDef > {
233
+ ty. iterate_assoc_items ( db, ty. krate ( db) , move |assoc_item| {
234
+ if assoc_item. name ( db) ? != * name {
235
+ return None ;
236
+ }
237
+ as_module_def_if_namespace_matches ( assoc_item, ns)
238
+ } )
239
+ }
240
+
241
+ fn resolve_impl_trait_item (
242
+ db : & dyn HirDatabase ,
243
+ resolver : Resolver ,
244
+ ty : & Type ,
245
+ name : & Name ,
246
+ ns : Option < Namespace > ,
247
+ ) -> Option < DocLinkDef > {
248
+ let canonical = ty. canonical ( ) ;
249
+ let krate = ty. krate ( db) ;
250
+ let environment = resolver. generic_def ( ) . map_or_else (
251
+ || crate :: TraitEnvironment :: empty ( krate. id ) . into ( ) ,
252
+ |d| db. trait_environment ( d) ,
253
+ ) ;
254
+ let traits_in_scope = resolver. traits_in_scope ( db. upcast ( ) ) ;
255
+
256
+ let mut result = None ;
257
+
258
+ // `ty.iterate_path_candidates()` require a scope, which is not available when resolving
259
+ // attributes here. Use path resolution directly instead.
260
+ //
261
+ // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates)
262
+ method_resolution:: iterate_path_candidates (
263
+ & canonical,
264
+ db,
265
+ environment,
266
+ & traits_in_scope,
267
+ method_resolution:: VisibleFromModule :: None ,
268
+ Some ( name) ,
269
+ & mut |assoc_item_id| {
270
+ let assoc_item: AssocItem = assoc_item_id. into ( ) ;
271
+
272
+ debug_assert_eq ! ( assoc_item. name( db) . as_ref( ) , Some ( name) ) ;
273
+
274
+ // If two traits in scope define the same item, Rustdoc links to no specific trait (for
275
+ // instance, given two methods `a`, Rustdoc simply links to `method.a` with no
276
+ // disambiguation) so we just pick the first one we find as well.
277
+ result = as_module_def_if_namespace_matches ( assoc_item, ns) ;
278
+
279
+ if result. is_some ( ) {
280
+ ControlFlow :: Break ( ( ) )
281
+ } else {
282
+ ControlFlow :: Continue ( ( ) )
283
+ }
284
+ } ,
285
+ ) ;
286
+
287
+ result
288
+ }
289
+
219
290
fn resolve_field (
220
291
db : & dyn HirDatabase ,
221
292
def : VariantDef ,
@@ -228,6 +299,19 @@ fn resolve_field(
228
299
def. fields ( db) . into_iter ( ) . find ( |f| f. name ( db) == name) . map ( DocLinkDef :: Field )
229
300
}
230
301
302
+ fn as_module_def_if_namespace_matches (
303
+ assoc_item : AssocItem ,
304
+ ns : Option < Namespace > ,
305
+ ) -> Option < DocLinkDef > {
306
+ let ( def, expected_ns) = match assoc_item {
307
+ AssocItem :: Function ( it) => ( ModuleDef :: Function ( it) , Namespace :: Values ) ,
308
+ AssocItem :: Const ( it) => ( ModuleDef :: Const ( it) , Namespace :: Values ) ,
309
+ AssocItem :: TypeAlias ( it) => ( ModuleDef :: TypeAlias ( it) , Namespace :: Types ) ,
310
+ } ;
311
+
312
+ ( ns. unwrap_or ( expected_ns) == expected_ns) . then ( || DocLinkDef :: ModuleDef ( def) )
313
+ }
314
+
231
315
fn modpath_from_str ( db : & dyn HirDatabase , link : & str ) -> Option < ModPath > {
232
316
// FIXME: this is not how we should get a mod path here.
233
317
let try_get_modpath = |link : & str | {
0 commit comments