3
3
use hir_def:: {
4
4
attr:: { AttrsWithOwner , Documentation } ,
5
5
item_scope:: ItemInNs ,
6
- path:: ModPath ,
7
- resolver:: HasResolver ,
8
- AttrDefId , GenericParamId , ModuleDefId ,
6
+ path:: { ModPath , Path } ,
7
+ resolver:: { HasResolver , Resolver , TypeNs } ,
8
+ AssocItemId , AttrDefId , GenericParamId , ModuleDefId ,
9
9
} ;
10
- use hir_expand:: hygiene:: Hygiene ;
10
+ use hir_expand:: { hygiene:: Hygiene , name :: Name } ;
11
11
use hir_ty:: db:: HirDatabase ;
12
12
use syntax:: { ast, AstNode } ;
13
13
14
14
use crate :: {
15
- Adt , AssocItem , Const , ConstParam , Enum , ExternCrateDecl , Field , Function , GenericParam , Impl ,
16
- LifetimeParam , Macro , Module , ModuleDef , Static , Struct , Trait , TraitAlias , TypeAlias ,
17
- TypeParam , Union , Variant ,
15
+ Adt , AsAssocItem , AssocItem , BuiltinType , Const , ConstParam , Enum , ExternCrateDecl , Field ,
16
+ Function , GenericParam , Impl , LifetimeParam , Macro , Module , ModuleDef , Static , Struct , Trait ,
17
+ TraitAlias , TypeAlias , TypeParam , Union , Variant , VariantDef ,
18
18
} ;
19
19
20
20
pub trait HasAttrs {
@@ -25,7 +25,7 @@ pub trait HasAttrs {
25
25
db : & dyn HirDatabase ,
26
26
link : & str ,
27
27
ns : Option < Namespace > ,
28
- ) -> Option < ModuleDef > ;
28
+ ) -> Option < DocLinkDef > ;
29
29
}
30
30
31
31
#[ derive( PartialEq , Eq , Hash , Copy , Clone , Debug ) ]
@@ -35,6 +35,13 @@ pub enum Namespace {
35
35
Macros ,
36
36
}
37
37
38
+ /// Subset of `ide_db::Definition` that doc links can resolve to.
39
+ pub enum DocLinkDef {
40
+ ModuleDef ( ModuleDef ) ,
41
+ Field ( Field ) ,
42
+ SelfType ( Trait ) ,
43
+ }
44
+
38
45
macro_rules! impl_has_attrs {
39
46
( $( ( $def: ident, $def_id: ident) , ) * ) => { $(
40
47
impl HasAttrs for $def {
@@ -46,9 +53,14 @@ macro_rules! impl_has_attrs {
46
53
let def = AttrDefId :: $def_id( self . into( ) ) ;
47
54
db. attrs( def) . docs( )
48
55
}
49
- fn resolve_doc_path( self , db: & dyn HirDatabase , link: & str , ns: Option <Namespace >) -> Option <ModuleDef > {
56
+ fn resolve_doc_path(
57
+ self ,
58
+ db: & dyn HirDatabase ,
59
+ link: & str ,
60
+ ns: Option <Namespace >
61
+ ) -> Option <DocLinkDef > {
50
62
let def = AttrDefId :: $def_id( self . into( ) ) ;
51
- resolve_doc_path( db, def, link, ns) . map ( ModuleDef :: from )
63
+ resolve_doc_path( db, def, link, ns)
52
64
}
53
65
}
54
66
) * } ;
@@ -79,7 +91,12 @@ macro_rules! impl_has_attrs_enum {
79
91
fn docs( self , db: & dyn HirDatabase ) -> Option <Documentation > {
80
92
$enum:: $variant( self ) . docs( db)
81
93
}
82
- fn resolve_doc_path( self , db: & dyn HirDatabase , link: & str , ns: Option <Namespace >) -> Option <ModuleDef > {
94
+ fn resolve_doc_path(
95
+ self ,
96
+ db: & dyn HirDatabase ,
97
+ link: & str ,
98
+ ns: Option <Namespace >
99
+ ) -> Option <DocLinkDef > {
83
100
$enum:: $variant( self ) . resolve_doc_path( db, link, ns)
84
101
}
85
102
}
@@ -111,7 +128,7 @@ impl HasAttrs for AssocItem {
111
128
db : & dyn HirDatabase ,
112
129
link : & str ,
113
130
ns : Option < Namespace > ,
114
- ) -> Option < ModuleDef > {
131
+ ) -> Option < DocLinkDef > {
115
132
match self {
116
133
AssocItem :: Function ( it) => it. resolve_doc_path ( db, link, ns) ,
117
134
AssocItem :: Const ( it) => it. resolve_doc_path ( db, link, ns) ,
@@ -147,9 +164,9 @@ impl HasAttrs for ExternCrateDecl {
147
164
db : & dyn HirDatabase ,
148
165
link : & str ,
149
166
ns : Option < Namespace > ,
150
- ) -> Option < ModuleDef > {
167
+ ) -> Option < DocLinkDef > {
151
168
let def = AttrDefId :: ExternCrateId ( self . into ( ) ) ;
152
- resolve_doc_path ( db, def, link, ns) . map ( ModuleDef :: from )
169
+ resolve_doc_path ( db, def, link, ns)
153
170
}
154
171
}
155
172
@@ -159,7 +176,7 @@ fn resolve_doc_path(
159
176
def : AttrDefId ,
160
177
link : & str ,
161
178
ns : Option < Namespace > ,
162
- ) -> Option < ModuleDefId > {
179
+ ) -> Option < DocLinkDef > {
163
180
let resolver = match def {
164
181
AttrDefId :: ModuleId ( it) => it. resolver ( db. upcast ( ) ) ,
165
182
AttrDefId :: FieldId ( it) => it. parent . resolver ( db. upcast ( ) ) ,
@@ -184,32 +201,128 @@ fn resolve_doc_path(
184
201
. resolver ( db. upcast ( ) ) ,
185
202
} ;
186
203
187
- let modpath = {
188
- // FIXME: this is not how we should get a mod path here
204
+ let mut modpath = modpath_from_str ( db, link) ?;
205
+
206
+ let resolved = resolver. resolve_module_path_in_items ( db. upcast ( ) , & modpath) ;
207
+ if resolved. is_none ( ) {
208
+ let last_name = modpath. pop_segment ( ) ?;
209
+ resolve_assoc_or_field ( db, resolver, modpath, last_name, ns)
210
+ } else {
211
+ let def = match ns {
212
+ Some ( Namespace :: Types ) => resolved. take_types ( ) ,
213
+ Some ( Namespace :: Values ) => resolved. take_values ( ) ,
214
+ Some ( Namespace :: Macros ) => resolved. take_macros ( ) . map ( ModuleDefId :: MacroId ) ,
215
+ None => resolved. iter_items ( ) . next ( ) . map ( |it| match it {
216
+ ItemInNs :: Types ( it) => it,
217
+ ItemInNs :: Values ( it) => it,
218
+ ItemInNs :: Macros ( it) => ModuleDefId :: MacroId ( it) ,
219
+ } ) ,
220
+ } ;
221
+ Some ( DocLinkDef :: ModuleDef ( def?. into ( ) ) )
222
+ }
223
+ }
224
+
225
+ fn resolve_assoc_or_field (
226
+ db : & dyn HirDatabase ,
227
+ resolver : Resolver ,
228
+ path : ModPath ,
229
+ name : Name ,
230
+ ns : Option < Namespace > ,
231
+ ) -> Option < DocLinkDef > {
232
+ let path = Path :: from_known_path_with_no_generic ( path) ;
233
+ // FIXME: This does not handle `Self` on trait definitions, which we should resolve to the
234
+ // trait itself.
235
+ let base_def = resolver. resolve_path_in_type_ns_fully ( db. upcast ( ) , & path) ?;
236
+
237
+ let ty = match base_def {
238
+ TypeNs :: SelfType ( id) => Impl :: from ( id) . self_ty ( db) ,
239
+ TypeNs :: GenericParam ( _) => {
240
+ // Even if this generic parameter has some trait bounds, rustdoc doesn't
241
+ // resolve `name` to trait items.
242
+ return None ;
243
+ }
244
+ TypeNs :: AdtId ( id) | TypeNs :: AdtSelfType ( id) => Adt :: from ( id) . ty ( db) ,
245
+ TypeNs :: EnumVariantId ( id) => {
246
+ // Enum variants don't have path candidates.
247
+ let variant = Variant :: from ( id) ;
248
+ return resolve_field ( db, variant. into ( ) , name, ns) ;
249
+ }
250
+ TypeNs :: TypeAliasId ( id) => {
251
+ let alias = TypeAlias :: from ( id) ;
252
+ if alias. as_assoc_item ( db) . is_some ( ) {
253
+ // We don't normalize associated type aliases, so we have nothing to
254
+ // resolve `name` to.
255
+ return None ;
256
+ }
257
+ alias. ty ( db)
258
+ }
259
+ TypeNs :: BuiltinType ( id) => BuiltinType :: from ( id) . ty ( db) ,
260
+ TypeNs :: TraitId ( id) => {
261
+ // Doc paths in this context may only resolve to an item of this trait
262
+ // (i.e. no items of its supertraits), so we need to handle them here
263
+ // independently of others.
264
+ return db. trait_data ( id) . items . iter ( ) . find ( |it| it. 0 == name) . map ( |( _, assoc_id) | {
265
+ let def = match * assoc_id {
266
+ AssocItemId :: FunctionId ( it) => ModuleDef :: Function ( it. into ( ) ) ,
267
+ AssocItemId :: ConstId ( it) => ModuleDef :: Const ( it. into ( ) ) ,
268
+ AssocItemId :: TypeAliasId ( it) => ModuleDef :: TypeAlias ( it. into ( ) ) ,
269
+ } ;
270
+ DocLinkDef :: ModuleDef ( def)
271
+ } ) ;
272
+ }
273
+ TypeNs :: TraitAliasId ( _) => {
274
+ // XXX: Do these get resolved?
275
+ return None ;
276
+ }
277
+ } ;
278
+
279
+ // FIXME: Resolve associated items here, e.g. `Option::map`. Note that associated items take
280
+ // precedence over fields.
281
+
282
+ let variant_def = match ty. as_adt ( ) ? {
283
+ Adt :: Struct ( it) => it. into ( ) ,
284
+ Adt :: Union ( it) => it. into ( ) ,
285
+ Adt :: Enum ( _) => return None ,
286
+ } ;
287
+ resolve_field ( db, variant_def, name, ns)
288
+ }
289
+
290
+ fn resolve_field (
291
+ db : & dyn HirDatabase ,
292
+ def : VariantDef ,
293
+ name : Name ,
294
+ ns : Option < Namespace > ,
295
+ ) -> Option < DocLinkDef > {
296
+ if let Some ( Namespace :: Types | Namespace :: Macros ) = ns {
297
+ return None ;
298
+ }
299
+ def. fields ( db) . into_iter ( ) . find ( |f| f. name ( db) == name) . map ( DocLinkDef :: Field )
300
+ }
301
+
302
+ fn modpath_from_str ( db : & dyn HirDatabase , link : & str ) -> Option < ModPath > {
303
+ // FIXME: this is not how we should get a mod path here.
304
+ let try_get_modpath = |link : & str | {
189
305
let ast_path = ast:: SourceFile :: parse ( & format ! ( "type T = {link};" ) )
190
306
. syntax_node ( )
191
307
. descendants ( )
192
308
. find_map ( ast:: Path :: cast) ?;
193
309
if ast_path. syntax ( ) . text ( ) != link {
194
310
return None ;
195
311
}
196
- ModPath :: from_src ( db. upcast ( ) , ast_path, & Hygiene :: new_unhygienic ( ) ) ?
312
+ ModPath :: from_src ( db. upcast ( ) , ast_path, & Hygiene :: new_unhygienic ( ) )
197
313
} ;
198
314
199
- let resolved = resolver. resolve_module_path_in_items ( db. upcast ( ) , & modpath) ;
200
- let resolved = if resolved. is_none ( ) {
201
- resolver. resolve_module_path_in_trait_assoc_items ( db. upcast ( ) , & modpath) ?
202
- } else {
203
- resolved
204
- } ;
205
- match ns {
206
- Some ( Namespace :: Types ) => resolved. take_types ( ) ,
207
- Some ( Namespace :: Values ) => resolved. take_values ( ) ,
208
- Some ( Namespace :: Macros ) => resolved. take_macros ( ) . map ( ModuleDefId :: MacroId ) ,
209
- None => resolved. iter_items ( ) . next ( ) . map ( |it| match it {
210
- ItemInNs :: Types ( it) => it,
211
- ItemInNs :: Values ( it) => it,
212
- ItemInNs :: Macros ( it) => ModuleDefId :: MacroId ( it) ,
213
- } ) ,
315
+ let full = try_get_modpath ( link) ;
316
+ if full. is_some ( ) {
317
+ return full;
214
318
}
319
+
320
+ // Tuple field names cannot be a part of `ModPath` usually, but rustdoc can
321
+ // resolve doc paths like `TupleStruct::0`.
322
+ // FIXME: Find a better way to handle these.
323
+ let ( base, maybe_tuple_field) = link. rsplit_once ( "::" ) ?;
324
+ let tuple_field = Name :: new_tuple_field ( maybe_tuple_field. parse ( ) . ok ( ) ?) ;
325
+ let mut modpath = try_get_modpath ( base) ?;
326
+ modpath. push_segment ( tuple_field) ;
327
+ Some ( modpath)
215
328
}
0 commit comments