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