1
1
use rustc_ast as ast;
2
- use rustc_hir:: def:: Namespace :: TypeNS ;
2
+ use rustc_hir:: def:: Namespace :: { self , * } ;
3
3
use rustc_hir:: def_id:: { DefId , LocalDefId , CRATE_DEF_INDEX } ;
4
4
use rustc_interface:: interface;
5
+ use rustc_resolve:: Resolver ;
5
6
6
7
use std:: cell:: RefCell ;
7
8
use std:: mem;
8
9
use std:: rc:: Rc ;
9
10
11
+ use super :: * ;
12
+
10
13
// Letting the resolver escape at the end of the function leads to inconsistencies between the
11
14
// crates the TyCtxt sees and the resolver sees (because the resolver could load more crates
12
15
// after escaping). Hopefully `IntraLinkCrateLoader` gets all the crates we need ...
@@ -20,6 +23,124 @@ impl IntraLinkCrateLoader {
20
23
let crate_id = LocalDefId { local_def_index : CRATE_DEF_INDEX } . to_def_id ( ) ;
21
24
Self { current_mod : crate_id, resolver }
22
25
}
26
+
27
+ crate fn enter_resolver < F , R > ( & self , f : F ) -> R
28
+ where
29
+ F : FnOnce ( & mut Resolver < ' _ > ) -> R ,
30
+ {
31
+ self . resolver . borrow_mut ( ) . access ( f)
32
+ }
33
+
34
+ }
35
+
36
+ impl IntraLinkCrateLoader {
37
+ /// Convenience wrapper around `resolve_str_path_error`.
38
+ ///
39
+ /// This also handles resolving `true` and `false` as booleans.
40
+ /// NOTE: `resolve_str_path_error` knows only about paths, not about types.
41
+ /// Associated items will never be resolved by this function.
42
+ fn resolve_path ( & self , path_str : & str , ns : Namespace , module_id : DefId ) -> Option < Res > {
43
+ let result = self . enter_resolver ( |resolver| {
44
+ resolver
45
+ . resolve_str_path_error ( DUMMY_SP , & path_str, ns, module_id)
46
+ . and_then ( |( _, res) | res. try_into ( ) )
47
+ } ) ;
48
+ debug ! ( "{} resolved to {:?} in namespace {:?}" , path_str, result, ns) ;
49
+ match result {
50
+ // resolver doesn't know about true, false, and types that aren't paths (e.g. `()`)
51
+ // manually as bool
52
+ Err ( ( ) ) => resolve_primitive ( path_str, ns) ,
53
+ Ok ( res) => Some ( res) ,
54
+ }
55
+ }
56
+
57
+ /// Resolves a string as a path within a particular namespace. Returns an
58
+ /// optional URL fragment in the case of variants and methods.
59
+ fn resolve < ' path > (
60
+ & mut self ,
61
+ path_str : & ' path str ,
62
+ ns : Namespace ,
63
+ module_id : DefId ,
64
+ extra_fragment : & Option < String > ,
65
+ ) -> EarlyResult {
66
+ if let Some ( res) = self . resolve_path ( path_str, ns, module_id) {
67
+ match res {
68
+ // FIXME(#76467): make this fallthrough to lookup the associated
69
+ // item a separate function.
70
+ Res :: Def ( DefKind :: AssocFn | DefKind :: AssocConst , _) => assert_eq ! ( ns, ValueNS ) ,
71
+ Res :: Def ( DefKind :: AssocTy , _) => assert_eq ! ( ns, TypeNS ) ,
72
+ Res :: Def ( DefKind :: Variant , _) => {
73
+ return EarlyResult :: UnresolvedVariant ( res) ;
74
+ }
75
+ // Not a trait item; just return what we found.
76
+ Res :: Primitive ( ty) => {
77
+ if extra_fragment. is_some ( ) {
78
+ return EarlyResult :: Error ( ErrorKind :: AnchorFailure (
79
+ AnchorFailure :: RustdocAnchorConflict ( res) ,
80
+ ) ) ;
81
+ }
82
+ return EarlyResult :: Resolved ( res, Some ( ty. as_str ( ) . to_owned ( ) ) ) ;
83
+ }
84
+ _ => return EarlyResult :: Resolved ( res, extra_fragment. clone ( ) ) ,
85
+ }
86
+ }
87
+
88
+ // Try looking for methods and associated items.
89
+ let mut split = path_str. rsplitn ( 2 , "::" ) ;
90
+ // NB: `split`'s first element is always defined, even if the delimiter was not present.
91
+ // NB: `item_str` could be empty when resolving in the root namespace (e.g. `::std`).
92
+ let item_str = split. next ( ) . unwrap ( ) ;
93
+ let item_name = Symbol :: intern ( item_str) ;
94
+ let path_root = match split. next ( ) {
95
+ Some ( r) => r. to_owned ( ) ,
96
+ None => {
97
+ // If there's no `::`, it's not an associated item.
98
+ // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
99
+ debug ! ( "found no `::`, assumming {} was correctly not in scope" , item_name) ;
100
+ return EarlyResult :: Error ( ResolutionFailure :: NotResolved {
101
+ module_id,
102
+ partial_res : None ,
103
+ unresolved : item_str. into ( ) ,
104
+ } . into ( ) ) ;
105
+ }
106
+ } ;
107
+
108
+ // FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support
109
+ // links to primitives when `#[doc(primitive)]` is present. It should give an ambiguity
110
+ // error instead and special case *only* modules with `#[doc(primitive)]`, not all
111
+ // primitives.
112
+ let ty_res = resolve_primitive ( & path_root, TypeNS )
113
+ . or_else ( || self . resolve_path ( & path_root, TypeNS , module_id) ) ;
114
+ let variant_res = if ns == Namespace :: ValueNS {
115
+ self . variant_res ( path_str, module_id)
116
+ } else {
117
+ None
118
+ } ;
119
+ EarlyResult :: Unresolved ( UnresolvedLink {
120
+ ty_res, variant_res
121
+ } )
122
+ }
123
+
124
+ fn variant_res ( & self , path_str : & str , module_id : DefId ) -> Option < Res > {
125
+ debug ! ( "looking for enum variant {}" , path_str) ;
126
+ let mut split = path_str. rsplitn ( 3 , "::" ) ;
127
+ let ( variant_field_str, variant_field_name) = split
128
+ . next ( )
129
+ . map ( |f| ( f, Symbol :: intern ( f) ) )
130
+ . expect ( "fold_item should ensure link is non-empty" ) ;
131
+ // we're not sure this is a variant at all, so use the full string
132
+ // If there's no second component, the link looks like `[path]`.
133
+ // So there's no partial res and we should say the whole link failed to resolve.
134
+ let variant_str = split. next ( ) ?;
135
+ let variant_name = Symbol :: intern ( variant_str) ;
136
+ let path = split. next ( ) ?. to_owned ( ) ;
137
+ self
138
+ . enter_resolver ( |resolver| {
139
+ resolver. resolve_str_path_error ( DUMMY_SP , & path, TypeNS , module_id)
140
+ } )
141
+ . and_then ( |( _, res) | res. try_into ( ) )
142
+ . ok ( )
143
+ }
23
144
}
24
145
25
146
impl ast:: visit:: Visitor < ' _ > for IntraLinkCrateLoader {
0 commit comments