]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/passes/collect_intra_doc_links.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / src / librustdoc / passes / collect_intra_doc_links.rs
CommitLineData
3dfed10e
XL
1use rustc_ast as ast;
2use rustc_data_structures::stable_set::FxHashSet;
3use rustc_errors::{Applicability, DiagnosticBuilder};
dfeec247 4use rustc_expand::base::SyntaxExtensionKind;
dfeec247
XL
5use rustc_hir as hir;
6use rustc_hir::def::{
60c5eb7d
XL
7 DefKind,
8 Namespace::{self, *},
9 PerNS, Res,
10};
3dfed10e 11use rustc_hir::def_id::DefId;
ba9703b0 12use rustc_middle::ty;
e1599b0c 13use rustc_resolve::ParentScope;
1b1a35ee
XL
14use rustc_session::lint::{
15 builtin::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS},
16 Lint,
17};
f035d41b 18use rustc_span::hygiene::MacroKind;
f9f354fc 19use rustc_span::symbol::Ident;
dfeec247
XL
20use rustc_span::symbol::Symbol;
21use rustc_span::DUMMY_SP;
1b1a35ee 22use smallvec::{smallvec, SmallVec};
b7449926 23
1b1a35ee 24use std::borrow::Cow;
3dfed10e 25use std::cell::Cell;
b7449926
XL
26use std::ops::Range;
27
60c5eb7d 28use crate::clean::*;
9fa01778
XL
29use crate::core::DocContext;
30use crate::fold::DocFolder;
31use crate::html::markdown::markdown_links;
3dfed10e 32use crate::passes::Pass;
0bf4aa26 33
9fa01778 34use super::span_of_attrs;
b7449926 35
532ac7d7
XL
36pub const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
37 name: "collect-intra-doc-links",
60c5eb7d 38 run: collect_intra_doc_links,
532ac7d7
XL
39 description: "reads a crate's documentation to resolve intra-doc-links",
40};
b7449926 41
532ac7d7 42pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate {
1b1a35ee
XL
43 let mut coll = LinkCollector::new(cx);
44 coll.fold_crate(krate)
45}
b7449926 46
1b1a35ee
XL
47enum ErrorKind<'a> {
48 Resolve(Box<ResolutionFailure<'a>>),
49 AnchorFailure(AnchorFailure),
50}
51
52impl<'a> From<ResolutionFailure<'a>> for ErrorKind<'a> {
53 fn from(err: ResolutionFailure<'a>) -> Self {
54 ErrorKind::Resolve(box err)
b7449926
XL
55 }
56}
57
1b1a35ee
XL
58#[derive(Debug)]
59enum ResolutionFailure<'a> {
60 /// This resolved, but with the wrong namespace.
61 /// `Namespace` is the expected namespace (as opposed to the actual).
62 WrongNamespace(Res, Namespace),
63 /// The link failed to resolve. `resolution_failure` should look to see if there's
64 /// a more helpful error that can be given.
65 NotResolved { module_id: DefId, partial_res: Option<Res>, unresolved: Cow<'a, str> },
66 /// should not ever happen
67 NoParentItem,
68 /// used to communicate that this should be ignored, but shouldn't be reported to the user
69 Dummy,
70}
71
72impl ResolutionFailure<'a> {
73 // This resolved fully (not just partially) but is erroneous for some other reason
74 fn full_res(&self) -> Option<Res> {
75 match self {
76 Self::WrongNamespace(res, _) => Some(*res),
77 _ => None,
78 }
79 }
3dfed10e
XL
80}
81
82enum AnchorFailure {
83 MultipleAnchors,
1b1a35ee 84 RustdocAnchorConflict(Res),
60c5eb7d
XL
85}
86
532ac7d7
XL
87struct LinkCollector<'a, 'tcx> {
88 cx: &'a DocContext<'tcx>,
3dfed10e
XL
89 // NOTE: this may not necessarily be a module in the current crate
90 mod_ids: Vec<DefId>,
91 /// This is used to store the kind of associated items,
92 /// because `clean` and the disambiguator code expect them to be different.
93 /// See the code for associated items on inherent impls for details.
1b1a35ee 94 kind_side_channel: Cell<Option<(DefKind, DefId)>>,
b7449926
XL
95}
96
532ac7d7
XL
97impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
98 fn new(cx: &'a DocContext<'tcx>) -> Self {
3dfed10e 99 LinkCollector { cx, mod_ids: Vec::new(), kind_side_channel: Cell::new(None) }
60c5eb7d
XL
100 }
101
102 fn variant_field(
103 &self,
1b1a35ee 104 path_str: &'path str,
60c5eb7d 105 current_item: &Option<String>,
3dfed10e 106 module_id: DefId,
1b1a35ee 107 ) -> Result<(Res, Option<String>), ErrorKind<'path>> {
60c5eb7d 108 let cx = self.cx;
1b1a35ee
XL
109 let no_res = || ResolutionFailure::NotResolved {
110 module_id,
111 partial_res: None,
112 unresolved: path_str.into(),
113 };
60c5eb7d 114
1b1a35ee 115 debug!("looking for enum variant {}", path_str);
60c5eb7d 116 let mut split = path_str.rsplitn(3, "::");
1b1a35ee
XL
117 let (variant_field_str, variant_field_name) = split
118 .next()
119 .map(|f| (f, Symbol::intern(f)))
120 .expect("fold_item should ensure link is non-empty");
121 let (variant_str, variant_name) =
122 // we're not sure this is a variant at all, so use the full string
123 // If there's no second component, the link looks like `[path]`.
124 // So there's no partial res and we should say the whole link failed to resolve.
125 split.next().map(|f| (f, Symbol::intern(f))).ok_or_else(no_res)?;
60c5eb7d
XL
126 let path = split
127 .next()
128 .map(|f| {
129 if f == "self" || f == "Self" {
130 if let Some(name) = current_item.as_ref() {
131 return name.clone();
132 }
133 }
134 f.to_owned()
135 })
1b1a35ee
XL
136 // If there's no third component, we saw `[a::b]` before and it failed to resolve.
137 // So there's no partial res.
138 .ok_or_else(no_res)?;
139 let ty_res = cx
60c5eb7d
XL
140 .enter_resolver(|resolver| {
141 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
142 })
1b1a35ee
XL
143 .map(|(_, res)| res)
144 .unwrap_or(Res::Err);
60c5eb7d 145 if let Res::Err = ty_res {
1b1a35ee 146 return Err(no_res().into());
60c5eb7d
XL
147 }
148 let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
149 match ty_res {
150 Res::Def(DefKind::Enum, did) => {
151 if cx
152 .tcx
153 .inherent_impls(did)
154 .iter()
74b04a01 155 .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order())
60c5eb7d
XL
156 .any(|item| item.ident.name == variant_name)
157 {
1b1a35ee
XL
158 // This is just to let `fold_item` know that this shouldn't be considered;
159 // it's a bug for the error to make it to the user
160 return Err(ResolutionFailure::Dummy.into());
60c5eb7d 161 }
1b1a35ee 162 match cx.tcx.type_of(did).kind() {
60c5eb7d
XL
163 ty::Adt(def, _) if def.is_enum() => {
164 if def.all_fields().any(|item| item.ident.name == variant_field_name) {
165 Ok((
166 ty_res,
167 Some(format!(
168 "variant.{}.field.{}",
1b1a35ee 169 variant_str, variant_field_name
60c5eb7d
XL
170 )),
171 ))
172 } else {
1b1a35ee
XL
173 Err(ResolutionFailure::NotResolved {
174 module_id,
175 partial_res: Some(Res::Def(DefKind::Enum, def.did)),
176 unresolved: variant_field_str.into(),
177 }
178 .into())
60c5eb7d
XL
179 }
180 }
1b1a35ee 181 _ => unreachable!(),
60c5eb7d
XL
182 }
183 }
1b1a35ee
XL
184 _ => Err(ResolutionFailure::NotResolved {
185 module_id,
186 partial_res: Some(ty_res),
187 unresolved: variant_str.into(),
188 }
189 .into()),
b7449926
XL
190 }
191 }
192
f035d41b 193 /// Resolves a string as a macro.
1b1a35ee
XL
194 fn macro_resolve(
195 &self,
196 path_str: &'a str,
197 module_id: DefId,
198 ) -> Result<Res, ResolutionFailure<'a>> {
f035d41b
XL
199 let cx = self.cx;
200 let path = ast::Path::from_ident(Ident::from_str(path_str));
201 cx.enter_resolver(|resolver| {
202 if let Ok((Some(ext), res)) = resolver.resolve_macro_path(
203 &path,
204 None,
205 &ParentScope::module(resolver.graph_root()),
206 false,
207 false,
208 ) {
209 if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
1b1a35ee 210 return Ok(res.map_id(|_| panic!("unexpected id")));
f035d41b
XL
211 }
212 }
213 if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
1b1a35ee 214 return Ok(res.map_id(|_| panic!("unexpected id")));
f035d41b 215 }
1b1a35ee
XL
216 debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
217 if let Ok((_, res)) =
218 resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id)
219 {
220 // don't resolve builtins like `#[derive]`
221 if let Res::Def(..) = res {
222 let res = res.map_id(|_| panic!("unexpected node_id"));
223 return Ok(res);
f035d41b 224 }
f035d41b 225 }
1b1a35ee
XL
226 Err(ResolutionFailure::NotResolved {
227 module_id,
228 partial_res: None,
229 unresolved: path_str.into(),
230 })
f035d41b
XL
231 })
232 }
1b1a35ee 233
532ac7d7
XL
234 /// Resolves a string as a path within a particular namespace. Also returns an optional
235 /// URL fragment in the case of variants and methods.
1b1a35ee 236 fn resolve<'path>(
60c5eb7d 237 &self,
1b1a35ee 238 path_str: &'path str,
60c5eb7d
XL
239 ns: Namespace,
240 current_item: &Option<String>,
1b1a35ee 241 module_id: DefId,
60c5eb7d 242 extra_fragment: &Option<String>,
1b1a35ee 243 ) -> Result<(Res, Option<String>), ErrorKind<'path>> {
b7449926
XL
244 let cx = self.cx;
245
1b1a35ee
XL
246 let result = cx.enter_resolver(|resolver| {
247 resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
248 });
249 debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
250 let result = match result {
251 Ok((_, Res::Err)) => Err(()),
252 x => x,
253 };
b7449926 254
1b1a35ee
XL
255 if let Ok((_, res)) = result {
256 let res = res.map_id(|_| panic!("unexpected node_id"));
257 // In case this is a trait item, skip the
258 // early return and try looking for the trait.
259 let value = match res {
260 Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => true,
261 Res::Def(DefKind::AssocTy, _) => false,
262 Res::Def(DefKind::Variant, _) => {
263 return handle_variant(cx, res, extra_fragment);
264 }
265 // Not a trait item; just return what we found.
266 Res::PrimTy(ty) => {
267 if extra_fragment.is_some() {
268 return Err(ErrorKind::AnchorFailure(
269 AnchorFailure::RustdocAnchorConflict(res),
270 ));
f035d41b 271 }
1b1a35ee
XL
272 return Ok((res, Some(ty.name_str().to_owned())));
273 }
274 Res::Def(DefKind::Mod, _) => {
275 return Ok((res, extra_fragment.clone()));
b7449926 276 }
1b1a35ee
XL
277 _ => {
278 return Ok((res, extra_fragment.clone()));
b7449926 279 }
1b1a35ee
XL
280 };
281
282 if value != (ns == ValueNS) {
283 return Err(ResolutionFailure::WrongNamespace(res, ns).into());
284 }
285 // FIXME: why is this necessary?
286 } else if let Some((path, prim)) = is_primitive(path_str, ns) {
287 if extra_fragment.is_some() {
288 return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(prim)));
b7449926 289 }
1b1a35ee
XL
290 return Ok((prim, Some(path.to_owned())));
291 }
b7449926 292
1b1a35ee
XL
293 // Try looking for methods and associated items.
294 let mut split = path_str.rsplitn(2, "::");
295 // this can be an `unwrap()` because we ensure the link is never empty
296 let (item_str, item_name) = split.next().map(|i| (i, Symbol::intern(i))).unwrap();
297 let path_root = split
298 .next()
299 .map(|f| {
300 if f == "self" || f == "Self" {
301 if let Some(name) = current_item.as_ref() {
302 return name.clone();
3dfed10e
XL
303 }
304 }
1b1a35ee
XL
305 f.to_owned()
306 })
307 // If there's no `::`, it's not an associated item.
308 // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
309 .ok_or_else(|| {
310 debug!("found no `::`, assumming {} was correctly not in scope", item_name);
311 ResolutionFailure::NotResolved {
312 module_id,
313 partial_res: None,
314 unresolved: item_str.into(),
315 }
316 })?;
317
318 if let Some((path, prim)) = is_primitive(&path_root, TypeNS) {
319 let impls =
320 primitive_impl(cx, &path).ok_or_else(|| ResolutionFailure::NotResolved {
321 module_id,
322 partial_res: Some(prim),
323 unresolved: item_str.into(),
324 })?;
325 for &impl_ in impls {
326 let link = cx
327 .tcx
328 .associated_items(impl_)
329 .find_by_name_and_namespace(
330 cx.tcx,
331 Ident::with_dummy_span(item_name),
332 ns,
333 impl_,
334 )
335 .map(|item| match item.kind {
336 ty::AssocKind::Fn => "method",
337 ty::AssocKind::Const => "associatedconstant",
338 ty::AssocKind::Type => "associatedtype",
339 })
340 .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_str))));
341 if let Some(link) = link {
342 return Ok(link);
343 }
9fa01778 344 }
1b1a35ee
XL
345 debug!(
346 "returning primitive error for {}::{} in {} namespace",
347 path,
348 item_name,
349 ns.descr()
350 );
351 return Err(ResolutionFailure::NotResolved {
352 module_id,
353 partial_res: Some(prim),
354 unresolved: item_str.into(),
355 }
356 .into());
357 }
b7449926 358
1b1a35ee
XL
359 let ty_res = cx
360 .enter_resolver(|resolver| {
361 // only types can have associated items
362 resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id)
363 })
364 .map(|(_, res)| res);
365 let ty_res = match ty_res {
366 Err(()) | Ok(Res::Err) => {
3dfed10e
XL
367 return if ns == Namespace::ValueNS {
368 self.variant_field(path_str, current_item, module_id)
369 } else {
1b1a35ee
XL
370 Err(ResolutionFailure::NotResolved {
371 module_id,
372 partial_res: None,
373 unresolved: path_root.into(),
374 }
375 .into())
3dfed10e 376 };
416331ca 377 }
1b1a35ee
XL
378 Ok(res) => res,
379 };
380 let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
381 let res = match ty_res {
382 Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias, did) => {
383 debug!("looking for associated item named {} for item {:?}", item_name, did);
384 // Checks if item_name belongs to `impl SomeItem`
385 let assoc_item = cx
386 .tcx
387 .inherent_impls(did)
388 .iter()
389 .flat_map(|&imp| {
390 cx.tcx.associated_items(imp).find_by_name_and_namespace(
391 cx.tcx,
392 Ident::with_dummy_span(item_name),
393 ns,
394 imp,
395 )
396 })
397 .map(|item| (item.kind, item.def_id))
398 // There should only ever be one associated item that matches from any inherent impl
399 .next()
400 // Check if item_name belongs to `impl SomeTrait for SomeItem`
401 // This gives precedence to `impl SomeItem`:
402 // Although having both would be ambiguous, use impl version for compat. sake.
403 // To handle that properly resolve() would have to support
404 // something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
405 .or_else(|| {
406 let kind =
407 resolve_associated_trait_item(did, module_id, item_name, ns, &self.cx);
408 debug!("got associated item kind {:?}", kind);
409 kind
410 });
411
412 if let Some((kind, id)) = assoc_item {
413 let out = match kind {
414 ty::AssocKind::Fn => "method",
415 ty::AssocKind::Const => "associatedconstant",
416 ty::AssocKind::Type => "associatedtype",
417 };
418 Some(if extra_fragment.is_some() {
419 Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res)))
420 } else {
421 // HACK(jynelson): `clean` expects the type, not the associated item.
422 // but the disambiguator logic expects the associated item.
423 // Store the kind in a side channel so that only the disambiguator logic looks at it.
424 self.kind_side_channel.set(Some((kind.as_def_kind(), id)));
425 Ok((ty_res, Some(format!("{}.{}", out, item_str))))
426 })
427 } else if ns == Namespace::ValueNS {
428 debug!("looking for variants or fields named {} for {:?}", item_name, did);
429 match cx.tcx.type_of(did).kind() {
430 ty::Adt(def, _) => {
431 let field = if def.is_enum() {
432 def.all_fields().find(|item| item.ident.name == item_name)
60c5eb7d 433 } else {
1b1a35ee
XL
434 def.non_enum_variant()
435 .fields
436 .iter()
437 .find(|item| item.ident.name == item_name)
438 };
439 field.map(|item| {
440 if extra_fragment.is_some() {
441 let res = Res::Def(
442 if def.is_enum() {
443 DefKind::Variant
60c5eb7d 444 } else {
1b1a35ee
XL
445 DefKind::Field
446 },
447 item.did,
448 );
449 Err(ErrorKind::AnchorFailure(
450 AnchorFailure::RustdocAnchorConflict(res),
451 ))
452 } else {
453 Ok((
454 ty_res,
455 Some(format!(
456 "{}.{}",
457 if def.is_enum() { "variant" } else { "structfield" },
458 item.ident
459 )),
460 ))
461 }
462 })
463 }
464 _ => None,
465 }
466 } else {
467 // We already know this isn't in ValueNS, so no need to check variant_field
468 return Err(ResolutionFailure::NotResolved {
469 module_id,
470 partial_res: Some(ty_res),
471 unresolved: item_str.into(),
472 }
473 .into());
474 }
475 }
476 Res::Def(DefKind::Trait, did) => cx
477 .tcx
478 .associated_items(did)
479 .find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, did)
480 .map(|item| {
481 let kind = match item.kind {
482 ty::AssocKind::Const => "associatedconstant",
483 ty::AssocKind::Type => "associatedtype",
484 ty::AssocKind::Fn => {
485 if item.defaultness.has_value() {
486 "method"
487 } else {
488 "tymethod"
b7449926 489 }
b7449926 490 }
1b1a35ee
XL
491 };
492
493 if extra_fragment.is_some() {
494 Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res)))
3dfed10e 495 } else {
1b1a35ee
XL
496 let res = Res::Def(item.kind.as_def_kind(), item.def_id);
497 Ok((res, Some(format!("{}.{}", kind, item_str))))
b7449926 498 }
1b1a35ee
XL
499 }),
500 _ => None,
501 };
502 res.unwrap_or_else(|| {
503 if ns == Namespace::ValueNS {
504 self.variant_field(path_str, current_item, module_id)
505 } else {
506 Err(ResolutionFailure::NotResolved {
507 module_id,
508 partial_res: Some(ty_res),
509 unresolved: item_str.into(),
b7449926 510 }
1b1a35ee
XL
511 .into())
512 }
513 })
514 }
b7449926 515
1b1a35ee
XL
516 /// Used for reporting better errors.
517 ///
518 /// Returns whether the link resolved 'fully' in another namespace.
519 /// 'fully' here means that all parts of the link resolved, not just some path segments.
520 /// This returns the `Res` even if it was erroneous for some reason
521 /// (such as having invalid URL fragments or being in the wrong namespace).
522 fn check_full_res(
523 &self,
524 ns: Namespace,
525 path_str: &str,
526 module_id: DefId,
527 current_item: &Option<String>,
528 extra_fragment: &Option<String>,
529 ) -> Option<Res> {
530 let check_full_res_inner = |this: &Self, result: Result<Res, ErrorKind<'_>>| {
531 let res = match result {
532 Ok(res) => Some(res),
533 Err(ErrorKind::Resolve(box kind)) => kind.full_res(),
534 Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))) => {
535 Some(res)
b7449926 536 }
1b1a35ee
XL
537 Err(ErrorKind::AnchorFailure(AnchorFailure::MultipleAnchors)) => None,
538 };
539 this.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res)
540 };
541 // cannot be used for macro namespace
542 let check_full_res = |this: &Self, ns| {
543 let result = this.resolve(path_str, ns, current_item, module_id, extra_fragment);
544 check_full_res_inner(this, result.map(|(res, _)| res))
545 };
546 let check_full_res_macro = |this: &Self| {
547 let result = this.macro_resolve(path_str, module_id);
548 check_full_res_inner(this, result.map_err(ErrorKind::from))
549 };
550 match ns {
551 Namespace::MacroNS => check_full_res_macro(self),
552 Namespace::TypeNS | Namespace::ValueNS => check_full_res(self, ns),
b7449926
XL
553 }
554 }
555}
556
3dfed10e
XL
557fn resolve_associated_trait_item(
558 did: DefId,
559 module: DefId,
560 item_name: Symbol,
561 ns: Namespace,
562 cx: &DocContext<'_>,
1b1a35ee 563) -> Option<(ty::AssocKind, DefId)> {
3dfed10e
XL
564 let ty = cx.tcx.type_of(did);
565 // First consider automatic impls: `impl From<T> for T`
566 let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did);
567 let mut candidates: Vec<_> = implicit_impls
568 .flat_map(|impl_outer| {
569 match impl_outer.inner {
570 ImplItem(impl_) => {
571 debug!("considering auto or blanket impl for trait {:?}", impl_.trait_);
572 // Give precedence to methods that were overridden
573 if !impl_.provided_trait_methods.contains(&*item_name.as_str()) {
574 let mut items = impl_.items.into_iter().filter_map(|assoc| {
575 if assoc.name.as_deref() != Some(&*item_name.as_str()) {
576 return None;
577 }
578 let kind = assoc
579 .inner
580 .as_assoc_kind()
581 .expect("inner items for a trait should be associated items");
582 if kind.namespace() != ns {
583 return None;
584 }
585
586 trace!("considering associated item {:?}", assoc.inner);
587 // We have a slight issue: normal methods come from `clean` types,
588 // but provided methods come directly from `tcx`.
589 // Fortunately, we don't need the whole method, we just need to know
590 // what kind of associated item it is.
1b1a35ee 591 Some((kind, assoc.def_id))
3dfed10e
XL
592 });
593 let assoc = items.next();
594 debug_assert_eq!(items.count(), 0);
595 assoc
596 } else {
597 // These are provided methods or default types:
598 // ```
599 // trait T {
600 // type A = usize;
601 // fn has_default() -> A { 0 }
602 // }
603 // ```
604 let trait_ = impl_.trait_.unwrap().def_id().unwrap();
605 cx.tcx
606 .associated_items(trait_)
607 .find_by_name_and_namespace(
608 cx.tcx,
609 Ident::with_dummy_span(item_name),
610 ns,
611 trait_,
612 )
1b1a35ee 613 .map(|assoc| (assoc.kind, assoc.def_id))
3dfed10e
XL
614 }
615 }
616 _ => panic!("get_impls returned something that wasn't an impl"),
617 }
618 })
619 .collect();
620
621 // Next consider explicit impls: `impl MyTrait for MyType`
622 // Give precedence to inherent impls.
623 if candidates.is_empty() {
624 let traits = traits_implemented_by(cx, did, module);
625 debug!("considering traits {:?}", traits);
626 candidates.extend(traits.iter().filter_map(|&trait_| {
627 cx.tcx
628 .associated_items(trait_)
629 .find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, trait_)
1b1a35ee 630 .map(|assoc| (assoc.kind, assoc.def_id))
3dfed10e
XL
631 }));
632 }
633 // FIXME: warn about ambiguity
634 debug!("the candidates were {:?}", candidates);
1b1a35ee 635 candidates.pop()
3dfed10e
XL
636}
637
638/// Given a type, return all traits in scope in `module` implemented by that type.
639///
640/// NOTE: this cannot be a query because more traits could be available when more crates are compiled!
641/// So it is not stable to serialize cross-crate.
642fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> FxHashSet<DefId> {
643 let mut cache = cx.module_trait_cache.borrow_mut();
644 let in_scope_traits = cache.entry(module).or_insert_with(|| {
645 cx.enter_resolver(|resolver| {
646 resolver.traits_in_scope(module).into_iter().map(|candidate| candidate.def_id).collect()
647 })
648 });
649
650 let ty = cx.tcx.type_of(type_);
651 let iter = in_scope_traits.iter().flat_map(|&trait_| {
652 trace!("considering explicit impl for trait {:?}", trait_);
653 let mut saw_impl = false;
654 // Look at each trait implementation to see if it's an impl for `did`
655 cx.tcx.for_each_relevant_impl(trait_, ty, |impl_| {
656 // FIXME: this is inefficient, find a way to short-circuit for_each_* so this doesn't take as long
657 if saw_impl {
658 return;
659 }
660
661 let trait_ref = cx.tcx.impl_trait_ref(impl_).expect("this is not an inherent impl");
662 // Check if these are the same type.
663 let impl_type = trait_ref.self_ty();
1b1a35ee 664 trace!(
3dfed10e 665 "comparing type {} with kind {:?} against type {:?}",
1b1a35ee
XL
666 impl_type,
667 impl_type.kind(),
668 type_
3dfed10e
XL
669 );
670 // Fast path: if this is a primitive simple `==` will work
671 saw_impl = impl_type == ty
1b1a35ee 672 || match impl_type.kind() {
3dfed10e
XL
673 // Check if these are the same def_id
674 ty::Adt(def, _) => {
675 debug!("adt def_id: {:?}", def.did);
676 def.did == type_
677 }
1b1a35ee 678 ty::Foreign(def_id) => *def_id == type_,
3dfed10e
XL
679 _ => false,
680 };
681 });
682 if saw_impl { Some(trait_) } else { None }
683 });
684 iter.collect()
685}
686
f035d41b
XL
687/// Check for resolve collisions between a trait and its derive
688///
689/// These are common and we should just resolve to the trait in that case
1b1a35ee 690fn is_derive_trait_collision<T>(ns: &PerNS<Result<(Res, T), ResolutionFailure<'_>>>) -> bool {
f035d41b 691 if let PerNS {
1b1a35ee
XL
692 type_ns: Ok((Res::Def(DefKind::Trait, _), _)),
693 macro_ns: Ok((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)),
f035d41b
XL
694 ..
695 } = *ns
696 {
697 true
698 } else {
699 false
700 }
701}
702
532ac7d7 703impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
b7449926 704 fn fold_item(&mut self, mut item: Item) -> Option<Item> {
3dfed10e 705 use rustc_middle::ty::DefIdTree;
b7449926 706
3dfed10e
XL
707 let parent_node = if item.is_fake() {
708 // FIXME: is this correct?
709 None
1b1a35ee
XL
710 // If we're documenting the crate root itself, it has no parent. Use the root instead.
711 } else if item.def_id.is_top_level_module() {
712 Some(item.def_id)
3dfed10e
XL
713 } else {
714 let mut current = item.def_id;
715 // The immediate parent might not always be a module.
716 // Find the first parent which is.
717 loop {
718 if let Some(parent) = self.cx.tcx.parent(current) {
719 if self.cx.tcx.def_kind(parent) == DefKind::Mod {
720 break Some(parent);
721 }
722 current = parent;
723 } else {
1b1a35ee
XL
724 debug!(
725 "{:?} has no parent (kind={:?}, original was {:?})",
726 current,
727 self.cx.tcx.def_kind(current),
728 item.def_id
729 );
3dfed10e
XL
730 break None;
731 }
0bf4aa26 732 }
3dfed10e 733 };
0bf4aa26
XL
734
735 if parent_node.is_some() {
3dfed10e 736 trace!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
0bf4aa26
XL
737 }
738
b7449926
XL
739 let current_item = match item.inner {
740 ModuleItem(..) => {
741 if item.attrs.inner_docs {
3dfed10e 742 if item.def_id.is_top_level_module() { item.name.clone() } else { None }
b7449926 743 } else {
3dfed10e
XL
744 match parent_node.or(self.mod_ids.last().copied()) {
745 Some(parent) if !parent.is_top_level_module() => {
0731742a 746 // FIXME: can we pull the parent module's name from elsewhere?
3dfed10e 747 Some(self.cx.tcx.item_name(parent).to_string())
b7449926
XL
748 }
749 _ => None,
750 }
751 }
752 }
753 ImplItem(Impl { ref for_, .. }) => {
754 for_.def_id().map(|did| self.cx.tcx.item_name(did).to_string())
755 }
0731742a 756 // we don't display docs on `extern crate` items anyway, so don't process them.
3dfed10e
XL
757 ExternCrateItem(..) => {
758 debug!("ignoring extern crate item {:?}", item.def_id);
759 return self.fold_item_recur(item);
760 }
b7449926
XL
761 ImportItem(Import::Simple(ref name, ..)) => Some(name.clone()),
762 MacroItem(..) => None,
763 _ => item.name.clone(),
764 };
765
766 if item.is_mod() && item.attrs.inner_docs {
3dfed10e 767 self.mod_ids.push(item.def_id);
b7449926
XL
768 }
769
b7449926 770 let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);
3dfed10e 771 trace!("got documentation '{}'", dox);
0bf4aa26 772
f9f354fc
XL
773 // find item's parent to resolve `Self` in item's docs below
774 let parent_name = self.cx.as_local_hir_id(item.def_id).and_then(|item_hir| {
775 let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir);
776 let item_parent = self.cx.tcx.hir().find(parent_hir);
777 match item_parent {
778 Some(hir::Node::Item(hir::Item {
779 kind:
780 hir::ItemKind::Impl {
781 self_ty:
782 hir::Ty {
783 kind:
784 hir::TyKind::Path(hir::QPath::Resolved(
785 _,
786 hir::Path { segments, .. },
787 )),
788 ..
789 },
790 ..
791 },
792 ..
f035d41b 793 })) => segments.first().map(|seg| seg.ident.to_string()),
f9f354fc
XL
794 Some(hir::Node::Item(hir::Item {
795 ident, kind: hir::ItemKind::Enum(..), ..
796 }))
797 | Some(hir::Node::Item(hir::Item {
798 ident, kind: hir::ItemKind::Struct(..), ..
799 }))
800 | Some(hir::Node::Item(hir::Item {
801 ident, kind: hir::ItemKind::Union(..), ..
802 }))
803 | Some(hir::Node::Item(hir::Item {
804 ident, kind: hir::ItemKind::Trait(..), ..
805 })) => Some(ident.to_string()),
806 _ => None,
807 }
808 });
809
b7449926 810 for (ori_link, link_range) in markdown_links(&dox) {
1b1a35ee
XL
811 self.resolve_link(
812 &mut item,
813 &dox,
814 &current_item,
815 parent_node,
816 &parent_name,
817 ori_link,
818 link_range,
819 );
820 }
3dfed10e 821
1b1a35ee
XL
822 if item.is_mod() && !item.attrs.inner_docs {
823 self.mod_ids.push(item.def_id);
824 }
825
826 if item.is_mod() {
827 let ret = self.fold_item_recur(item);
828
829 self.mod_ids.pop();
830
831 ret
832 } else {
833 self.fold_item_recur(item)
834 }
835 }
836}
837
838impl LinkCollector<'_, '_> {
839 fn resolve_link(
840 &self,
841 item: &mut Item,
842 dox: &str,
843 current_item: &Option<String>,
844 parent_node: Option<DefId>,
845 parent_name: &Option<String>,
846 ori_link: String,
847 link_range: Option<Range<usize>>,
848 ) {
849 trace!("considering link '{}'", ori_link);
850
851 // Bail early for real links.
852 if ori_link.contains('/') {
853 return;
854 }
855
856 // [] is mostly likely not supposed to be a link
857 if ori_link.is_empty() {
858 return;
859 }
860
861 let cx = self.cx;
862 let link = ori_link.replace("`", "");
863 let parts = link.split('#').collect::<Vec<_>>();
864 let (link, extra_fragment) = if parts.len() > 2 {
865 anchor_failure(cx, &item, &link, dox, link_range, AnchorFailure::MultipleAnchors);
866 return;
867 } else if parts.len() == 2 {
868 if parts[0].trim().is_empty() {
869 // This is an anchor to an element of the current page, nothing to do in here!
870 return;
b7449926 871 }
1b1a35ee
XL
872 (parts[0], Some(parts[1].to_owned()))
873 } else {
874 (parts[0], None)
875 };
876 let resolved_self;
877 let link_text;
878 let mut path_str;
879 let disambiguator;
880 let (mut res, mut fragment) = {
881 path_str = if let Ok((d, path)) = Disambiguator::from_str(&link) {
882 disambiguator = Some(d);
883 path
884 } else {
885 disambiguator = None;
886 &link
887 }
888 .trim();
532ac7d7 889
1b1a35ee
XL
890 if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ch == ':' || ch == '_')) {
891 return;
532ac7d7
XL
892 }
893
1b1a35ee
XL
894 // We stripped `()` and `!` when parsing the disambiguator.
895 // Add them back to be displayed, but not prefix disambiguators.
896 link_text = disambiguator
897 .map(|d| d.display_for(path_str))
898 .unwrap_or_else(|| path_str.to_owned());
899
900 // In order to correctly resolve intra-doc-links we need to
901 // pick a base AST node to work from. If the documentation for
902 // this module came from an inner comment (//!) then we anchor
903 // our name resolution *inside* the module. If, on the other
904 // hand it was an outer comment (///) then we anchor the name
905 // resolution in the parent module on the basis that the names
906 // used are more likely to be intended to be parent names. For
907 // this, we set base_node to None for inner comments since
908 // we've already pushed this node onto the resolution stack but
909 // for outer comments we explicitly try and resolve against the
910 // parent_node first.
911 let base_node = if item.is_mod() && item.attrs.inner_docs {
912 self.mod_ids.last().copied()
60c5eb7d 913 } else {
1b1a35ee 914 parent_node
60c5eb7d 915 };
b7449926 916
1b1a35ee
XL
917 let mut module_id = if let Some(id) = base_node {
918 id
919 } else {
920 debug!("attempting to resolve item without parent module: {}", path_str);
921 let err_kind = ResolutionFailure::NoParentItem.into();
922 resolution_failure(
923 self,
924 &item,
925 path_str,
926 disambiguator,
927 dox,
928 link_range,
929 smallvec![err_kind],
930 );
931 return;
932 };
933
934 // replace `Self` with suitable item's parent name
935 if path_str.starts_with("Self::") {
936 if let Some(ref name) = parent_name {
937 resolved_self = format!("{}::{}", name, &path_str[6..]);
938 path_str = &resolved_self;
b7449926 939 }
1b1a35ee
XL
940 } else if path_str.starts_with("crate::") {
941 use rustc_span::def_id::CRATE_DEF_INDEX;
942
943 // HACK(jynelson): rustc_resolve thinks that `crate` is the crate currently being documented.
944 // But rustdoc wants it to mean the crate this item was originally present in.
945 // To work around this, remove it and resolve relative to the crate root instead.
946 // HACK(jynelson)(2): If we just strip `crate::` then suddenly primitives become ambiguous
947 // (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root.
948 resolved_self = format!("self::{}", &path_str["crate::".len()..]);
949 path_str = &resolved_self;
950 module_id = DefId { krate: item.def_id.krate, index: CRATE_DEF_INDEX };
951 }
b7449926 952
1b1a35ee
XL
953 match self.resolve_with_disambiguator(
954 disambiguator,
955 item,
956 dox,
957 path_str,
958 current_item,
959 module_id,
960 extra_fragment,
961 &ori_link,
962 link_range.clone(),
963 ) {
964 Some(x) => x,
965 None => return,
966 }
967 };
e74abb32 968
1b1a35ee
XL
969 // Check for a primitive which might conflict with a module
970 // Report the ambiguity and require that the user specify which one they meant.
971 // FIXME: could there ever be a primitive not in the type namespace?
972 if matches!(
973 disambiguator,
974 None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive)
975 ) && !matches!(res, Res::PrimTy(_))
976 {
977 if let Some((path, prim)) = is_primitive(path_str, TypeNS) {
978 // `prim@char`
979 if matches!(disambiguator, Some(Disambiguator::Primitive)) {
980 if fragment.is_some() {
981 anchor_failure(
982 cx,
983 &item,
984 path_str,
985 dox,
986 link_range,
987 AnchorFailure::RustdocAnchorConflict(prim),
988 );
989 return;
f9f354fc 990 }
1b1a35ee
XL
991 res = prim;
992 fragment = Some(path.to_owned());
993 } else {
994 // `[char]` when a `char` module is in scope
995 let candidates = vec![res, prim];
996 ambiguity_error(cx, &item, path_str, dox, link_range, candidates);
997 return;
f9f354fc 998 }
1b1a35ee
XL
999 }
1000 }
f9f354fc 1001
1b1a35ee
XL
1002 let report_mismatch = |specified: Disambiguator, resolved: Disambiguator| {
1003 // The resolved item did not match the disambiguator; give a better error than 'not found'
1004 let msg = format!("incompatible link kind for `{}`", path_str);
1005 let callback = |diag: &mut DiagnosticBuilder<'_>, sp| {
1006 let note = format!(
1007 "this link resolved to {} {}, which is not {} {}",
1008 resolved.article(),
1009 resolved.descr(),
1010 specified.article(),
1011 specified.descr()
1012 );
1013 diag.note(&note);
1014 suggest_disambiguator(resolved, diag, path_str, dox, sp, &link_range);
1015 };
1016 report_diagnostic(cx, BROKEN_INTRA_DOC_LINKS, &msg, &item, dox, &link_range, callback);
1017 };
1018 if let Res::PrimTy(..) = res {
1019 match disambiguator {
1020 Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {
1021 item.attrs.links.push(ItemLink {
1022 link: ori_link,
1023 link_text,
1024 did: None,
1025 fragment,
1026 });
1027 }
1028 Some(other) => {
1029 report_mismatch(other, Disambiguator::Primitive);
1030 return;
1031 }
1032 }
1033 } else {
1034 debug!("intra-doc link to {} resolved to {:?}", path_str, res);
1035
1036 // Disallow e.g. linking to enums with `struct@`
1037 if let Res::Def(kind, _) = res {
1038 debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
1039 match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) {
1040 | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
1041 // NOTE: this allows 'method' to mean both normal functions and associated functions
1042 // This can't cause ambiguity because both are in the same namespace.
1043 | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn)))
1044 // These are namespaces; allow anything in the namespace to match
1045 | (_, Some(Disambiguator::Namespace(_)))
1046 // If no disambiguator given, allow anything
1047 | (_, None)
1048 // All of these are valid, so do nothing
1049 => {}
1050 (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {}
1051 (_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => {
1052 report_mismatch(specified, Disambiguator::Kind(kind));
1053 return;
b7449926 1054 }
1b1a35ee
XL
1055 }
1056 }
532ac7d7 1057
1b1a35ee
XL
1058 // item can be non-local e.g. when using #[doc(primitive = "pointer")]
1059 if let Some((src_id, dst_id)) = res
1060 .opt_def_id()
1061 .and_then(|def_id| def_id.as_local())
1062 .and_then(|dst_id| item.def_id.as_local().map(|src_id| (src_id, dst_id)))
1063 {
1064 use rustc_hir::def_id::LOCAL_CRATE;
532ac7d7 1065
1b1a35ee
XL
1066 let hir_src = self.cx.tcx.hir().local_def_id_to_hir_id(src_id);
1067 let hir_dst = self.cx.tcx.hir().local_def_id_to_hir_id(dst_id);
f035d41b 1068
1b1a35ee
XL
1069 if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src)
1070 && !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst)
1071 {
1072 privacy_error(cx, &item, &path_str, dox, link_range);
1073 }
1074 }
1075 let id = register_res(cx, res);
1076 item.attrs.links.push(ItemLink { link: ori_link, link_text, did: Some(id), fragment });
1077 }
1078 }
1079
1080 fn resolve_with_disambiguator(
1081 &self,
1082 disambiguator: Option<Disambiguator>,
1083 item: &mut Item,
1084 dox: &str,
1085 path_str: &str,
1086 current_item: &Option<String>,
1087 base_node: DefId,
1088 extra_fragment: Option<String>,
1089 ori_link: &str,
1090 link_range: Option<Range<usize>>,
1091 ) -> Option<(Res, Option<String>)> {
1092 match disambiguator.map(Disambiguator::ns) {
1093 Some(ns @ (ValueNS | TypeNS)) => {
1094 match self.resolve(path_str, ns, &current_item, base_node, &extra_fragment) {
1095 Ok(res) => Some(res),
1096 Err(ErrorKind::Resolve(box mut kind)) => {
1097 // We only looked in one namespace. Try to give a better error if possible.
1098 if kind.full_res().is_none() {
1099 let other_ns = if ns == ValueNS { TypeNS } else { ValueNS };
1100 // FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`
1101 // See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach
1102 for &new_ns in &[other_ns, MacroNS] {
1103 if let Some(res) = self.check_full_res(
1104 new_ns,
1105 path_str,
1106 base_node,
1107 &current_item,
1108 &extra_fragment,
1109 ) {
1110 kind = ResolutionFailure::WrongNamespace(res, ns);
1111 break;
1112 }
f035d41b 1113 }
532ac7d7 1114 }
1b1a35ee
XL
1115 resolution_failure(
1116 self,
1117 &item,
1118 path_str,
1119 disambiguator,
1120 dox,
1121 link_range,
1122 smallvec![kind],
1123 );
1124 // This could just be a normal link or a broken link
1125 // we could potentially check if something is
1126 // "intra-doc-link-like" and warn in that case.
1127 return None;
b7449926 1128 }
1b1a35ee
XL
1129 Err(ErrorKind::AnchorFailure(msg)) => {
1130 anchor_failure(self.cx, &item, &ori_link, dox, link_range, msg);
1131 return None;
b7449926
XL
1132 }
1133 }
1b1a35ee
XL
1134 }
1135 None => {
1136 // Try everything!
1137 let mut candidates = PerNS {
1138 macro_ns: self
1139 .macro_resolve(path_str, base_node)
1140 .map(|res| (res, extra_fragment.clone())),
1141 type_ns: match self.resolve(
1142 path_str,
1143 TypeNS,
1144 &current_item,
1145 base_node,
1146 &extra_fragment,
1147 ) {
1148 Ok(res) => {
1149 debug!("got res in TypeNS: {:?}", res);
1150 Ok(res)
3dfed10e 1151 }
1b1a35ee
XL
1152 Err(ErrorKind::AnchorFailure(msg)) => {
1153 anchor_failure(self.cx, &item, ori_link, dox, link_range, msg);
1154 return None;
1155 }
1156 Err(ErrorKind::Resolve(box kind)) => Err(kind),
1157 },
1158 value_ns: match self.resolve(
1159 path_str,
1160 ValueNS,
1161 &current_item,
1162 base_node,
1163 &extra_fragment,
1164 ) {
1165 Ok(res) => Ok(res),
1166 Err(ErrorKind::AnchorFailure(msg)) => {
1167 anchor_failure(self.cx, &item, ori_link, dox, link_range, msg);
1168 return None;
1169 }
1170 Err(ErrorKind::Resolve(box kind)) => Err(kind),
3dfed10e 1171 }
1b1a35ee
XL
1172 .and_then(|(res, fragment)| {
1173 // Constructors are picked up in the type namespace.
1174 match res {
1175 Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => {
1176 Err(ResolutionFailure::WrongNamespace(res, TypeNS))
1177 }
1178 _ => match (fragment, extra_fragment) {
1179 (Some(fragment), Some(_)) => {
1180 // Shouldn't happen but who knows?
1181 Ok((res, Some(fragment)))
1182 }
1183 (fragment, None) | (None, fragment) => Ok((res, fragment)),
1184 },
1185 }
1186 }),
1187 };
3dfed10e 1188
1b1a35ee
XL
1189 let len = candidates.iter().filter(|res| res.is_ok()).count();
1190
1191 if len == 0 {
1192 resolution_failure(
1193 self,
1194 &item,
1195 path_str,
1196 disambiguator,
1197 dox,
1198 link_range,
1199 candidates.into_iter().filter_map(|res| res.err()).collect(),
3dfed10e 1200 );
1b1a35ee
XL
1201 // this could just be a normal link
1202 return None;
3dfed10e 1203 }
1b1a35ee
XL
1204
1205 if len == 1 {
1206 Some(candidates.into_iter().filter_map(|res| res.ok()).next().unwrap())
1207 } else if len == 2 && is_derive_trait_collision(&candidates) {
1208 Some(candidates.type_ns.unwrap())
1209 } else {
1210 if is_derive_trait_collision(&candidates) {
1211 candidates.macro_ns = Err(ResolutionFailure::Dummy);
3dfed10e 1212 }
1b1a35ee
XL
1213 // If we're reporting an ambiguity, don't mention the namespaces that failed
1214 let candidates = candidates.map(|candidate| candidate.ok().map(|(res, _)| res));
1215 ambiguity_error(
1216 self.cx,
1217 &item,
1218 path_str,
1219 dox,
1220 link_range,
1221 candidates.present_items().collect(),
1222 );
1223 return None;
3dfed10e 1224 }
1b1a35ee
XL
1225 }
1226 Some(MacroNS) => {
1227 match self.macro_resolve(path_str, base_node) {
1228 Ok(res) => Some((res, extra_fragment)),
1229 Err(mut kind) => {
1230 // `macro_resolve` only looks in the macro namespace. Try to give a better error if possible.
1231 for &ns in &[TypeNS, ValueNS] {
1232 if let Some(res) = self.check_full_res(
1233 ns,
1234 path_str,
1235 base_node,
1236 &current_item,
1237 &extra_fragment,
1238 ) {
1239 kind = ResolutionFailure::WrongNamespace(res, MacroNS);
1240 break;
1241 }
1242 }
1243 resolution_failure(
1244 self,
1245 &item,
1246 path_str,
1247 disambiguator,
1248 dox,
1249 link_range,
1250 smallvec![kind],
1251 );
1252 return None;
f035d41b
XL
1253 }
1254 }
b7449926
XL
1255 }
1256 }
48663c56 1257 }
b7449926
XL
1258}
1259
3dfed10e
XL
1260#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1261enum Disambiguator {
1262 Primitive,
1263 Kind(DefKind),
1264 Namespace(Namespace),
1265}
1266
1267impl Disambiguator {
1b1a35ee
XL
1268 /// The text that should be displayed when the path is rendered as HTML.
1269 ///
1270 /// NOTE: `path` is not the original link given by the user, but a name suitable for passing to `resolve`.
1271 fn display_for(&self, path: &str) -> String {
1272 match self {
1273 // FIXME: this will have different output if the user had `m!()` originally.
1274 Self::Kind(DefKind::Macro(MacroKind::Bang)) => format!("{}!", path),
1275 Self::Kind(DefKind::Fn) => format!("{}()", path),
1276 _ => path.to_owned(),
1277 }
1278 }
1279
3dfed10e
XL
1280 /// (disambiguator, path_str)
1281 fn from_str(link: &str) -> Result<(Self, &str), ()> {
1282 use Disambiguator::{Kind, Namespace as NS, Primitive};
1283
1284 let find_suffix = || {
1285 let suffixes = [
1286 ("!()", DefKind::Macro(MacroKind::Bang)),
1287 ("()", DefKind::Fn),
1288 ("!", DefKind::Macro(MacroKind::Bang)),
1289 ];
1290 for &(suffix, kind) in &suffixes {
1291 if link.ends_with(suffix) {
1292 return Ok((Kind(kind), link.trim_end_matches(suffix)));
1293 }
1294 }
1295 Err(())
1296 };
1297
1298 if let Some(idx) = link.find('@') {
1299 let (prefix, rest) = link.split_at(idx);
1300 let d = match prefix {
1301 "struct" => Kind(DefKind::Struct),
1302 "enum" => Kind(DefKind::Enum),
1303 "trait" => Kind(DefKind::Trait),
1304 "union" => Kind(DefKind::Union),
1305 "module" | "mod" => Kind(DefKind::Mod),
1306 "const" | "constant" => Kind(DefKind::Const),
1307 "static" => Kind(DefKind::Static),
1308 "function" | "fn" | "method" => Kind(DefKind::Fn),
1309 "derive" => Kind(DefKind::Macro(MacroKind::Derive)),
1310 "type" => NS(Namespace::TypeNS),
1311 "value" => NS(Namespace::ValueNS),
1312 "macro" => NS(Namespace::MacroNS),
1313 "prim" | "primitive" => Primitive,
1314 _ => return find_suffix(),
1315 };
1316 Ok((d, &rest[1..]))
1317 } else {
1318 find_suffix()
1319 }
1320 }
1321
1b1a35ee
XL
1322 /// WARNING: panics on `Res::Err`
1323 fn from_res(res: Res) -> Self {
1324 match res {
1325 Res::Def(kind, _) => Disambiguator::Kind(kind),
1326 Res::PrimTy(_) => Disambiguator::Primitive,
1327 _ => Disambiguator::Namespace(res.ns().expect("can't call `from_res` on Res::err")),
1328 }
1329 }
1330
1331 fn suggestion(self) -> Suggestion {
3dfed10e 1332 let kind = match self {
1b1a35ee 1333 Disambiguator::Primitive => return Suggestion::Prefix("prim"),
3dfed10e
XL
1334 Disambiguator::Kind(kind) => kind,
1335 Disambiguator::Namespace(_) => panic!("display_for cannot be used on namespaces"),
1336 };
1337 if kind == DefKind::Macro(MacroKind::Bang) {
1b1a35ee 1338 return Suggestion::Macro;
3dfed10e 1339 } else if kind == DefKind::Fn || kind == DefKind::AssocFn {
1b1a35ee 1340 return Suggestion::Function;
3dfed10e 1341 }
1b1a35ee 1342
3dfed10e
XL
1343 let prefix = match kind {
1344 DefKind::Struct => "struct",
1345 DefKind::Enum => "enum",
1346 DefKind::Trait => "trait",
1347 DefKind::Union => "union",
1348 DefKind::Mod => "mod",
1349 DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst => {
1350 "const"
1351 }
1352 DefKind::Static => "static",
1353 DefKind::Macro(MacroKind::Derive) => "derive",
1354 // Now handle things that don't have a specific disambiguator
1355 _ => match kind
1356 .ns()
1357 .expect("tried to calculate a disambiguator for a def without a namespace?")
1358 {
1359 Namespace::TypeNS => "type",
1360 Namespace::ValueNS => "value",
1361 Namespace::MacroNS => "macro",
1362 },
1363 };
1b1a35ee
XL
1364
1365 Suggestion::Prefix(prefix)
3dfed10e
XL
1366 }
1367
1368 fn ns(self) -> Namespace {
1369 match self {
1370 Self::Namespace(n) => n,
1371 Self::Kind(k) => {
1372 k.ns().expect("only DefKinds with a valid namespace can be disambiguators")
1373 }
1374 Self::Primitive => TypeNS,
1375 }
1376 }
1377
1378 fn article(self) -> &'static str {
1379 match self {
1380 Self::Namespace(_) => panic!("article() doesn't make sense for namespaces"),
1381 Self::Kind(k) => k.article(),
1382 Self::Primitive => "a",
1383 }
1384 }
1385
1386 fn descr(self) -> &'static str {
1387 match self {
1388 Self::Namespace(n) => n.descr(),
1389 // HACK(jynelson): by looking at the source I saw the DefId we pass
1390 // for `expected.descr()` doesn't matter, since it's not a crate
1391 Self::Kind(k) => k.descr(DefId::local(hir::def_id::DefIndex::from_usize(0))),
1392 Self::Primitive => "builtin type",
1393 }
1394 }
1395}
1396
1b1a35ee
XL
1397enum Suggestion {
1398 Prefix(&'static str),
1399 Function,
1400 Macro,
1401}
1402
1403impl Suggestion {
1404 fn descr(&self) -> Cow<'static, str> {
1405 match self {
1406 Self::Prefix(x) => format!("prefix with `{}@`", x).into(),
1407 Self::Function => "add parentheses".into(),
1408 Self::Macro => "add an exclamation mark".into(),
1409 }
1410 }
1411
1412 fn as_help(&self, path_str: &str) -> String {
1413 // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
1414 match self {
1415 Self::Prefix(prefix) => format!("{}@{}", prefix, path_str),
1416 Self::Function => format!("{}()", path_str),
1417 Self::Macro => format!("{}!", path_str),
1418 }
1419 }
1420}
1421
3dfed10e
XL
1422/// Reports a diagnostic for an intra-doc link.
1423///
1424/// If no link range is provided, or the source span of the link cannot be determined, the span of
1425/// the entire documentation block is used for the lint. If a range is provided but the span
1426/// calculation fails, a note is added to the diagnostic pointing to the link in the markdown.
1427///
1428/// The `decorate` callback is invoked in all cases to allow further customization of the
1429/// diagnostic before emission. If the span of the link was able to be determined, the second
1430/// parameter of the callback will contain it, and the primary span of the diagnostic will be set
1431/// to it.
1432fn report_diagnostic(
532ac7d7 1433 cx: &DocContext<'_>,
1b1a35ee 1434 lint: &'static Lint,
3dfed10e 1435 msg: &str,
48663c56 1436 item: &Item,
b7449926 1437 dox: &str,
1b1a35ee 1438 link_range: &Option<Range<usize>>,
3dfed10e 1439 decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
b7449926 1440) {
48663c56
XL
1441 let hir_id = match cx.as_local_hir_id(item.def_id) {
1442 Some(hir_id) => hir_id,
1443 None => {
1444 // If non-local, no need to check anything.
3dfed10e 1445 info!("ignoring warning from parent crate: {}", msg);
48663c56
XL
1446 return;
1447 }
1448 };
3dfed10e 1449
48663c56 1450 let attrs = &item.attrs;
416331ca 1451 let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
b7449926 1452
1b1a35ee 1453 cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| {
3dfed10e
XL
1454 let mut diag = lint.build(msg);
1455
1456 let span = link_range
1457 .as_ref()
1458 .and_then(|range| super::source_span_for_markdown_range(cx, dox, range, attrs));
1459
1460 if let Some(link_range) = link_range {
1461 if let Some(sp) = span {
1462 diag.set_span(sp);
1463 } else {
1464 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
1465 // ^ ~~~~
1466 // | link_range
1467 // last_new_line_offset
1468 let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
1469 let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
1470
1471 // Print the line containing the `link_range` and manually mark it with '^'s.
1472 diag.note(&format!(
1473 "the link appears in this line:\n\n{line}\n\
1b1a35ee 1474 {indicator: <before$}{indicator:^<found$}",
3dfed10e
XL
1475 line = line,
1476 indicator = "",
1477 before = link_range.start - last_new_line_offset,
1478 found = link_range.len(),
1479 ));
74b04a01 1480 }
3dfed10e
XL
1481 }
1482
1483 decorate(&mut diag, span);
1484
1485 diag.emit();
1486 });
b7449926
XL
1487}
1488
60c5eb7d 1489fn resolution_failure(
1b1a35ee 1490 collector: &LinkCollector<'_, '_>,
60c5eb7d
XL
1491 item: &Item,
1492 path_str: &str,
1b1a35ee 1493 disambiguator: Option<Disambiguator>,
60c5eb7d
XL
1494 dox: &str,
1495 link_range: Option<Range<usize>>,
1b1a35ee 1496 kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
60c5eb7d 1497) {
3dfed10e 1498 report_diagnostic(
1b1a35ee
XL
1499 collector.cx,
1500 BROKEN_INTRA_DOC_LINKS,
3dfed10e 1501 &format!("unresolved link to `{}`", path_str),
60c5eb7d 1502 item,
60c5eb7d 1503 dox,
1b1a35ee 1504 &link_range,
3dfed10e 1505 |diag, sp| {
1b1a35ee
XL
1506 let item = |res: Res| {
1507 format!(
1508 "the {} `{}`",
1509 res.descr(),
1510 collector.cx.tcx.item_name(res.def_id()).to_string()
1511 )
1512 };
1513 let assoc_item_not_allowed = |res: Res| {
1514 let def_id = res.def_id();
1515 let name = collector.cx.tcx.item_name(def_id);
1516 format!(
1517 "`{}` is {} {}, not a module or type, and cannot have associated items",
1518 name,
1519 res.article(),
1520 res.descr()
1521 )
1522 };
1523 // ignore duplicates
1524 let mut variants_seen = SmallVec::<[_; 3]>::new();
1525 for mut failure in kinds {
1526 let variant = std::mem::discriminant(&failure);
1527 if variants_seen.contains(&variant) {
1528 continue;
1529 }
1530 variants_seen.push(variant);
1531
1532 if let ResolutionFailure::NotResolved { module_id, partial_res, unresolved } =
1533 &mut failure
1534 {
1535 use DefKind::*;
1536
1537 let module_id = *module_id;
1538 // FIXME(jynelson): this might conflict with my `Self` fix in #76467
1539 // FIXME: maybe use itertools `collect_tuple` instead?
1540 fn split(path: &str) -> Option<(&str, &str)> {
1541 let mut splitter = path.rsplitn(2, "::");
1542 splitter.next().and_then(|right| splitter.next().map(|left| (left, right)))
1543 }
3dfed10e 1544
1b1a35ee
XL
1545 // Check if _any_ parent of the path gets resolved.
1546 // If so, report it and say the first which failed; if not, say the first path segment didn't resolve.
1547 let mut name = path_str;
1548 'outer: loop {
1549 let (start, end) = if let Some(x) = split(name) {
1550 x
1551 } else {
1552 // avoid bug that marked [Quux::Z] as missing Z, not Quux
1553 if partial_res.is_none() {
1554 *unresolved = name.into();
1555 }
1556 break;
1557 };
1558 name = start;
1559 for &ns in &[TypeNS, ValueNS, MacroNS] {
1560 if let Some(res) =
1561 collector.check_full_res(ns, &start, module_id, &None, &None)
1562 {
1563 debug!("found partial_res={:?}", res);
1564 *partial_res = Some(res);
1565 *unresolved = end.into();
1566 break 'outer;
1567 }
1568 }
1569 *unresolved = end.into();
1570 }
1571
1572 let last_found_module = match *partial_res {
1573 Some(Res::Def(DefKind::Mod, id)) => Some(id),
1574 None => Some(module_id),
1575 _ => None,
1576 };
1577 // See if this was a module: `[path]` or `[std::io::nope]`
1578 if let Some(module) = last_found_module {
1579 let module_name = collector.cx.tcx.item_name(module);
1580 let note = format!(
1581 "the module `{}` contains no item named `{}`",
1582 module_name, unresolved
1583 );
1584 if let Some(span) = sp {
1585 diag.span_label(span, &note);
1586 } else {
1587 diag.note(&note);
1588 }
1589 // If the link has `::` in it, assume it was meant to be an intra-doc link.
1590 // Otherwise, the `[]` might be unrelated.
1591 // FIXME: don't show this for autolinks (`<>`), `()` style links, or reference links
1592 if !path_str.contains("::") {
1593 diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
1594 }
1595 continue;
1596 }
1597
1598 // Otherwise, it must be an associated item or variant
1599 let res = partial_res.expect("None case was handled by `last_found_module`");
1600 let diagnostic_name;
1601 let (kind, name) = match res {
1602 Res::Def(kind, def_id) => {
1603 diagnostic_name = collector.cx.tcx.item_name(def_id).as_str();
1604 (Some(kind), &*diagnostic_name)
1605 }
1606 Res::PrimTy(ty) => (None, ty.name_str()),
1607 _ => unreachable!("only ADTs and primitives are in scope at module level"),
1608 };
1609 let path_description = if let Some(kind) = kind {
1610 match kind {
1611 Mod | ForeignMod => "inner item",
1612 Struct => "field or associated item",
1613 Enum | Union => "variant or associated item",
1614 Variant
1615 | Field
1616 | Closure
1617 | Generator
1618 | AssocTy
1619 | AssocConst
1620 | AssocFn
1621 | Fn
1622 | Macro(_)
1623 | Const
1624 | ConstParam
1625 | ExternCrate
1626 | Use
1627 | LifetimeParam
1628 | Ctor(_, _)
1629 | AnonConst => {
1630 let note = assoc_item_not_allowed(res);
1631 if let Some(span) = sp {
1632 diag.span_label(span, &note);
1633 } else {
1634 diag.note(&note);
1635 }
1636 return;
1637 }
1638 Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
1639 | Static => "associated item",
1640 Impl | GlobalAsm => unreachable!("not a path"),
1641 }
1642 } else {
1643 "associated item"
1644 };
1645 let note = format!(
1646 "the {} `{}` has no {} named `{}`",
1647 res.descr(),
1648 name,
1649 disambiguator.map_or(path_description, |d| d.descr()),
1650 unresolved,
1651 );
1652 if let Some(span) = sp {
1653 diag.span_label(span, &note);
1654 } else {
1655 diag.note(&note);
1656 }
1657
1658 continue;
1659 }
1660 let note = match failure {
1661 ResolutionFailure::NotResolved { .. } => unreachable!("handled above"),
1662 ResolutionFailure::Dummy => continue,
1663 ResolutionFailure::WrongNamespace(res, expected_ns) => {
1664 if let Res::Def(kind, _) = res {
1665 let disambiguator = Disambiguator::Kind(kind);
1666 suggest_disambiguator(
1667 disambiguator,
1668 diag,
1669 path_str,
1670 dox,
1671 sp,
1672 &link_range,
1673 )
1674 }
1675
1676 format!(
1677 "this link resolves to {}, which is not in the {} namespace",
1678 item(res),
1679 expected_ns.descr()
1680 )
1681 }
1682 ResolutionFailure::NoParentItem => {
1683 diag.level = rustc_errors::Level::Bug;
1684 "all intra doc links should have a parent item".to_owned()
1685 }
1686 };
1687 if let Some(span) = sp {
1688 diag.span_label(span, &note);
1689 } else {
1690 diag.note(&note);
1691 }
1692 }
3dfed10e 1693 },
60c5eb7d
XL
1694 );
1695}
1696
1697fn anchor_failure(
1698 cx: &DocContext<'_>,
1699 item: &Item,
1700 path_str: &str,
1701 dox: &str,
1702 link_range: Option<Range<usize>>,
3dfed10e 1703 failure: AnchorFailure,
60c5eb7d 1704) {
3dfed10e
XL
1705 let msg = match failure {
1706 AnchorFailure::MultipleAnchors => format!("`{}` contains multiple anchors", path_str),
1b1a35ee
XL
1707 AnchorFailure::RustdocAnchorConflict(res) => format!(
1708 "`{}` contains an anchor, but links to {kind}s are already anchored",
1709 path_str,
1710 kind = res.descr(),
1711 ),
3dfed10e
XL
1712 };
1713
1b1a35ee 1714 report_diagnostic(cx, BROKEN_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| {
3dfed10e
XL
1715 if let Some(sp) = sp {
1716 diag.span_label(sp, "contains invalid anchor");
1717 }
1718 });
60c5eb7d
XL
1719}
1720
532ac7d7
XL
1721fn ambiguity_error(
1722 cx: &DocContext<'_>,
48663c56 1723 item: &Item,
532ac7d7
XL
1724 path_str: &str,
1725 dox: &str,
1726 link_range: Option<Range<usize>>,
1b1a35ee 1727 candidates: Vec<Res>,
532ac7d7 1728) {
3dfed10e
XL
1729 let mut msg = format!("`{}` is ", path_str);
1730
1731 match candidates.as_slice() {
1b1a35ee 1732 [first_def, second_def] => {
3dfed10e
XL
1733 msg += &format!(
1734 "both {} {} and {} {}",
1735 first_def.article(),
1736 first_def.descr(),
1737 second_def.article(),
1738 second_def.descr(),
1739 );
48663c56 1740 }
3dfed10e
XL
1741 _ => {
1742 let mut candidates = candidates.iter().peekable();
1b1a35ee 1743 while let Some(res) = candidates.next() {
3dfed10e
XL
1744 if candidates.peek().is_some() {
1745 msg += &format!("{} {}, ", res.article(), res.descr());
1746 } else {
1747 msg += &format!("and {} {}", res.article(), res.descr());
74b04a01
XL
1748 }
1749 }
3dfed10e
XL
1750 }
1751 }
74b04a01 1752
1b1a35ee 1753 report_diagnostic(cx, BROKEN_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| {
3dfed10e
XL
1754 if let Some(sp) = sp {
1755 diag.span_label(sp, "ambiguous link");
1b1a35ee
XL
1756 } else {
1757 diag.note("ambiguous link");
1758 }
74b04a01 1759
1b1a35ee
XL
1760 for res in candidates {
1761 let disambiguator = Disambiguator::from_res(res);
1762 suggest_disambiguator(disambiguator, diag, path_str, dox, sp, &link_range);
1763 }
1764 });
1765}
532ac7d7 1766
1b1a35ee
XL
1767fn suggest_disambiguator(
1768 disambiguator: Disambiguator,
1769 diag: &mut DiagnosticBuilder<'_>,
1770 path_str: &str,
1771 dox: &str,
1772 sp: Option<rustc_span::Span>,
1773 link_range: &Option<Range<usize>>,
1774) {
1775 let suggestion = disambiguator.suggestion();
1776 let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr());
3dfed10e 1777
1b1a35ee
XL
1778 if let Some(sp) = sp {
1779 let link_range = link_range.as_ref().expect("must have a link range if we have a span");
1780 let msg = if dox.bytes().nth(link_range.start) == Some(b'`') {
1781 format!("`{}`", suggestion.as_help(path_str))
1782 } else {
1783 suggestion.as_help(path_str)
1784 };
3dfed10e 1785
1b1a35ee
XL
1786 diag.span_suggestion(sp, &help, msg, Applicability::MaybeIncorrect);
1787 } else {
1788 diag.help(&format!("{}: {}", help, suggestion.as_help(path_str)));
1789 }
3dfed10e
XL
1790}
1791
1792fn privacy_error(
1793 cx: &DocContext<'_>,
1794 item: &Item,
1795 path_str: &str,
1796 dox: &str,
1797 link_range: Option<Range<usize>>,
1798) {
1799 let item_name = item.name.as_deref().unwrap_or("<unknown>");
1800 let msg =
1801 format!("public documentation for `{}` links to private item `{}`", item_name, path_str);
1802
1b1a35ee 1803 report_diagnostic(cx, PRIVATE_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| {
3dfed10e
XL
1804 if let Some(sp) = sp {
1805 diag.span_label(sp, "this item is private");
1806 }
1807
1808 let note_msg = if cx.render_options.document_private {
1809 "this link resolves only because you passed `--document-private-items`, but will break without"
1810 } else {
1811 "this link will resolve properly if you pass `--document-private-items`"
1812 };
1813 diag.note(note_msg);
1814 });
b7449926
XL
1815}
1816
48663c56 1817/// Given an enum variant's res, return the res of its enum and the associated fragment.
60c5eb7d
XL
1818fn handle_variant(
1819 cx: &DocContext<'_>,
1820 res: Res,
1821 extra_fragment: &Option<String>,
1b1a35ee 1822) -> Result<(Res, Option<String>), ErrorKind<'static>> {
ba9703b0 1823 use rustc_middle::ty::DefIdTree;
b7449926 1824
60c5eb7d 1825 if extra_fragment.is_some() {
1b1a35ee 1826 return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res)));
60c5eb7d 1827 }
48663c56 1828 let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) {
b7449926
XL
1829 parent
1830 } else {
1b1a35ee 1831 return Err(ResolutionFailure::NoParentItem.into());
b7449926 1832 };
48663c56
XL
1833 let parent_def = Res::Def(DefKind::Enum, parent);
1834 let variant = cx.tcx.expect_variant_res(res);
f035d41b 1835 Ok((parent_def, Some(format!("variant.{}", variant.ident.name))))
b7449926
XL
1836}
1837
48663c56 1838const PRIMITIVES: &[(&str, Res)] = &[
3dfed10e
XL
1839 ("u8", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U8))),
1840 ("u16", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U16))),
1841 ("u32", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U32))),
1842 ("u64", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U64))),
1843 ("u128", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U128))),
1844 ("usize", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::Usize))),
1845 ("i8", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I8))),
1846 ("i16", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I16))),
1847 ("i32", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I32))),
1848 ("i64", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I64))),
1849 ("i128", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I128))),
1850 ("isize", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::Isize))),
1851 ("f32", Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F32))),
1852 ("f64", Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F64))),
60c5eb7d
XL
1853 ("str", Res::PrimTy(hir::PrimTy::Str)),
1854 ("bool", Res::PrimTy(hir::PrimTy::Bool)),
3dfed10e
XL
1855 ("true", Res::PrimTy(hir::PrimTy::Bool)),
1856 ("false", Res::PrimTy(hir::PrimTy::Bool)),
60c5eb7d 1857 ("char", Res::PrimTy(hir::PrimTy::Char)),
b7449926
XL
1858];
1859
3dfed10e
XL
1860fn is_primitive(path_str: &str, ns: Namespace) -> Option<(&'static str, Res)> {
1861 if ns == TypeNS {
1862 PRIMITIVES
1863 .iter()
1864 .filter(|x| x.0 == path_str)
1865 .copied()
1866 .map(|x| if x.0 == "true" || x.0 == "false" { ("bool", x.1) } else { x })
1867 .next()
1868 } else {
1869 None
1870 }
b7449926 1871}
9fa01778 1872
3dfed10e
XL
1873fn primitive_impl(cx: &DocContext<'_>, path_str: &str) -> Option<&'static SmallVec<[DefId; 4]>> {
1874 Some(PrimitiveType::from_symbol(Symbol::intern(path_str))?.impls(cx.tcx))
9fa01778 1875}