1
+ //! This module implements [RFC 1946]: Intra-rustdoc-links
2
+ //!
3
+ //! [RFC 1946]: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md
4
+
1
5
use rustc_ast as ast;
2
6
use rustc_data_structures:: stable_set:: FxHashSet ;
3
7
use rustc_errors:: { Applicability , DiagnosticBuilder } ;
@@ -27,7 +31,7 @@ use std::cell::Cell;
27
31
use std:: mem;
28
32
use std:: ops:: Range ;
29
33
30
- use crate :: clean:: * ;
34
+ use crate :: clean:: { self , Crate , GetDefId , Import , Item , ItemLink , PrimitiveType } ;
31
35
use crate :: core:: DocContext ;
32
36
use crate :: fold:: DocFolder ;
33
37
use crate :: html:: markdown:: markdown_links;
@@ -42,10 +46,10 @@ pub const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
42
46
} ;
43
47
44
48
pub fn collect_intra_doc_links ( krate : Crate , cx : & DocContext < ' _ > ) -> Crate {
45
- let mut coll = LinkCollector :: new ( cx) ;
46
- coll. fold_crate ( krate)
49
+ LinkCollector :: new ( cx) . fold_crate ( krate)
47
50
}
48
51
52
+ /// Top-level errors emitted by this pass.
49
53
enum ErrorKind < ' a > {
50
54
Resolve ( Box < ResolutionFailure < ' a > > ) ,
51
55
AnchorFailure ( AnchorFailure ) ,
@@ -58,18 +62,37 @@ impl<'a> From<ResolutionFailure<'a>> for ErrorKind<'a> {
58
62
}
59
63
60
64
#[ derive( Debug ) ]
65
+ /// A link failed to resolve.
61
66
enum ResolutionFailure < ' a > {
62
67
/// This resolved, but with the wrong namespace.
63
- /// `Namespace` is the expected namespace (as opposed to the actual).
64
- WrongNamespace ( Res , Namespace ) ,
68
+ ///
69
+ /// `Namespace` is the namespace specified with a disambiguator
70
+ /// (as opposed to the actual namespace of the `Res`).
71
+ WrongNamespace ( Res , /* disambiguated */ Namespace ) ,
65
72
/// The link failed to resolve. `resolution_failure` should look to see if there's
66
73
/// a more helpful error that can be given.
67
- NotResolved { module_id : DefId , partial_res : Option < Res > , unresolved : Cow < ' a , str > } ,
68
- /// should not ever happen
74
+ NotResolved {
75
+ /// The scope the link was resolved in.
76
+ module_id : DefId ,
77
+ /// If part of the link resolved, this has the `Res`.
78
+ ///
79
+ /// In `[std::io::Error::x]`, `std::io::Error` would be a partial resolution.
80
+ partial_res : Option < Res > ,
81
+ /// The remaining unresolved path segments.
82
+ ///
83
+ /// In `[std::io::Error::x]`, `x` would be unresolved.
84
+ unresolved : Cow < ' a , str > ,
85
+ } ,
86
+ /// This happens when rustdoc can't determine the parent scope for an item.
87
+ ///
88
+ /// It is always a bug in rustdoc.
69
89
NoParentItem ,
70
90
/// This link has malformed generic parameters; e.g., the angle brackets are unbalanced.
71
91
MalformedGenerics ( MalformedGenerics ) ,
72
- /// used to communicate that this should be ignored, but shouldn't be reported to the user
92
+ /// Used to communicate that this should be ignored, but shouldn't be reported to the user
93
+ ///
94
+ /// This happens when there is no disambiguator and one of the namespaces
95
+ /// failed to resolve.
73
96
Dummy ,
74
97
}
75
98
@@ -115,7 +138,9 @@ enum MalformedGenerics {
115
138
}
116
139
117
140
impl ResolutionFailure < ' a > {
118
- // This resolved fully (not just partially) but is erroneous for some other reason
141
+ /// This resolved fully (not just partially) but is erroneous for some other reason
142
+ ///
143
+ /// Returns the full resolution of the link, if present.
119
144
fn full_res ( & self ) -> Option < Res > {
120
145
match self {
121
146
Self :: WrongNamespace ( res, _) => Some ( * res) ,
@@ -125,13 +150,30 @@ impl ResolutionFailure<'a> {
125
150
}
126
151
127
152
enum AnchorFailure {
153
+ /// User error: `[std#x#y]` is not valid
128
154
MultipleAnchors ,
155
+ /// The anchor provided by the user conflicts with Rustdoc's generated anchor.
156
+ ///
157
+ /// This is an unfortunate state of affairs. Not every item that can be
158
+ /// linked to has its own page; sometimes it is a subheading within a page,
159
+ /// like for associated items. In those cases, rustdoc uses an anchor to
160
+ /// link to the subheading. Since you can't have two anchors for the same
161
+ /// link, Rustdoc disallows having a user-specified anchor.
162
+ ///
163
+ /// Most of the time this is fine, because you can just link to the page of
164
+ /// the item if you want to provide your own anchor. For primitives, though,
165
+ /// rustdoc uses the anchor as a side channel to know which page to link to;
166
+ /// it doesn't show up in the generated link. Ideally, rustdoc would remove
167
+ /// this limitation, allowing you to link to subheaders on primitives.
129
168
RustdocAnchorConflict ( Res ) ,
130
169
}
131
170
132
171
struct LinkCollector < ' a , ' tcx > {
133
172
cx : & ' a DocContext < ' tcx > ,
134
- // NOTE: this may not necessarily be a module in the current crate
173
+ /// A stack of modules used to decide what scope to resolve in.
174
+ ///
175
+ /// The last module will be used if the parent scope of the current item is
176
+ /// unknown.
135
177
mod_ids : Vec < DefId > ,
136
178
/// This is used to store the kind of associated items,
137
179
/// because `clean` and the disambiguator code expect them to be different.
@@ -144,6 +186,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
144
186
LinkCollector { cx, mod_ids : Vec :: new ( ) , kind_side_channel : Cell :: new ( None ) }
145
187
}
146
188
189
+ /// Given a full link, parse it as an [enum struct variant].
190
+ ///
191
+ /// In particular, this will return an error whenever there aren't three
192
+ /// full path segments left in the link.
193
+ ///
194
+ /// [enum struct variant]: hir::VariantData::Struct
147
195
fn variant_field (
148
196
& self ,
149
197
path_str : & ' path str ,
@@ -235,6 +283,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
235
283
}
236
284
}
237
285
286
+ /// Given a primitive type, try to resolve an associated item.
287
+ ///
288
+ /// HACK(jynelson): `item_str` is passed in instead of derived from `item_name` so the
289
+ /// lifetimes on `&'path` will work.
238
290
fn resolve_primitive_associated_item (
239
291
& self ,
240
292
prim_ty : hir:: PrimTy ,
@@ -286,14 +338,17 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
286
338
}
287
339
288
340
/// Resolves a string as a macro.
289
- fn macro_resolve (
341
+ ///
342
+ /// FIXME(jynelson): Can this be unified with `resolve()`?
343
+ fn resolve_macro (
290
344
& self ,
291
345
path_str : & ' a str ,
292
346
module_id : DefId ,
293
347
) -> Result < Res , ResolutionFailure < ' a > > {
294
348
let cx = self . cx ;
295
349
let path = ast:: Path :: from_ident ( Ident :: from_str ( path_str) ) ;
296
350
cx. enter_resolver ( |resolver| {
351
+ // FIXME(jynelson): does this really need 3 separate lookups?
297
352
if let Ok ( ( Some ( ext) , res) ) = resolver. resolve_macro_path (
298
353
& path,
299
354
None ,
@@ -326,6 +381,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
326
381
} )
327
382
}
328
383
384
+ /// Convenience wrapper around `resolve_str_path_error`.
385
+ ///
386
+ /// This also handles resolving `true` and `false` as booleans.
387
+ /// NOTE: `resolve_str_path_error` knows only about paths, not about types.
388
+ /// Associated items will never be resolved by this function.
329
389
fn resolve_path ( & self , path_str : & str , ns : Namespace , module_id : DefId ) -> Option < Res > {
330
390
let result = self . cx . enter_resolver ( |resolver| {
331
391
resolver. resolve_str_path_error ( DUMMY_SP , & path_str, ns, module_id)
@@ -339,12 +399,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
339
399
}
340
400
}
341
401
342
- /// Resolves a string as a path within a particular namespace. Also returns an optional
343
- /// URL fragment in the case of variants and methods.
402
+ /// Resolves a string as a path within a particular namespace. Returns an
403
+ /// optional URL fragment in the case of variants and methods.
344
404
fn resolve < ' path > (
345
405
& self ,
346
406
path_str : & ' path str ,
347
407
ns : Namespace ,
408
+ // FIXME(#76467): This is for `Self`, and it's wrong.
348
409
current_item : & Option < String > ,
349
410
module_id : DefId ,
350
411
extra_fragment : & Option < String > ,
@@ -353,15 +414,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
353
414
354
415
if let Some ( res) = self . resolve_path ( path_str, ns, module_id) {
355
416
match res {
417
+ // FIXME(#76467): make this fallthrough to lookup the associated
418
+ // item a separate function.
356
419
Res :: Def ( DefKind :: AssocFn | DefKind :: AssocConst , _) => {
357
420
assert_eq ! ( ns, ValueNS ) ;
358
- // Fall through: In case this is a trait item, skip the
359
- // early return and try looking for the trait.
360
421
}
361
422
Res :: Def ( DefKind :: AssocTy , _) => {
362
423
assert_eq ! ( ns, TypeNS ) ;
363
- // Fall through: In case this is a trait item, skip the
364
- // early return and try looking for the trait.
365
424
}
366
425
Res :: Def ( DefKind :: Variant , _) => {
367
426
return handle_variant ( cx, res, extra_fragment) ;
@@ -410,7 +469,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
410
469
} ) ?;
411
470
412
471
// FIXME: are these both necessary?
413
- let ty_res = if let Some ( ty_res) = is_primitive ( & path_root, TypeNS )
472
+ let ty_res = if let Some ( ty_res) = resolve_primitive ( & path_root, TypeNS )
414
473
. map ( |( _, res) | res)
415
474
. or_else ( || self . resolve_path ( & path_root, TypeNS , module_id) )
416
475
{
@@ -452,8 +511,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
452
511
// There should only ever be one associated item that matches from any inherent impl
453
512
. next ( )
454
513
// Check if item_name belongs to `impl SomeTrait for SomeItem`
455
- // This gives precedence to `impl SomeItem`:
456
- // Although having both would be ambiguous, use impl version for compat. sake.
514
+ // FIXME(#74563): This gives precedence to `impl SomeItem`:
515
+ // Although having both would be ambiguous, use impl version for compatibility's sake.
457
516
// To handle that properly resolve() would have to support
458
517
// something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
459
518
. or_else ( || {
@@ -480,6 +539,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
480
539
} )
481
540
} else if ns == Namespace :: ValueNS {
482
541
debug ! ( "looking for variants or fields named {} for {:?}" , item_name, did) ;
542
+ // FIXME(jynelson): why is this different from
543
+ // `variant_field`?
483
544
match cx. tcx . type_of ( did) . kind ( ) {
484
545
ty:: Adt ( def, _) => {
485
546
let field = if def. is_enum ( ) {
@@ -577,7 +638,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
577
638
) -> Option < Res > {
578
639
// resolve() can't be used for macro namespace
579
640
let result = match ns {
580
- Namespace :: MacroNS => self . macro_resolve ( path_str, module_id) . map_err ( ErrorKind :: from) ,
641
+ Namespace :: MacroNS => self . resolve_macro ( path_str, module_id) . map_err ( ErrorKind :: from) ,
581
642
Namespace :: TypeNS | Namespace :: ValueNS => self
582
643
. resolve ( path_str, ns, current_item, module_id, extra_fragment)
583
644
. map ( |( res, _) | res) ,
@@ -593,6 +654,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
593
654
}
594
655
}
595
656
657
+ /// Look to see if a resolved item has an associated item named `item_name`.
658
+ ///
659
+ /// Given `[std::io::Error::source]`, where `source` is unresolved, this would
660
+ /// find `std::error::Error::source` and return
661
+ /// `<io::Error as error::Error>::source`.
596
662
fn resolve_associated_trait_item (
597
663
did : DefId ,
598
664
module : DefId ,
@@ -601,12 +667,12 @@ fn resolve_associated_trait_item(
601
667
cx : & DocContext < ' _ > ,
602
668
) -> Option < ( ty:: AssocKind , DefId ) > {
603
669
let ty = cx. tcx . type_of ( did) ;
604
- // First consider automatic impls: `impl From<T> for T`
670
+ // First consider blanket impls: `impl From<T> for T`
605
671
let implicit_impls = crate :: clean:: get_auto_trait_and_blanket_impls ( cx, ty, did) ;
606
672
let mut candidates: Vec < _ > = implicit_impls
607
673
. flat_map ( |impl_outer| {
608
674
match impl_outer. inner {
609
- ImplItem ( impl_) => {
675
+ clean :: ImplItem ( impl_) => {
610
676
debug ! ( "considering auto or blanket impl for trait {:?}" , impl_. trait_) ;
611
677
// Give precedence to methods that were overridden
612
678
if !impl_. provided_trait_methods . contains ( & * item_name. as_str ( ) ) {
@@ -669,7 +735,7 @@ fn resolve_associated_trait_item(
669
735
. map ( |assoc| ( assoc. kind , assoc. def_id ) )
670
736
} ) ) ;
671
737
}
672
- // FIXME: warn about ambiguity
738
+ // FIXME(#74563) : warn about ambiguity
673
739
debug ! ( "the candidates were {:?}" , candidates) ;
674
740
candidates. pop ( )
675
741
}
@@ -719,20 +785,15 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx
719
785
iter. collect ( )
720
786
}
721
787
722
- /// Check for resolve collisions between a trait and its derive
788
+ /// Check for resolve collisions between a trait and its derive.
723
789
///
724
- /// These are common and we should just resolve to the trait in that case
790
+ /// These are common and we should just resolve to the trait in that case.
725
791
fn is_derive_trait_collision < T > ( ns : & PerNS < Result < ( Res , T ) , ResolutionFailure < ' _ > > > ) -> bool {
726
- if let PerNS {
792
+ matches ! ( * ns , PerNS {
727
793
type_ns: Ok ( ( Res :: Def ( DefKind :: Trait , _) , _) ) ,
728
794
macro_ns: Ok ( ( Res :: Def ( DefKind :: Macro ( MacroKind :: Derive ) , _) , _) ) ,
729
795
..
730
- } = * ns
731
- {
732
- true
733
- } else {
734
- false
735
- }
796
+ } )
736
797
}
737
798
738
799
impl < ' a , ' tcx > DocFolder for LinkCollector < ' a , ' tcx > {
@@ -772,29 +833,30 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
772
833
}
773
834
774
835
let current_item = match item. inner {
775
- ModuleItem ( ..) => {
836
+ clean :: ModuleItem ( ..) => {
776
837
if item. attrs . inner_docs {
777
838
if item. def_id . is_top_level_module ( ) { item. name . clone ( ) } else { None }
778
839
} else {
779
840
match parent_node. or ( self . mod_ids . last ( ) . copied ( ) ) {
780
841
Some ( parent) if !parent. is_top_level_module ( ) => {
781
- // FIXME: can we pull the parent module's name from elsewhere?
782
842
Some ( self . cx . tcx . item_name ( parent) . to_string ( ) )
783
843
}
784
844
_ => None ,
785
845
}
786
846
}
787
847
}
788
- ImplItem ( Impl { ref for_, .. } ) => {
848
+ clean :: ImplItem ( clean :: Impl { ref for_, .. } ) => {
789
849
for_. def_id ( ) . map ( |did| self . cx . tcx . item_name ( did) . to_string ( ) )
790
850
}
791
851
// we don't display docs on `extern crate` items anyway, so don't process them.
792
- ExternCrateItem ( ..) => {
852
+ clean :: ExternCrateItem ( ..) => {
793
853
debug ! ( "ignoring extern crate item {:?}" , item. def_id) ;
794
854
return self . fold_item_recur ( item) ;
795
855
}
796
- ImportItem ( Import { kind : ImportKind :: Simple ( ref name, ..) , .. } ) => Some ( name. clone ( ) ) ,
797
- MacroItem ( ..) => None ,
856
+ clean:: ImportItem ( Import { kind : clean:: ImportKind :: Simple ( ref name, ..) , .. } ) => {
857
+ Some ( name. clone ( ) )
858
+ }
859
+ clean:: MacroItem ( ..) => None ,
798
860
_ => item. name . clone ( ) ,
799
861
} ;
800
862
@@ -803,6 +865,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
803
865
}
804
866
805
867
// find item's parent to resolve `Self` in item's docs below
868
+ // FIXME(#76467, #75809): this is a mess and doesn't handle cross-crate
869
+ // re-exports
806
870
let parent_name = self . cx . as_local_hir_id ( item. def_id ) . and_then ( |item_hir| {
807
871
let parent_hir = self . cx . tcx . hir ( ) . get_parent_item ( item_hir) ;
808
872
let item_parent = self . cx . tcx . hir ( ) . find ( parent_hir) ;
@@ -870,7 +934,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
870
934
} ;
871
935
// NOTE: if there are links that start in one crate and end in another, this will not resolve them.
872
936
// This is a degenerate case and it's not supported by rustdoc.
873
- // FIXME: this will break links that start in `#[doc = ...]` and end as a sugared doc. Should this be supported?
874
937
for ( ori_link, link_range) in markdown_links ( & combined_docs) {
875
938
let link = self . resolve_link (
876
939
& item,
@@ -888,15 +951,13 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
888
951
}
889
952
}
890
953
891
- if item. is_mod ( ) && !item. attrs . inner_docs {
892
- self . mod_ids . push ( item. def_id ) ;
893
- }
894
-
895
954
if item. is_mod ( ) {
896
- let ret = self . fold_item_recur ( item) ;
955
+ if !item. attrs . inner_docs {
956
+ self . mod_ids . push ( item. def_id ) ;
957
+ }
897
958
959
+ let ret = self . fold_item_recur ( item) ;
898
960
self . mod_ids . pop ( ) ;
899
-
900
961
ret
901
962
} else {
902
963
self . fold_item_recur ( item)
@@ -905,6 +966,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
905
966
}
906
967
907
968
impl LinkCollector < ' _ , ' _ > {
969
+ /// This is the entry point for resolving an intra-doc link.
970
+ ///
971
+ /// FIXME(jynelson): this is way too many arguments
908
972
fn resolve_link (
909
973
& self ,
910
974
item : & Item ,
@@ -943,129 +1007,120 @@ impl LinkCollector<'_, '_> {
943
1007
} else {
944
1008
( parts[ 0 ] , None )
945
1009
} ;
946
- let resolved_self;
947
- let link_text;
948
- let mut path_str;
949
- let disambiguator;
950
- let stripped_path_string;
951
- let ( mut res, mut fragment) = {
952
- path_str = if let Ok ( ( d, path) ) = Disambiguator :: from_str ( & link) {
953
- disambiguator = Some ( d) ;
954
- path
955
- } else {
956
- disambiguator = None ;
957
- & link
958
- }
959
- . trim ( ) ;
960
1010
961
- if path_str. contains ( |ch : char | !( ch. is_alphanumeric ( ) || ":_<>, " . contains ( ch) ) ) {
962
- return None ;
963
- }
1011
+ // Parse and strip the disambiguator from the link, if present.
1012
+ let ( mut path_str, disambiguator) = if let Ok ( ( d, path) ) = Disambiguator :: from_str ( & link) {
1013
+ ( path. trim ( ) , Some ( d) )
1014
+ } else {
1015
+ ( link. trim ( ) , None )
1016
+ } ;
964
1017
965
- // We stripped `()` and `!` when parsing the disambiguator.
966
- // Add them back to be displayed, but not prefix disambiguators.
967
- link_text = disambiguator
968
- . map ( |d| d. display_for ( path_str) )
969
- . unwrap_or_else ( || path_str. to_owned ( ) ) ;
970
-
971
- // In order to correctly resolve intra-doc-links we need to
972
- // pick a base AST node to work from. If the documentation for
973
- // this module came from an inner comment (//!) then we anchor
974
- // our name resolution *inside* the module. If, on the other
975
- // hand it was an outer comment (///) then we anchor the name
976
- // resolution in the parent module on the basis that the names
977
- // used are more likely to be intended to be parent names. For
978
- // this, we set base_node to None for inner comments since
979
- // we've already pushed this node onto the resolution stack but
980
- // for outer comments we explicitly try and resolve against the
981
- // parent_node first.
982
- let base_node = if item. is_mod ( ) && item. attrs . inner_docs {
983
- self . mod_ids . last ( ) . copied ( )
984
- } else {
985
- parent_node
986
- } ;
1018
+ if path_str. contains ( |ch : char | !( ch. is_alphanumeric ( ) || ":_<>, " . contains ( ch) ) ) {
1019
+ return None ;
1020
+ }
987
1021
988
- let mut module_id = if let Some ( id) = base_node {
989
- id
990
- } else {
991
- debug ! ( "attempting to resolve item without parent module: {}" , path_str) ;
992
- let err_kind = ResolutionFailure :: NoParentItem . into ( ) ;
993
- resolution_failure (
994
- self ,
995
- & item,
996
- path_str,
997
- disambiguator,
998
- dox,
999
- link_range,
1000
- smallvec ! [ err_kind] ,
1001
- ) ;
1002
- return None ;
1003
- } ;
1022
+ // We stripped `()` and `!` when parsing the disambiguator.
1023
+ // Add them back to be displayed, but not prefix disambiguators.
1024
+ let link_text =
1025
+ disambiguator. map ( |d| d. display_for ( path_str) ) . unwrap_or_else ( || path_str. to_owned ( ) ) ;
1026
+
1027
+ // In order to correctly resolve intra-doc-links we need to
1028
+ // pick a base AST node to work from. If the documentation for
1029
+ // this module came from an inner comment (//!) then we anchor
1030
+ // our name resolution *inside* the module. If, on the other
1031
+ // hand it was an outer comment (///) then we anchor the name
1032
+ // resolution in the parent module on the basis that the names
1033
+ // used are more likely to be intended to be parent names. For
1034
+ // this, we set base_node to None for inner comments since
1035
+ // we've already pushed this node onto the resolution stack but
1036
+ // for outer comments we explicitly try and resolve against the
1037
+ // parent_node first.
1038
+ let base_node = if item. is_mod ( ) && item. attrs . inner_docs {
1039
+ self . mod_ids . last ( ) . copied ( )
1040
+ } else {
1041
+ parent_node
1042
+ } ;
1004
1043
1005
- // replace `Self` with suitable item's parent name
1006
- if path_str. starts_with ( "Self::" ) {
1007
- if let Some ( ref name) = parent_name {
1008
- resolved_self = format ! ( "{}::{}" , name, & path_str[ 6 ..] ) ;
1009
- path_str = & resolved_self;
1010
- }
1011
- } else if path_str. starts_with ( "crate::" ) {
1012
- use rustc_span:: def_id:: CRATE_DEF_INDEX ;
1013
-
1014
- // HACK(jynelson): rustc_resolve thinks that `crate` is the crate currently being documented.
1015
- // But rustdoc wants it to mean the crate this item was originally present in.
1016
- // To work around this, remove it and resolve relative to the crate root instead.
1017
- // HACK(jynelson)(2): If we just strip `crate::` then suddenly primitives become ambiguous
1018
- // (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root.
1019
- resolved_self = format ! ( "self::{}" , & path_str[ "crate::" . len( ) ..] ) ;
1020
- path_str = & resolved_self;
1021
- module_id = DefId { krate, index : CRATE_DEF_INDEX } ;
1022
- }
1044
+ let mut module_id = if let Some ( id) = base_node {
1045
+ id
1046
+ } else {
1047
+ // This is a bug.
1048
+ debug ! ( "attempting to resolve item without parent module: {}" , path_str) ;
1049
+ let err_kind = ResolutionFailure :: NoParentItem . into ( ) ;
1050
+ resolution_failure (
1051
+ self ,
1052
+ & item,
1053
+ path_str,
1054
+ disambiguator,
1055
+ dox,
1056
+ link_range,
1057
+ smallvec ! [ err_kind] ,
1058
+ ) ;
1059
+ return None ;
1060
+ } ;
1023
1061
1024
- // Strip generics from the path.
1025
- if path_str. contains ( [ '<' , '>' ] . as_slice ( ) ) {
1026
- stripped_path_string = match strip_generics_from_path ( path_str) {
1027
- Ok ( path) => path,
1028
- Err ( err_kind) => {
1029
- debug ! ( "link has malformed generics: {}" , path_str) ;
1030
- resolution_failure (
1031
- self ,
1032
- & item,
1033
- path_str,
1034
- disambiguator,
1035
- dox,
1036
- link_range,
1037
- smallvec ! [ err_kind] ,
1038
- ) ;
1039
- return None ;
1040
- }
1041
- } ;
1042
- path_str = & stripped_path_string;
1062
+ let resolved_self;
1063
+ // replace `Self` with suitable item's parent name
1064
+ if path_str. starts_with ( "Self::" ) {
1065
+ if let Some ( ref name) = parent_name {
1066
+ resolved_self = format ! ( "{}::{}" , name, & path_str[ 6 ..] ) ;
1067
+ path_str = & resolved_self;
1043
1068
}
1069
+ } else if path_str. starts_with ( "crate::" ) {
1070
+ use rustc_span:: def_id:: CRATE_DEF_INDEX ;
1071
+
1072
+ // HACK(jynelson): rustc_resolve thinks that `crate` is the crate currently being documented.
1073
+ // But rustdoc wants it to mean the crate this item was originally present in.
1074
+ // To work around this, remove it and resolve relative to the crate root instead.
1075
+ // HACK(jynelson)(2): If we just strip `crate::` then suddenly primitives become ambiguous
1076
+ // (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root.
1077
+ // FIXME(#78696): This doesn't always work.
1078
+ resolved_self = format ! ( "self::{}" , & path_str[ "crate::" . len( ) ..] ) ;
1079
+ path_str = & resolved_self;
1080
+ module_id = DefId { krate, index : CRATE_DEF_INDEX } ;
1081
+ }
1044
1082
1045
- // Sanity check to make sure we don't have any angle brackets after stripping generics.
1046
- assert ! ( !path_str. contains( [ '<' , '>' ] . as_slice( ) ) ) ;
1083
+ // Strip generics from the path.
1084
+ let stripped_path_string;
1085
+ if path_str. contains ( [ '<' , '>' ] . as_slice ( ) ) {
1086
+ stripped_path_string = match strip_generics_from_path ( path_str) {
1087
+ Ok ( path) => path,
1088
+ Err ( err_kind) => {
1089
+ debug ! ( "link has malformed generics: {}" , path_str) ;
1090
+ resolution_failure (
1091
+ self ,
1092
+ & item,
1093
+ path_str,
1094
+ disambiguator,
1095
+ dox,
1096
+ link_range,
1097
+ smallvec ! [ err_kind] ,
1098
+ ) ;
1099
+ return None ;
1100
+ }
1101
+ } ;
1102
+ path_str = & stripped_path_string;
1103
+ }
1104
+ // Sanity check to make sure we don't have any angle brackets after stripping generics.
1105
+ assert ! ( !path_str. contains( [ '<' , '>' ] . as_slice( ) ) ) ;
1047
1106
1048
- // The link is not an intra-doc link if it still contains commas or spaces after
1049
- // stripping generics.
1050
- if path_str. contains ( [ ',' , ' ' ] . as_slice ( ) ) {
1051
- return None ;
1052
- }
1107
+ // The link is not an intra-doc link if it still contains commas or spaces after
1108
+ // stripping generics.
1109
+ if path_str. contains ( [ ',' , ' ' ] . as_slice ( ) ) {
1110
+ return None ;
1111
+ }
1053
1112
1054
- match self . resolve_with_disambiguator (
1055
- disambiguator,
1056
- item,
1057
- dox,
1058
- path_str,
1059
- current_item,
1060
- module_id,
1061
- extra_fragment,
1062
- & ori_link,
1063
- link_range. clone ( ) ,
1064
- ) {
1065
- Some ( x) => x,
1066
- None => return None ,
1067
- }
1068
- } ;
1113
+ let ( mut res, mut fragment) = self . resolve_with_disambiguator (
1114
+ disambiguator,
1115
+ item,
1116
+ dox,
1117
+ path_str,
1118
+ current_item,
1119
+ module_id,
1120
+ extra_fragment,
1121
+ & ori_link,
1122
+ link_range. clone ( ) ,
1123
+ ) ?;
1069
1124
1070
1125
// Check for a primitive which might conflict with a module
1071
1126
// Report the ambiguity and require that the user specify which one they meant.
@@ -1075,7 +1130,7 @@ impl LinkCollector<'_, '_> {
1075
1130
None | Some ( Disambiguator :: Namespace ( Namespace :: TypeNS ) | Disambiguator :: Primitive )
1076
1131
) && !matches ! ( res, Res :: PrimTy ( _) )
1077
1132
{
1078
- if let Some ( ( path, prim) ) = is_primitive ( path_str, TypeNS ) {
1133
+ if let Some ( ( path, prim) ) = resolve_primitive ( path_str, TypeNS ) {
1079
1134
// `prim@char`
1080
1135
if matches ! ( disambiguator, Some ( Disambiguator :: Primitive ) ) {
1081
1136
if fragment. is_some ( ) {
@@ -1168,11 +1223,13 @@ impl LinkCollector<'_, '_> {
1168
1223
privacy_error ( cx, & item, & path_str, dox, link_range) ;
1169
1224
}
1170
1225
}
1171
- let id = register_res ( cx, res) ;
1226
+ let id = clean :: register_res ( cx, res) ;
1172
1227
Some ( ItemLink { link : ori_link, link_text, did : Some ( id) , fragment } )
1173
1228
}
1174
1229
}
1175
1230
1231
+ /// After parsing the disambiguator, resolve the main part of the link.
1232
+ // FIXME(jynelson): wow this is just so much
1176
1233
fn resolve_with_disambiguator (
1177
1234
& self ,
1178
1235
disambiguator : Option < Disambiguator > ,
@@ -1232,7 +1289,7 @@ impl LinkCollector<'_, '_> {
1232
1289
// Try everything!
1233
1290
let mut candidates = PerNS {
1234
1291
macro_ns : self
1235
- . macro_resolve ( path_str, base_node)
1292
+ . resolve_macro ( path_str, base_node)
1236
1293
. map ( |res| ( res, extra_fragment. clone ( ) ) ) ,
1237
1294
type_ns : match self . resolve (
1238
1295
path_str,
@@ -1320,10 +1377,10 @@ impl LinkCollector<'_, '_> {
1320
1377
}
1321
1378
}
1322
1379
Some ( MacroNS ) => {
1323
- match self . macro_resolve ( path_str, base_node) {
1380
+ match self . resolve_macro ( path_str, base_node) {
1324
1381
Ok ( res) => Some ( ( res, extra_fragment) ) ,
1325
1382
Err ( mut kind) => {
1326
- // `macro_resolve ` only looks in the macro namespace. Try to give a better error if possible.
1383
+ // `resolve_macro ` only looks in the macro namespace. Try to give a better error if possible.
1327
1384
for & ns in & [ TypeNS , ValueNS ] {
1328
1385
if let Some ( res) = self . check_full_res (
1329
1386
ns,
@@ -1354,9 +1411,15 @@ impl LinkCollector<'_, '_> {
1354
1411
}
1355
1412
1356
1413
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
1414
+ /// Disambiguators for a link.
1357
1415
enum Disambiguator {
1416
+ /// `prim@`
1417
+ ///
1418
+ /// This is buggy, see <https://github.com/rust-lang/rust/pull/77875#discussion_r503583103>
1358
1419
Primitive ,
1420
+ /// `struct@` or `f()`
1359
1421
Kind ( DefKind ) ,
1422
+ /// `type@`
1360
1423
Namespace ( Namespace ) ,
1361
1424
}
1362
1425
@@ -1373,7 +1436,7 @@ impl Disambiguator {
1373
1436
}
1374
1437
}
1375
1438
1376
- /// (disambiguator, path_str)
1439
+ /// Given a link, parse and return ` (disambiguator, path_str)`
1377
1440
fn from_str ( link : & str ) -> Result < ( Self , & str ) , ( ) > {
1378
1441
use Disambiguator :: { Kind , Namespace as NS , Primitive } ;
1379
1442
@@ -1424,6 +1487,7 @@ impl Disambiguator {
1424
1487
}
1425
1488
}
1426
1489
1490
+ /// Used for error reporting.
1427
1491
fn suggestion ( self ) -> Suggestion {
1428
1492
let kind = match self {
1429
1493
Disambiguator :: Primitive => return Suggestion :: Prefix ( "prim" ) ,
@@ -1490,9 +1554,13 @@ impl Disambiguator {
1490
1554
}
1491
1555
}
1492
1556
1557
+ /// A suggestion to show in a diagnostic.
1493
1558
enum Suggestion {
1559
+ /// `struct@`
1494
1560
Prefix ( & ' static str ) ,
1561
+ /// `f()`
1495
1562
Function ,
1563
+ /// `m!`
1496
1564
Macro ,
1497
1565
}
1498
1566
@@ -1582,6 +1650,11 @@ fn report_diagnostic(
1582
1650
} ) ;
1583
1651
}
1584
1652
1653
+ /// Reports a link that failed to resolve.
1654
+ ///
1655
+ /// This also tries to resolve any intermediate path segments that weren't
1656
+ /// handled earlier. For example, if passed `Item::Crate(std)` and `path_str`
1657
+ /// `std::io::Error::x`, this will resolve `std::io::Error`.
1585
1658
fn resolution_failure (
1586
1659
collector : & LinkCollector < ' _ , ' _ > ,
1587
1660
item : & Item ,
@@ -1816,6 +1889,7 @@ fn resolution_failure(
1816
1889
) ;
1817
1890
}
1818
1891
1892
+ /// Report an anchor failure.
1819
1893
fn anchor_failure (
1820
1894
cx : & DocContext < ' _ > ,
1821
1895
item : & Item ,
@@ -1840,6 +1914,7 @@ fn anchor_failure(
1840
1914
} ) ;
1841
1915
}
1842
1916
1917
+ /// Report an ambiguity error, where there were multiple possible resolutions.
1843
1918
fn ambiguity_error (
1844
1919
cx : & DocContext < ' _ > ,
1845
1920
item : & Item ,
@@ -1886,6 +1961,8 @@ fn ambiguity_error(
1886
1961
} ) ;
1887
1962
}
1888
1963
1964
+ /// In case of an ambiguity or mismatched disambiguator, suggest the correct
1965
+ /// disambiguator.
1889
1966
fn suggest_disambiguator (
1890
1967
disambiguator : Disambiguator ,
1891
1968
diag : & mut DiagnosticBuilder < ' _ > ,
@@ -1911,6 +1988,7 @@ fn suggest_disambiguator(
1911
1988
}
1912
1989
}
1913
1990
1991
+ /// Report a link from a public item to a private one.
1914
1992
fn privacy_error (
1915
1993
cx : & DocContext < ' _ > ,
1916
1994
item : & Item ,
@@ -1978,7 +2056,8 @@ const PRIMITIVES: &[(Symbol, Res)] = &[
1978
2056
( sym:: char, Res :: PrimTy ( hir:: PrimTy :: Char ) ) ,
1979
2057
] ;
1980
2058
1981
- fn is_primitive ( path_str : & str , ns : Namespace ) -> Option < ( Symbol , Res ) > {
2059
+ /// Resolve a primitive type or value.
2060
+ fn resolve_primitive ( path_str : & str , ns : Namespace ) -> Option < ( Symbol , Res ) > {
1982
2061
is_bool_value ( path_str, ns) . or_else ( || {
1983
2062
if ns == TypeNS {
1984
2063
// FIXME: this should be replaced by a lookup in PrimitiveTypeTable
@@ -1990,6 +2069,7 @@ fn is_primitive(path_str: &str, ns: Namespace) -> Option<(Symbol, Res)> {
1990
2069
} )
1991
2070
}
1992
2071
2072
+ /// Resolve a primitive value.
1993
2073
fn is_bool_value ( path_str : & str , ns : Namespace ) -> Option < ( Symbol , Res ) > {
1994
2074
if ns == TypeNS && ( path_str == "true" || path_str == "false" ) {
1995
2075
Some ( ( sym:: bool, Res :: PrimTy ( hir:: PrimTy :: Bool ) ) )
0 commit comments