2 use rustc_errors::Applicability;
3 use rustc_expand::base::SyntaxExtensionKind;
4 use rustc_feature::UnstableFeatures;
11 use rustc_hir::def_id::{DefId, LocalDefId};
13 use rustc_resolve::ParentScope;
14 use rustc_session::lint;
15 use rustc_span::hygiene::MacroKind;
16 use rustc_span::symbol::Ident;
17 use rustc_span::symbol::Symbol;
18 use rustc_span::DUMMY_SP;
23 use crate::core::DocContext;
24 use crate::fold::DocFolder;
25 use crate::html::markdown::markdown_links;
26 use crate::passes::{look_for_tests, Pass};
28 use super::span_of_attrs;
30 pub const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
31 name: "collect-intra-doc-links",
32 run: collect_intra_doc_links,
33 description: "reads a crate's documentation to resolve intra-doc-links",
36 pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate {
37 if !UnstableFeatures::from_environment().is_nightly_build() {
40 let mut coll = LinkCollector::new(cx);
42 coll.fold_crate(krate)
48 AnchorFailure(&'static str),
51 struct LinkCollector<'a, 'tcx> {
52 cx: &'a DocContext<'tcx>,
53 mod_ids: Vec<hir::HirId>,
56 impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
57 fn new(cx: &'a DocContext<'tcx>) -> Self {
58 LinkCollector { cx, mod_ids: Vec::new() }
64 current_item: &Option<String>,
65 module_id: LocalDefId,
66 ) -> Result<(Res, Option<String>), ErrorKind> {
69 let mut split = path_str.rsplitn(3, "::");
70 let variant_field_name =
71 split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
73 split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
77 if f == "self" || f == "Self" {
78 if let Some(name) = current_item.as_ref() {
84 .ok_or(ErrorKind::ResolutionFailure)?;
86 .enter_resolver(|resolver| {
87 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
89 .map_err(|_| ErrorKind::ResolutionFailure)?;
90 if let Res::Err = ty_res {
91 return Err(ErrorKind::ResolutionFailure);
93 let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
95 Res::Def(DefKind::Enum, did) => {
100 .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order())
101 .any(|item| item.ident.name == variant_name)
103 return Err(ErrorKind::ResolutionFailure);
105 match cx.tcx.type_of(did).kind {
106 ty::Adt(def, _) if def.is_enum() => {
107 if def.all_fields().any(|item| item.ident.name == variant_field_name) {
111 "variant.{}.field.{}",
112 variant_name, variant_field_name
116 Err(ErrorKind::ResolutionFailure)
119 _ => Err(ErrorKind::ResolutionFailure),
122 _ => Err(ErrorKind::ResolutionFailure),
126 /// Resolves a string as a macro.
127 fn macro_resolve(&self, path_str: &str, parent_id: Option<hir::HirId>) -> Option<Res> {
129 let path = ast::Path::from_ident(Ident::from_str(path_str));
130 cx.enter_resolver(|resolver| {
131 if let Ok((Some(ext), res)) = resolver.resolve_macro_path(
134 &ParentScope::module(resolver.graph_root()),
138 if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
139 return Some(res.map_id(|_| panic!("unexpected id")));
142 if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
143 return Some(res.map_id(|_| panic!("unexpected id")));
145 if let Some(module_id) = parent_id.or(self.mod_ids.last().cloned()) {
146 let module_id = cx.tcx.hir().local_def_id(module_id);
147 if let Ok((_, res)) =
148 resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id)
150 // don't resolve builtins like `#[derive]`
151 if let Res::Def(..) = res {
152 let res = res.map_id(|_| panic!("unexpected node_id"));
157 debug!("attempting to resolve item without parent module: {}", path_str);
162 /// Resolves a string as a path within a particular namespace. Also returns an optional
163 /// URL fragment in the case of variants and methods.
168 current_item: &Option<String>,
169 parent_id: Option<hir::HirId>,
170 extra_fragment: &Option<String>,
171 item_opt: Option<&Item>,
172 ) -> Result<(Res, Option<String>), ErrorKind> {
175 // In case we're in a module, try to resolve the relative path.
176 if let Some(module_id) = parent_id.or(self.mod_ids.last().cloned()) {
177 let module_id = cx.tcx.hir().local_def_id(module_id);
178 let result = cx.enter_resolver(|resolver| {
179 resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
181 let result = match result {
182 Ok((_, Res::Err)) => Err(ErrorKind::ResolutionFailure),
183 _ => result.map_err(|_| ErrorKind::ResolutionFailure),
186 if let Ok((_, res)) = result {
187 let res = res.map_id(|_| panic!("unexpected node_id"));
188 // In case this is a trait item, skip the
189 // early return and try looking for the trait.
190 let value = match res {
191 Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => true,
192 Res::Def(DefKind::AssocTy, _) => false,
193 Res::Def(DefKind::Variant, _) => {
194 return handle_variant(cx, res, extra_fragment);
196 // Not a trait item; just return what we found.
198 if extra_fragment.is_some() {
199 return Err(ErrorKind::AnchorFailure(
200 "primitive types cannot be followed by anchors",
203 return Ok((res, Some(path_str.to_owned())));
205 _ => return Ok((res, extra_fragment.clone())),
208 if value != (ns == ValueNS) {
209 return Err(ErrorKind::ResolutionFailure);
211 } else if let Some(prim) = is_primitive(path_str, ns) {
212 if extra_fragment.is_some() {
213 return Err(ErrorKind::AnchorFailure(
214 "primitive types cannot be followed by anchors",
217 return Ok((prim, Some(path_str.to_owned())));
219 // If resolution failed, it may still be a method
220 // because methods are not handled by the resolver
221 // If so, bail when we're not looking for a value.
223 return Err(ErrorKind::ResolutionFailure);
227 // Try looking for methods and associated items.
228 let mut split = path_str.rsplitn(2, "::");
230 split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
234 if f == "self" || f == "Self" {
235 if let Some(name) = current_item.as_ref() {
241 .ok_or(ErrorKind::ResolutionFailure)?;
243 if let Some(prim) = is_primitive(&path, TypeNS) {
244 let did = primitive_impl(cx, &path).ok_or(ErrorKind::ResolutionFailure)?;
247 .associated_items(did)
248 .filter_by_name_unhygienic(item_name)
250 .and_then(|item| match item.kind {
251 ty::AssocKind::Fn => Some("method"),
254 .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name))))
255 .ok_or(ErrorKind::ResolutionFailure);
259 .enter_resolver(|resolver| {
260 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
262 .map_err(|_| ErrorKind::ResolutionFailure)?;
263 if let Res::Err = ty_res {
264 return self.variant_field(path_str, current_item, module_id);
266 let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
269 DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias,
272 // Checks if item_name belongs to `impl SomeItem`
277 .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order())
278 .find(|item| item.ident.name == item_name);
279 let trait_item = item_opt
280 .and_then(|item| self.cx.as_local_hir_id(item.def_id))
281 .and_then(|item_hir| {
282 // Checks if item_name belongs to `impl SomeTrait for SomeItem`
283 let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir);
284 let item_parent = self.cx.tcx.hir().find(parent_hir);
286 Some(hir::Node::Item(hir::Item {
287 kind: hir::ItemKind::Impl { of_trait: Some(_), self_ty, .. },
291 .associated_item_def_ids(self_ty.hir_id.owner)
294 let associated_item = cx.tcx.associated_item(*child);
297 .find(|child| child.ident.name == item_name),
301 let item = match (impl_item, trait_item) {
302 (Some(from_impl), Some(_)) => {
303 // Although it's ambiguous, return impl version for compat. sake.
304 // To handle that properly resolve() would have to support
306 // [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
309 (None, Some(from_trait)) => Some(from_trait),
310 (Some(from_impl), None) => Some(from_impl),
314 if let Some(item) = item {
315 let out = match item.kind {
316 ty::AssocKind::Fn if ns == ValueNS => "method",
317 ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
318 ty::AssocKind::Type if ns == ValueNS => "associatedtype",
319 _ => return self.variant_field(path_str, current_item, module_id),
321 if extra_fragment.is_some() {
322 Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Fn {
323 "methods cannot be followed by anchors"
325 "associated constants cannot be followed by anchors"
328 Ok((ty_res, Some(format!("{}.{}", out, item_name))))
331 match cx.tcx.type_of(did).kind {
333 if let Some(item) = if def.is_enum() {
334 def.all_fields().find(|item| item.ident.name == item_name)
336 def.non_enum_variant()
339 .find(|item| item.ident.name == item_name)
341 if extra_fragment.is_some() {
342 Err(ErrorKind::AnchorFailure(if def.is_enum() {
343 "enum variants cannot be followed by anchors"
345 "struct fields cannot be followed by anchors"
362 self.variant_field(path_str, current_item, module_id)
365 _ => self.variant_field(path_str, current_item, module_id),
369 Res::Def(DefKind::Trait, did) => {
372 .associated_item_def_ids(did)
374 .map(|item| cx.tcx.associated_item(*item))
375 .find(|item| item.ident.name == item_name);
376 if let Some(item) = item {
379 ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
380 ty::AssocKind::Type if ns == TypeNS => "associatedtype",
381 ty::AssocKind::Fn if ns == ValueNS => {
382 if item.defaultness.has_value() { "method" } else { "tymethod" }
384 _ => return self.variant_field(path_str, current_item, module_id),
387 if extra_fragment.is_some() {
388 Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Const {
389 "associated constants cannot be followed by anchors"
390 } else if item.kind == ty::AssocKind::Type {
391 "associated types cannot be followed by anchors"
393 "methods cannot be followed by anchors"
396 Ok((ty_res, Some(format!("{}.{}", kind, item_name))))
399 self.variant_field(path_str, current_item, module_id)
402 _ => self.variant_field(path_str, current_item, module_id),
405 debug!("attempting to resolve item without parent module: {}", path_str);
406 Err(ErrorKind::ResolutionFailure)
411 /// Check for resolve collisions between a trait and its derive
413 /// These are common and we should just resolve to the trait in that case
414 fn is_derive_trait_collision<T>(ns: &PerNS<Option<(Res, T)>>) -> bool {
416 type_ns: Some((Res::Def(DefKind::Trait, _), _)),
417 macro_ns: Some((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)),
427 impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
428 fn fold_item(&mut self, mut item: Item) -> Option<Item> {
429 let item_hir_id = if item.is_mod() {
430 if let Some(def_id) = item.def_id.as_local() {
431 Some(self.cx.tcx.hir().as_local_hir_id(def_id))
433 debug!("attempting to fold on a non-local item: {:?}", item);
434 return self.fold_item_recur(item);
440 // FIXME: get the resolver to work with non-local resolve scopes.
441 let parent_node = self.cx.as_local_hir_id(item.def_id).and_then(|hir_id| {
442 // FIXME: this fails hard for impls in non-module scope, but is necessary for the
443 // current `resolve()` implementation.
444 match self.cx.as_local_hir_id(self.cx.tcx.parent_module(hir_id).to_def_id()).unwrap() {
445 id if id != hir_id => Some(id),
450 if parent_node.is_some() {
451 debug!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
454 let current_item = match item.inner {
456 if item.attrs.inner_docs {
457 if item_hir_id.unwrap() != hir::CRATE_HIR_ID { item.name.clone() } else { None }
459 match parent_node.or(self.mod_ids.last().cloned()) {
460 Some(parent) if parent != hir::CRATE_HIR_ID => {
461 // FIXME: can we pull the parent module's name from elsewhere?
462 Some(self.cx.tcx.hir().name(parent).to_string())
468 ImplItem(Impl { ref for_, .. }) => {
469 for_.def_id().map(|did| self.cx.tcx.item_name(did).to_string())
471 // we don't display docs on `extern crate` items anyway, so don't process them.
472 ExternCrateItem(..) => return self.fold_item_recur(item),
473 ImportItem(Import::Simple(ref name, ..)) => Some(name.clone()),
474 MacroItem(..) => None,
475 _ => item.name.clone(),
478 if item.is_mod() && item.attrs.inner_docs {
479 self.mod_ids.push(item_hir_id.unwrap());
483 let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);
485 look_for_tests(&cx, &dox, &item, true);
487 // find item's parent to resolve `Self` in item's docs below
488 let parent_name = self.cx.as_local_hir_id(item.def_id).and_then(|item_hir| {
489 let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir);
490 let item_parent = self.cx.tcx.hir().find(parent_hir);
492 Some(hir::Node::Item(hir::Item {
494 hir::ItemKind::Impl {
498 hir::TyKind::Path(hir::QPath::Resolved(
500 hir::Path { segments, .. },
507 })) => segments.first().and_then(|seg| Some(seg.ident.to_string())),
508 Some(hir::Node::Item(hir::Item {
509 ident, kind: hir::ItemKind::Enum(..), ..
511 | Some(hir::Node::Item(hir::Item {
512 ident, kind: hir::ItemKind::Struct(..), ..
514 | Some(hir::Node::Item(hir::Item {
515 ident, kind: hir::ItemKind::Union(..), ..
517 | Some(hir::Node::Item(hir::Item {
518 ident, kind: hir::ItemKind::Trait(..), ..
519 })) => Some(ident.to_string()),
524 for (ori_link, link_range) in markdown_links(&dox) {
525 // Bail early for real links.
526 if ori_link.contains('/') {
530 // [] is mostly likely not supposed to be a link
531 if ori_link.is_empty() {
535 let link = ori_link.replace("`", "");
536 let parts = link.split('#').collect::<Vec<_>>();
537 let (link, extra_fragment) = if parts.len() > 2 {
544 "has an issue with the link anchor.",
545 "only one `#` is allowed in a link",
549 } else if parts.len() == 2 {
550 if parts[0].trim().is_empty() {
551 // This is an anchor to an element of the current page, nothing to do in here!
554 (parts[0].to_owned(), Some(parts[1].to_owned()))
556 (parts[0].to_owned(), None)
558 let (res, fragment) = {
560 let mut path_str = if let Some(prefix) =
561 ["struct@", "enum@", "type@", "trait@", "union@"]
563 .find(|p| link.starts_with(**p))
566 link.trim_start_matches(prefix)
567 } else if let Some(prefix) = [
578 .find(|p| link.starts_with(**p))
580 kind = Some(ValueNS);
581 link.trim_start_matches(prefix)
582 } else if link.ends_with("()") {
583 kind = Some(ValueNS);
584 link.trim_end_matches("()")
585 } else if link.starts_with("macro@") {
586 kind = Some(MacroNS);
587 link.trim_start_matches("macro@")
588 } else if link.starts_with("derive@") {
589 kind = Some(MacroNS);
590 link.trim_start_matches("derive@")
591 } else if link.ends_with('!') {
592 kind = Some(MacroNS);
593 link.trim_end_matches('!')
599 if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ch == ':' || ch == '_')) {
603 // In order to correctly resolve intra-doc-links we need to
604 // pick a base AST node to work from. If the documentation for
605 // this module came from an inner comment (//!) then we anchor
606 // our name resolution *inside* the module. If, on the other
607 // hand it was an outer comment (///) then we anchor the name
608 // resolution in the parent module on the basis that the names
609 // used are more likely to be intended to be parent names. For
610 // this, we set base_node to None for inner comments since
611 // we've already pushed this node onto the resolution stack but
612 // for outer comments we explicitly try and resolve against the
613 // parent_node first.
615 if item.is_mod() && item.attrs.inner_docs { None } else { parent_node };
618 // replace `Self` with suitable item's parent name
619 if path_str.starts_with("Self::") {
620 if let Some(ref name) = parent_name {
621 resolved_self = format!("{}::{}", name, &path_str[6..]);
622 path_str = &resolved_self;
627 Some(ns @ ValueNS) => {
637 Err(ErrorKind::ResolutionFailure) => {
638 resolution_failure(cx, &item, path_str, &dox, link_range);
639 // This could just be a normal link or a broken link
640 // we could potentially check if something is
641 // "intra-doc-link-like" and warn in that case.
644 Err(ErrorKind::AnchorFailure(msg)) => {
645 anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
650 Some(ns @ TypeNS) => {
660 Err(ErrorKind::ResolutionFailure) => {
661 resolution_failure(cx, &item, path_str, &dox, link_range);
662 // This could just be a normal link.
665 Err(ErrorKind::AnchorFailure(msg)) => {
666 anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
673 let mut candidates = PerNS {
675 .macro_resolve(path_str, base_node)
676 .map(|res| (res, extra_fragment.clone())),
677 type_ns: match self.resolve(
685 Err(ErrorKind::AnchorFailure(msg)) => {
686 anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
691 value_ns: match self.resolve(
699 Err(ErrorKind::AnchorFailure(msg)) => {
700 anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
705 .and_then(|(res, fragment)| {
706 // Constructors are picked up in the type namespace.
708 Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None,
709 _ => match (fragment, extra_fragment) {
710 (Some(fragment), Some(_)) => {
711 // Shouldn't happen but who knows?
712 Some((res, Some(fragment)))
714 (fragment, None) | (None, fragment) => {
715 Some((res, fragment))
722 if candidates.is_empty() {
723 resolution_failure(cx, &item, path_str, &dox, link_range);
724 // this could just be a normal link
728 let len = candidates.clone().present_items().count();
731 candidates.present_items().next().unwrap()
732 } else if len == 2 && is_derive_trait_collision(&candidates) {
733 candidates.type_ns.unwrap()
735 if is_derive_trait_collision(&candidates) {
736 candidates.macro_ns = None;
744 candidates.map(|candidate| candidate.map(|(res, _)| res)),
750 if let Some(res) = self.macro_resolve(path_str, base_node) {
751 (res, extra_fragment)
753 resolution_failure(cx, &item, path_str, &dox, link_range);
760 if let Res::PrimTy(_) = res {
761 item.attrs.links.push((ori_link, None, fragment));
763 let id = register_res(cx, res);
764 item.attrs.links.push((ori_link, Some(id), fragment));
768 if item.is_mod() && !item.attrs.inner_docs {
769 self.mod_ids.push(item_hir_id.unwrap());
773 let ret = self.fold_item_recur(item);
779 self.fold_item_recur(item)
783 // FIXME: if we can resolve intra-doc links from other crates, we can use the stock
784 // `fold_crate`, but until then we should avoid scanning `krate.external_traits` since those
785 // will never resolve properly
786 fn fold_crate(&mut self, mut c: Crate) -> Crate {
787 c.module = c.module.take().and_then(|module| self.fold_item(module));
798 link_range: Option<Range<usize>>,
801 help_msg: Option<&str>,
803 let hir_id = match cx.as_local_hir_id(item.def_id) {
804 Some(hir_id) => hir_id,
806 // If non-local, no need to check anything.
810 let attrs = &item.attrs;
811 let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
813 cx.tcx.struct_span_lint_hir(
814 lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
818 let mut diag = lint.build(&format!("`[{}]` {}", path_str, err_msg));
819 if let Some(link_range) = link_range {
820 if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs)
823 diag.span_label(sp, short_err_msg);
825 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
828 // last_new_line_offset
829 let last_new_line_offset =
830 dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
831 let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
833 // Print the line containing the `link_range` and manually mark it with '^'s.
835 "the link appears in this line:\n\n{line}\n\
836 {indicator: <before$}{indicator:^<found$}",
839 before = link_range.start - last_new_line_offset,
840 found = link_range.len(),
844 if let Some(help_msg) = help_msg {
852 /// Reports a resolution failure diagnostic.
854 /// If we cannot find the exact source span of the resolution failure, we use the span of the
855 /// documentation attributes themselves. This is a little heavy-handed, so we display the markdown
856 /// line containing the failure as a note as well.
857 fn resolution_failure(
862 link_range: Option<Range<usize>>,
870 "cannot be resolved, ignoring it.",
871 "cannot be resolved, ignoring",
872 Some("to escape `[` and `]` characters, just add '\\' before them like `\\[` or `\\]`"),
881 link_range: Option<Range<usize>>,
890 "has an issue with the link anchor.",
901 link_range: Option<Range<usize>>,
902 candidates: PerNS<Option<Res>>,
904 let hir_id = match cx.as_local_hir_id(item.def_id) {
905 Some(hir_id) => hir_id,
907 // If non-local, no need to check anything.
911 let attrs = &item.attrs;
912 let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
914 cx.tcx.struct_span_lint_hir(
915 lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
919 let mut msg = format!("`{}` is ", path_str);
921 let candidates = [TypeNS, ValueNS, MacroNS]
923 .filter_map(|&ns| candidates[ns].map(|res| (res, ns)))
924 .collect::<Vec<_>>();
925 match candidates.as_slice() {
926 [(first_def, _), (second_def, _)] => {
928 "both {} {} and {} {}",
931 second_def.article(),
936 let mut candidates = candidates.iter().peekable();
937 while let Some((res, _)) = candidates.next() {
938 if candidates.peek().is_some() {
939 msg += &format!("{} {}, ", res.article(), res.descr());
941 msg += &format!("and {} {}", res.article(), res.descr());
947 let mut diag = lint.build(&msg);
949 if let Some(link_range) = link_range {
950 if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs)
953 diag.span_label(sp, "ambiguous link");
955 for (res, ns) in candidates {
956 let (action, mut suggestion) = match res {
957 Res::Def(DefKind::AssocFn | DefKind::Fn, _) => {
958 ("add parentheses", format!("{}()", path_str))
960 Res::Def(DefKind::Macro(MacroKind::Bang), _) => {
961 ("add an exclamation mark", format!("{}!", path_str))
964 let type_ = match (res, ns) {
965 (Res::Def(DefKind::Const, _), _) => "const",
966 (Res::Def(DefKind::Static, _), _) => "static",
967 (Res::Def(DefKind::Struct, _), _) => "struct",
968 (Res::Def(DefKind::Enum, _), _) => "enum",
969 (Res::Def(DefKind::Union, _), _) => "union",
970 (Res::Def(DefKind::Trait, _), _) => "trait",
971 (Res::Def(DefKind::Mod, _), _) => "module",
972 (_, TypeNS) => "type",
973 (_, ValueNS) => "value",
974 (Res::Def(DefKind::Macro(MacroKind::Derive), _), MacroNS) => {
977 (_, MacroNS) => "macro",
980 // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
981 ("prefix with the item type", format!("{}@{}", type_, path_str))
985 if dox.bytes().nth(link_range.start) == Some(b'`') {
986 suggestion = format!("`{}`", suggestion);
989 diag.span_suggestion(
991 &format!("to link to the {}, {}", res.descr(), action),
993 Applicability::MaybeIncorrect,
997 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
1000 // last_new_line_offset
1001 let last_new_line_offset =
1002 dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
1003 let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
1005 // Print the line containing the `link_range` and manually mark it with '^'s.
1007 "the link appears in this line:\n\n{line}\n\
1008 {indicator: <before$}{indicator:^<found$}",
1011 before = link_range.start - last_new_line_offset,
1012 found = link_range.len(),
1021 /// Given an enum variant's res, return the res of its enum and the associated fragment.
1023 cx: &DocContext<'_>,
1025 extra_fragment: &Option<String>,
1026 ) -> Result<(Res, Option<String>), ErrorKind> {
1027 use rustc_middle::ty::DefIdTree;
1029 if extra_fragment.is_some() {
1030 return Err(ErrorKind::AnchorFailure("variants cannot be followed by anchors"));
1032 let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) {
1035 return Err(ErrorKind::ResolutionFailure);
1037 let parent_def = Res::Def(DefKind::Enum, parent);
1038 let variant = cx.tcx.expect_variant_res(res);
1039 Ok((parent_def, Some(format!("{}.v", variant.ident.name))))
1042 const PRIMITIVES: &[(&str, Res)] = &[
1043 ("u8", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U8))),
1044 ("u16", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U16))),
1045 ("u32", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U32))),
1046 ("u64", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U64))),
1047 ("u128", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U128))),
1048 ("usize", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::Usize))),
1049 ("i8", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I8))),
1050 ("i16", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I16))),
1051 ("i32", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I32))),
1052 ("i64", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I64))),
1053 ("i128", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I128))),
1054 ("isize", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::Isize))),
1055 ("f32", Res::PrimTy(hir::PrimTy::Float(rustc_ast::ast::FloatTy::F32))),
1056 ("f64", Res::PrimTy(hir::PrimTy::Float(rustc_ast::ast::FloatTy::F64))),
1057 ("str", Res::PrimTy(hir::PrimTy::Str)),
1058 ("bool", Res::PrimTy(hir::PrimTy::Bool)),
1059 ("char", Res::PrimTy(hir::PrimTy::Char)),
1062 fn is_primitive(path_str: &str, ns: Namespace) -> Option<Res> {
1063 if ns == TypeNS { PRIMITIVES.iter().find(|x| x.0 == path_str).map(|x| x.1) } else { None }
1066 fn primitive_impl(cx: &DocContext<'_>, path_str: &str) -> Option<DefId> {
1069 "u8" => tcx.lang_items().u8_impl(),
1070 "u16" => tcx.lang_items().u16_impl(),
1071 "u32" => tcx.lang_items().u32_impl(),
1072 "u64" => tcx.lang_items().u64_impl(),
1073 "u128" => tcx.lang_items().u128_impl(),
1074 "usize" => tcx.lang_items().usize_impl(),
1075 "i8" => tcx.lang_items().i8_impl(),
1076 "i16" => tcx.lang_items().i16_impl(),
1077 "i32" => tcx.lang_items().i32_impl(),
1078 "i64" => tcx.lang_items().i64_impl(),
1079 "i128" => tcx.lang_items().i128_impl(),
1080 "isize" => tcx.lang_items().isize_impl(),
1081 "f32" => tcx.lang_items().f32_impl(),
1082 "f64" => tcx.lang_items().f64_impl(),
1083 "str" => tcx.lang_items().str_impl(),
1084 "bool" => tcx.lang_items().bool_impl(),
1085 "char" => tcx.lang_items().char_impl(),