11//! Attributes & documentation for hir types.
22
3+ use std:: ops:: ControlFlow ;
4+
35use base_db:: FileId ;
46use hir_def:: {
57 attr:: AttrsWithOwner ,
@@ -13,13 +15,13 @@ use hir_expand::{
1315 name:: Name ,
1416 span_map:: { RealSpanMap , SpanMapRef } ,
1517} ;
16- use hir_ty:: db:: HirDatabase ;
18+ use hir_ty:: { db:: HirDatabase , method_resolution } ;
1719use syntax:: { ast, AstNode } ;
1820
1921use crate :: {
2022 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 ,
2325} ;
2426
2527pub trait HasAttrs {
@@ -205,8 +207,14 @@ fn resolve_assoc_or_field(
205207 }
206208 } ;
207209
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+ }
210218
211219 let variant_def = match ty. as_adt ( ) ? {
212220 Adt :: Struct ( it) => it. into ( ) ,
@@ -216,6 +224,69 @@ fn resolve_assoc_or_field(
216224 resolve_field ( db, variant_def, name, ns)
217225}
218226
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+
219290fn resolve_field (
220291 db : & dyn HirDatabase ,
221292 def : VariantDef ,
@@ -228,6 +299,19 @@ fn resolve_field(
228299 def. fields ( db) . into_iter ( ) . find ( |f| f. name ( db) == name) . map ( DocLinkDef :: Field )
229300}
230301
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+
231315fn modpath_from_str ( db : & dyn HirDatabase , link : & str ) -> Option < ModPath > {
232316 // FIXME: this is not how we should get a mod path here.
233317 let try_get_modpath = |link : & str | {
0 commit comments