1 use errors
::Applicability
;
8 use rustc
::hir
::def_id
::DefId
;
11 use rustc_feature
::UnstableFeatures
;
12 use rustc_resolve
::ParentScope
;
14 use syntax
::ast
::{self, Ident}
;
15 use syntax
::symbol
::Symbol
;
16 use syntax_expand
::base
::SyntaxExtensionKind
;
17 use syntax_pos
::DUMMY_SP
;
22 use crate::core
::DocContext
;
23 use crate::fold
::DocFolder
;
24 use crate::html
::markdown
::markdown_links
;
25 use crate::passes
::{look_for_tests, Pass}
;
27 use super::span_of_attrs
;
29 pub const COLLECT_INTRA_DOC_LINKS
: Pass
= Pass
{
30 name
: "collect-intra-doc-links",
31 run
: collect_intra_doc_links
,
32 description
: "reads a crate's documentation to resolve intra-doc-links",
35 pub fn collect_intra_doc_links(krate
: Crate
, cx
: &DocContext
<'_
>) -> Crate
{
36 if !UnstableFeatures
::from_environment().is_nightly_build() {
39 let mut coll
= LinkCollector
::new(cx
);
41 coll
.fold_crate(krate
)
47 AnchorFailure(&'
static str),
50 struct LinkCollector
<'a
, 'tcx
> {
51 cx
: &'a DocContext
<'tcx
>,
52 mod_ids
: Vec
<hir
::HirId
>,
55 impl<'a
, 'tcx
> LinkCollector
<'a
, 'tcx
> {
56 fn new(cx
: &'a DocContext
<'tcx
>) -> Self {
57 LinkCollector { cx, mod_ids: Vec::new() }
63 current_item
: &Option
<String
>,
64 module_id
: syntax
::ast
::NodeId
,
65 ) -> Result
<(Res
, Option
<String
>), ErrorKind
> {
68 let mut split
= path_str
.rsplitn(3, "::");
69 let variant_field_name
=
70 split
.next().map(|f
| Symbol
::intern(f
)).ok_or(ErrorKind
::ResolutionFailure
)?
;
72 split
.next().map(|f
| Symbol
::intern(f
)).ok_or(ErrorKind
::ResolutionFailure
)?
;
76 if f
== "self" || f
== "Self" {
77 if let Some(name
) = current_item
.as_ref() {
83 .ok_or(ErrorKind
::ResolutionFailure
)?
;
85 .enter_resolver(|resolver
| {
86 resolver
.resolve_str_path_error(DUMMY_SP
, &path
, TypeNS
, module_id
)
88 .map_err(|_
| ErrorKind
::ResolutionFailure
)?
;
89 if let Res
::Err
= ty_res
{
90 return Err(ErrorKind
::ResolutionFailure
);
92 let ty_res
= ty_res
.map_id(|_
| panic
!("unexpected node_id"));
94 Res
::Def(DefKind
::Enum
, did
) => {
99 .flat_map(|imp
| cx
.tcx
.associated_items(*imp
))
100 .any(|item
| item
.ident
.name
== variant_name
)
102 return Err(ErrorKind
::ResolutionFailure
);
104 match cx
.tcx
.type_of(did
).kind
{
105 ty
::Adt(def
, _
) if def
.is_enum() => {
106 if def
.all_fields().any(|item
| item
.ident
.name
== variant_field_name
) {
110 "variant.{}.field.{}",
111 variant_name
, variant_field_name
115 Err(ErrorKind
::ResolutionFailure
)
118 _
=> Err(ErrorKind
::ResolutionFailure
),
121 _
=> Err(ErrorKind
::ResolutionFailure
),
125 /// Resolves a string as a path within a particular namespace. Also returns an optional
126 /// URL fragment in the case of variants and methods.
131 current_item
: &Option
<String
>,
132 parent_id
: Option
<hir
::HirId
>,
133 extra_fragment
: &Option
<String
>,
134 ) -> Result
<(Res
, Option
<String
>), ErrorKind
> {
137 // In case we're in a module, try to resolve the relative path.
138 if let Some(module_id
) = parent_id
.or(self.mod_ids
.last().cloned()) {
139 let module_id
= cx
.tcx
.hir().hir_to_node_id(module_id
);
140 let result
= cx
.enter_resolver(|resolver
| {
141 resolver
.resolve_str_path_error(DUMMY_SP
, &path_str
, ns
, module_id
)
143 let result
= match result
{
144 Ok((_
, Res
::Err
)) => Err(ErrorKind
::ResolutionFailure
),
145 _
=> result
.map_err(|_
| ErrorKind
::ResolutionFailure
),
148 if let Ok((_
, res
)) = result
{
149 let res
= res
.map_id(|_
| panic
!("unexpected node_id"));
150 // In case this is a trait item, skip the
151 // early return and try looking for the trait.
152 let value
= match res
{
153 Res
::Def(DefKind
::Method
, _
) | Res
::Def(DefKind
::AssocConst
, _
) => true,
154 Res
::Def(DefKind
::AssocTy
, _
) => false,
155 Res
::Def(DefKind
::Variant
, _
) => {
156 return handle_variant(cx
, res
, extra_fragment
);
158 // Not a trait item; just return what we found.
160 if extra_fragment
.is_some() {
161 return Err(ErrorKind
::AnchorFailure(
162 "primitive types cannot be followed by anchors",
165 return Ok((res
, Some(path_str
.to_owned())));
167 _
=> return Ok((res
, extra_fragment
.clone())),
170 if value
!= (ns
== ValueNS
) {
171 return Err(ErrorKind
::ResolutionFailure
);
173 } else if let Some(prim
) = is_primitive(path_str
, ns
) {
174 if extra_fragment
.is_some() {
175 return Err(ErrorKind
::AnchorFailure(
176 "primitive types cannot be followed by anchors",
179 return Ok((prim
, Some(path_str
.to_owned())));
181 // If resolution failed, it may still be a method
182 // because methods are not handled by the resolver
183 // If so, bail when we're not looking for a value.
185 return Err(ErrorKind
::ResolutionFailure
);
189 // Try looking for methods and associated items.
190 let mut split
= path_str
.rsplitn(2, "::");
192 split
.next().map(|f
| Symbol
::intern(f
)).ok_or(ErrorKind
::ResolutionFailure
)?
;
196 if f
== "self" || f
== "Self" {
197 if let Some(name
) = current_item
.as_ref() {
203 .ok_or(ErrorKind
::ResolutionFailure
)?
;
205 if let Some(prim
) = is_primitive(&path
, TypeNS
) {
206 let did
= primitive_impl(cx
, &path
).ok_or(ErrorKind
::ResolutionFailure
)?
;
209 .associated_items(did
)
210 .find(|item
| item
.ident
.name
== item_name
)
211 .and_then(|item
| match item
.kind
{
212 ty
::AssocKind
::Method
=> Some("method"),
215 .map(|out
| (prim
, Some(format
!("{}#{}.{}", path
, out
, item_name
))))
216 .ok_or(ErrorKind
::ResolutionFailure
);
220 .enter_resolver(|resolver
| {
221 resolver
.resolve_str_path_error(DUMMY_SP
, &path
, TypeNS
, module_id
)
223 .map_err(|_
| ErrorKind
::ResolutionFailure
)?
;
224 if let Res
::Err
= ty_res
{
225 return self.variant_field(path_str
, current_item
, module_id
);
227 let ty_res
= ty_res
.map_id(|_
| panic
!("unexpected node_id"));
229 Res
::Def(DefKind
::Struct
, did
)
230 | Res
::Def(DefKind
::Union
, did
)
231 | Res
::Def(DefKind
::Enum
, did
)
232 | Res
::Def(DefKind
::TyAlias
, did
) => {
237 .flat_map(|imp
| cx
.tcx
.associated_items(*imp
))
238 .find(|item
| item
.ident
.name
== item_name
);
239 if let Some(item
) = item
{
240 let out
= match item
.kind
{
241 ty
::AssocKind
::Method
if ns
== ValueNS
=> "method",
242 ty
::AssocKind
::Const
if ns
== ValueNS
=> "associatedconstant",
243 _
=> return self.variant_field(path_str
, current_item
, module_id
),
245 if extra_fragment
.is_some() {
246 Err(ErrorKind
::AnchorFailure(if item
.kind
== ty
::AssocKind
::Method
{
247 "methods cannot be followed by anchors"
249 "associated constants cannot be followed by anchors"
252 Ok((ty_res
, Some(format
!("{}.{}", out
, item_name
))))
255 match cx
.tcx
.type_of(did
).kind
{
257 if let Some(item
) = if def
.is_enum() {
258 def
.all_fields().find(|item
| item
.ident
.name
== item_name
)
260 def
.non_enum_variant()
263 .find(|item
| item
.ident
.name
== item_name
)
265 if extra_fragment
.is_some() {
266 Err(ErrorKind
::AnchorFailure(if def
.is_enum() {
267 "enum variants cannot be followed by anchors"
269 "struct fields cannot be followed by anchors"
286 self.variant_field(path_str
, current_item
, module_id
)
289 _
=> self.variant_field(path_str
, current_item
, module_id
),
293 Res
::Def(DefKind
::Trait
, did
) => {
296 .associated_item_def_ids(did
)
298 .map(|item
| cx
.tcx
.associated_item(*item
))
299 .find(|item
| item
.ident
.name
== item_name
);
300 if let Some(item
) = item
{
301 let kind
= match item
.kind
{
302 ty
::AssocKind
::Const
if ns
== ValueNS
=> "associatedconstant",
303 ty
::AssocKind
::Type
if ns
== TypeNS
=> "associatedtype",
304 ty
::AssocKind
::Method
if ns
== ValueNS
=> {
305 if item
.defaultness
.has_value() { "method" }
else { "tymethod" }
307 _
=> return self.variant_field(path_str
, current_item
, module_id
),
310 if extra_fragment
.is_some() {
311 Err(ErrorKind
::AnchorFailure(if item
.kind
== ty
::AssocKind
::Const
{
312 "associated constants cannot be followed by anchors"
313 } else if item
.kind
== ty
::AssocKind
::Type
{
314 "associated types cannot be followed by anchors"
316 "methods cannot be followed by anchors"
319 Ok((ty_res
, Some(format
!("{}.{}", kind
, item_name
))))
322 self.variant_field(path_str
, current_item
, module_id
)
325 _
=> self.variant_field(path_str
, current_item
, module_id
),
328 debug
!("attempting to resolve item without parent module: {}", path_str
);
329 Err(ErrorKind
::ResolutionFailure
)
334 impl<'a
, 'tcx
> DocFolder
for LinkCollector
<'a
, 'tcx
> {
335 fn fold_item(&mut self, mut item
: Item
) -> Option
<Item
> {
336 let item_hir_id
= if item
.is_mod() {
337 if let Some(id
) = self.cx
.tcx
.hir().as_local_hir_id(item
.def_id
) {
340 debug
!("attempting to fold on a non-local item: {:?}", item
);
341 return self.fold_item_recur(item
);
347 // FIXME: get the resolver to work with non-local resolve scopes.
348 let parent_node
= self.cx
.as_local_hir_id(item
.def_id
).and_then(|hir_id
| {
349 // FIXME: this fails hard for impls in non-module scope, but is necessary for the
350 // current `resolve()` implementation.
351 match self.cx
.tcx
.hir().get_module_parent_node(hir_id
) {
352 id
if id
!= hir_id
=> Some(id
),
357 if parent_node
.is_some() {
358 debug
!("got parent node for {:?} {:?}, id {:?}", item
.type_(), item
.name
, item
.def_id
);
361 let current_item
= match item
.inner
{
363 if item
.attrs
.inner_docs
{
364 if item_hir_id
.unwrap() != hir
::CRATE_HIR_ID { item.name.clone() }
else { None }
366 match parent_node
.or(self.mod_ids
.last().cloned()) {
367 Some(parent
) if parent
!= hir
::CRATE_HIR_ID
=> {
368 // FIXME: can we pull the parent module's name from elsewhere?
369 Some(self.cx
.tcx
.hir().name(parent
).to_string())
375 ImplItem(Impl { ref for_, .. }
) => {
376 for_
.def_id().map(|did
| self.cx
.tcx
.item_name(did
).to_string())
378 // we don't display docs on `extern crate` items anyway, so don't process them.
379 ExternCrateItem(..) => return self.fold_item_recur(item
),
380 ImportItem(Import
::Simple(ref name
, ..)) => Some(name
.clone()),
381 MacroItem(..) => None
,
382 _
=> item
.name
.clone(),
385 if item
.is_mod() && item
.attrs
.inner_docs
{
386 self.mod_ids
.push(item_hir_id
.unwrap());
390 let dox
= item
.attrs
.collapsed_doc_value().unwrap_or_else(String
::new
);
392 look_for_tests(&cx
, &dox
, &item
, true);
394 for (ori_link
, link_range
) in markdown_links(&dox
) {
395 // Bail early for real links.
396 if ori_link
.contains('
/'
) {
400 // [] is mostly likely not supposed to be a link
401 if ori_link
.is_empty() {
405 let link
= ori_link
.replace("`", "");
406 let parts
= link
.split('
#').collect::<Vec<_>>();
407 let (link
, extra_fragment
) = if parts
.len() > 2 {
414 "has an issue with the link anchor.",
415 "only one `#` is allowed in a link",
419 } else if parts
.len() == 2 {
420 if parts
[0].trim().is_empty() {
421 // This is an anchor to an element of the current page, nothing to do in here!
424 (parts
[0].to_owned(), Some(parts
[1].to_owned()))
426 (parts
[0].to_owned(), None
)
428 let (res
, fragment
) = {
430 let path_str
= if let Some(prefix
) =
431 ["struct@", "enum@", "type@", "trait@", "union@"]
433 .find(|p
| link
.starts_with(**p
))
436 link
.trim_start_matches(prefix
)
437 } else if let Some(prefix
) = [
448 .find(|p
| link
.starts_with(**p
))
450 kind
= Some(ValueNS
);
451 link
.trim_start_matches(prefix
)
452 } else if link
.ends_with("()") {
453 kind
= Some(ValueNS
);
454 link
.trim_end_matches("()")
455 } else if link
.starts_with("macro@") {
456 kind
= Some(MacroNS
);
457 link
.trim_start_matches("macro@")
458 } else if link
.ends_with('
!'
) {
459 kind
= Some(MacroNS
);
460 link
.trim_end_matches('
!'
)
466 if path_str
.contains(|ch
: char| !(ch
.is_alphanumeric() || ch
== '
:'
|| ch
== '_'
)) {
470 // In order to correctly resolve intra-doc-links we need to
471 // pick a base AST node to work from. If the documentation for
472 // this module came from an inner comment (//!) then we anchor
473 // our name resolution *inside* the module. If, on the other
474 // hand it was an outer comment (///) then we anchor the name
475 // resolution in the parent module on the basis that the names
476 // used are more likely to be intended to be parent names. For
477 // this, we set base_node to None for inner comments since
478 // we've already pushed this node onto the resolution stack but
479 // for outer comments we explicitly try and resolve against the
480 // parent_node first.
482 if item
.is_mod() && item
.attrs
.inner_docs { None }
else { parent_node }
;
485 Some(ns @ ValueNS
) => {
486 match self.resolve(path_str
, ns
, ¤t_item
, base_node
, &extra_fragment
)
489 Err(ErrorKind
::ResolutionFailure
) => {
490 resolution_failure(cx
, &item
, path_str
, &dox
, link_range
);
491 // This could just be a normal link or a broken link
492 // we could potentially check if something is
493 // "intra-doc-link-like" and warn in that case.
496 Err(ErrorKind
::AnchorFailure(msg
)) => {
497 anchor_failure(cx
, &item
, &ori_link
, &dox
, link_range
, msg
);
502 Some(ns @ TypeNS
) => {
503 match self.resolve(path_str
, ns
, ¤t_item
, base_node
, &extra_fragment
)
506 Err(ErrorKind
::ResolutionFailure
) => {
507 resolution_failure(cx
, &item
, path_str
, &dox
, link_range
);
508 // This could just be a normal link.
511 Err(ErrorKind
::AnchorFailure(msg
)) => {
512 anchor_failure(cx
, &item
, &ori_link
, &dox
, link_range
, msg
);
519 let candidates
= PerNS
{
520 macro_ns
: macro_resolve(cx
, path_str
)
521 .map(|res
| (res
, extra_fragment
.clone())),
522 type_ns
: match self.resolve(
529 Err(ErrorKind
::AnchorFailure(msg
)) => {
530 anchor_failure(cx
, &item
, &ori_link
, &dox
, link_range
, msg
);
535 value_ns
: match self.resolve(
542 Err(ErrorKind
::AnchorFailure(msg
)) => {
543 anchor_failure(cx
, &item
, &ori_link
, &dox
, link_range
, msg
);
548 .and_then(|(res
, fragment
)| {
549 // Constructors are picked up in the type namespace.
551 Res
::Def(DefKind
::Ctor(..), _
) | Res
::SelfCtor(..) => None
,
552 _
=> match (fragment
, extra_fragment
) {
553 (Some(fragment
), Some(_
)) => {
554 // Shouldn't happen but who knows?
555 Some((res
, Some(fragment
)))
557 (fragment
, None
) | (None
, fragment
) => {
558 Some((res
, fragment
))
565 if candidates
.is_empty() {
566 resolution_failure(cx
, &item
, path_str
, &dox
, link_range
);
567 // this could just be a normal link
571 let is_unambiguous
= candidates
.clone().present_items().count() == 1;
573 candidates
.present_items().next().unwrap()
581 candidates
.map(|candidate
| candidate
.map(|(res
, _
)| res
)),
587 if let Some(res
) = macro_resolve(cx
, path_str
) {
588 (res
, extra_fragment
)
590 resolution_failure(cx
, &item
, path_str
, &dox
, link_range
);
597 if let Res
::PrimTy(_
) = res
{
598 item
.attrs
.links
.push((ori_link
, None
, fragment
));
600 let id
= register_res(cx
, res
);
601 item
.attrs
.links
.push((ori_link
, Some(id
), fragment
));
605 if item
.is_mod() && !item
.attrs
.inner_docs
{
606 self.mod_ids
.push(item_hir_id
.unwrap());
610 let ret
= self.fold_item_recur(item
);
616 self.fold_item_recur(item
)
620 // FIXME: if we can resolve intra-doc links from other crates, we can use the stock
621 // `fold_crate`, but until then we should avoid scanning `krate.external_traits` since those
622 // will never resolve properly
623 fn fold_crate(&mut self, mut c
: Crate
) -> Crate
{
624 c
.module
= c
.module
.take().and_then(|module
| self.fold_item(module
));
630 /// Resolves a string as a macro.
631 fn macro_resolve(cx
: &DocContext
<'_
>, path_str
: &str) -> Option
<Res
> {
632 let path
= ast
::Path
::from_ident(Ident
::from_str(path_str
));
633 cx
.enter_resolver(|resolver
| {
634 if let Ok((Some(ext
), res
)) = resolver
.resolve_macro_path(
637 &ParentScope
::module(resolver
.graph_root()),
641 if let SyntaxExtensionKind
::LegacyBang { .. }
= ext
.kind
{
642 return Some(res
.map_id(|_
| panic
!("unexpected id")));
645 if let Some(res
) = resolver
.all_macros().get(&Symbol
::intern(path_str
)) {
646 return Some(res
.map_id(|_
| panic
!("unexpected id")));
657 link_range
: Option
<Range
<usize>>,
660 help_msg
: Option
<&str>,
662 let hir_id
= match cx
.as_local_hir_id(item
.def_id
) {
663 Some(hir_id
) => hir_id
,
665 // If non-local, no need to check anything.
669 let attrs
= &item
.attrs
;
670 let sp
= span_of_attrs(attrs
).unwrap_or(item
.source
.span());
672 let mut diag
= cx
.tcx
.struct_span_lint_hir(
673 lint
::builtin
::INTRA_DOC_LINK_RESOLUTION_FAILURE
,
676 &format
!("`[{}]` {}", path_str
, err_msg
),
678 if let Some(link_range
) = link_range
{
679 if let Some(sp
) = super::source_span_for_markdown_range(cx
, dox
, &link_range
, attrs
) {
681 diag
.span_label(sp
, short_err_msg
);
683 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
686 // last_new_line_offset
687 let last_new_line_offset
= dox
[..link_range
.start
].rfind('
\n'
).map_or(0, |n
| n
+ 1);
688 let line
= dox
[last_new_line_offset
..].lines().next().unwrap_or("");
690 // Print the line containing the `link_range` and manually mark it with '^'s.
692 "the link appears in this line:\n\n{line}\n\
693 {indicator: <before$}{indicator:^<found$}",
696 before
= link_range
.start
- last_new_line_offset
,
697 found
= link_range
.len(),
701 if let Some(help_msg
) = help_msg
{
707 /// Reports a resolution failure diagnostic.
709 /// If we cannot find the exact source span of the resolution failure, we use the span of the
710 /// documentation attributes themselves. This is a little heavy-handed, so we display the markdown
711 /// line containing the failure as a note as well.
712 fn resolution_failure(
717 link_range
: Option
<Range
<usize>>,
725 "cannot be resolved, ignoring it.",
726 "cannot be resolved, ignoring",
727 Some("to escape `[` and `]` characters, just add '\\' before them like `\\[` or `\\]`"),
736 link_range
: Option
<Range
<usize>>,
745 "has an issue with the link anchor.",
756 link_range
: Option
<Range
<usize>>,
757 candidates
: PerNS
<Option
<Res
>>,
759 let hir_id
= match cx
.as_local_hir_id(item
.def_id
) {
760 Some(hir_id
) => hir_id
,
762 // If non-local, no need to check anything.
766 let attrs
= &item
.attrs
;
767 let sp
= span_of_attrs(attrs
).unwrap_or(item
.source
.span());
769 let mut msg
= format
!("`{}` is ", path_str
);
771 let candidates
= [TypeNS
, ValueNS
, MacroNS
]
773 .filter_map(|&ns
| candidates
[ns
].map(|res
| (res
, ns
)))
774 .collect
::<Vec
<_
>>();
775 match candidates
.as_slice() {
776 [(first_def
, _
), (second_def
, _
)] => {
778 "both {} {} and {} {}",
781 second_def
.article(),
786 let mut candidates
= candidates
.iter().peekable();
787 while let Some((res
, _
)) = candidates
.next() {
788 if candidates
.peek().is_some() {
789 msg
+= &format
!("{} {}, ", res
.article(), res
.descr());
791 msg
+= &format
!("and {} {}", res
.article(), res
.descr());
797 let mut diag
= cx
.tcx
.struct_span_lint_hir(
798 lint
::builtin
::INTRA_DOC_LINK_RESOLUTION_FAILURE
,
804 if let Some(link_range
) = link_range
{
805 if let Some(sp
) = super::source_span_for_markdown_range(cx
, dox
, &link_range
, attrs
) {
807 diag
.span_label(sp
, "ambiguous link");
809 for (res
, ns
) in candidates
{
810 let (action
, mut suggestion
) = match res
{
811 Res
::Def(DefKind
::Method
, _
) | Res
::Def(DefKind
::Fn
, _
) => {
812 ("add parentheses", format
!("{}()", path_str
))
814 Res
::Def(DefKind
::Macro(..), _
) => {
815 ("add an exclamation mark", format
!("{}!", path_str
))
818 let type_
= match (res
, ns
) {
819 (Res
::Def(DefKind
::Const
, _
), _
) => "const",
820 (Res
::Def(DefKind
::Static
, _
), _
) => "static",
821 (Res
::Def(DefKind
::Struct
, _
), _
) => "struct",
822 (Res
::Def(DefKind
::Enum
, _
), _
) => "enum",
823 (Res
::Def(DefKind
::Union
, _
), _
) => "union",
824 (Res
::Def(DefKind
::Trait
, _
), _
) => "trait",
825 (Res
::Def(DefKind
::Mod
, _
), _
) => "module",
826 (_
, TypeNS
) => "type",
827 (_
, ValueNS
) => "value",
828 (_
, MacroNS
) => "macro",
831 // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
832 ("prefix with the item type", format
!("{}@{}", type_
, path_str
))
836 if dox
.bytes().nth(link_range
.start
) == Some(b'`'
) {
837 suggestion
= format
!("`{}`", suggestion
);
840 diag
.span_suggestion(
842 &format
!("to link to the {}, {}", res
.descr(), action
),
844 Applicability
::MaybeIncorrect
,
848 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
851 // last_new_line_offset
852 let last_new_line_offset
= dox
[..link_range
.start
].rfind('
\n'
).map_or(0, |n
| n
+ 1);
853 let line
= dox
[last_new_line_offset
..].lines().next().unwrap_or("");
855 // Print the line containing the `link_range` and manually mark it with '^'s.
857 "the link appears in this line:\n\n{line}\n\
858 {indicator: <before$}{indicator:^<found$}",
861 before
= link_range
.start
- last_new_line_offset
,
862 found
= link_range
.len(),
870 /// Given an enum variant's res, return the res of its enum and the associated fragment.
874 extra_fragment
: &Option
<String
>,
875 ) -> Result
<(Res
, Option
<String
>), ErrorKind
> {
876 use rustc
::ty
::DefIdTree
;
878 if extra_fragment
.is_some() {
879 return Err(ErrorKind
::AnchorFailure("variants cannot be followed by anchors"));
881 let parent
= if let Some(parent
) = cx
.tcx
.parent(res
.def_id()) {
884 return Err(ErrorKind
::ResolutionFailure
);
886 let parent_def
= Res
::Def(DefKind
::Enum
, parent
);
887 let variant
= cx
.tcx
.expect_variant_res(res
);
888 Ok((parent_def
, Some(format
!("{}.v", variant
.ident
.name
))))
891 const PRIMITIVES
: &[(&str, Res
)] = &[
892 ("u8", Res
::PrimTy(hir
::PrimTy
::Uint(syntax
::ast
::UintTy
::U8
))),
893 ("u16", Res
::PrimTy(hir
::PrimTy
::Uint(syntax
::ast
::UintTy
::U16
))),
894 ("u32", Res
::PrimTy(hir
::PrimTy
::Uint(syntax
::ast
::UintTy
::U32
))),
895 ("u64", Res
::PrimTy(hir
::PrimTy
::Uint(syntax
::ast
::UintTy
::U64
))),
896 ("u128", Res
::PrimTy(hir
::PrimTy
::Uint(syntax
::ast
::UintTy
::U128
))),
897 ("usize", Res
::PrimTy(hir
::PrimTy
::Uint(syntax
::ast
::UintTy
::Usize
))),
898 ("i8", Res
::PrimTy(hir
::PrimTy
::Int(syntax
::ast
::IntTy
::I8
))),
899 ("i16", Res
::PrimTy(hir
::PrimTy
::Int(syntax
::ast
::IntTy
::I16
))),
900 ("i32", Res
::PrimTy(hir
::PrimTy
::Int(syntax
::ast
::IntTy
::I32
))),
901 ("i64", Res
::PrimTy(hir
::PrimTy
::Int(syntax
::ast
::IntTy
::I64
))),
902 ("i128", Res
::PrimTy(hir
::PrimTy
::Int(syntax
::ast
::IntTy
::I128
))),
903 ("isize", Res
::PrimTy(hir
::PrimTy
::Int(syntax
::ast
::IntTy
::Isize
))),
904 ("f32", Res
::PrimTy(hir
::PrimTy
::Float(syntax
::ast
::FloatTy
::F32
))),
905 ("f64", Res
::PrimTy(hir
::PrimTy
::Float(syntax
::ast
::FloatTy
::F64
))),
906 ("str", Res
::PrimTy(hir
::PrimTy
::Str
)),
907 ("bool", Res
::PrimTy(hir
::PrimTy
::Bool
)),
908 ("char", Res
::PrimTy(hir
::PrimTy
::Char
)),
911 fn is_primitive(path_str
: &str, ns
: Namespace
) -> Option
<Res
> {
912 if ns
== TypeNS { PRIMITIVES.iter().find(|x| x.0 == path_str).map(|x| x.1) }
else { None }
915 fn primitive_impl(cx
: &DocContext
<'_
>, path_str
: &str) -> Option
<DefId
> {
918 "u8" => tcx
.lang_items().u8_impl(),
919 "u16" => tcx
.lang_items().u16_impl(),
920 "u32" => tcx
.lang_items().u32_impl(),
921 "u64" => tcx
.lang_items().u64_impl(),
922 "u128" => tcx
.lang_items().u128_impl(),
923 "usize" => tcx
.lang_items().usize_impl(),
924 "i8" => tcx
.lang_items().i8_impl(),
925 "i16" => tcx
.lang_items().i16_impl(),
926 "i32" => tcx
.lang_items().i32_impl(),
927 "i64" => tcx
.lang_items().i64_impl(),
928 "i128" => tcx
.lang_items().i128_impl(),
929 "isize" => tcx
.lang_items().isize_impl(),
930 "f32" => tcx
.lang_items().f32_impl(),
931 "f64" => tcx
.lang_items().f64_impl(),
932 "str" => tcx
.lang_items().str_impl(),
933 "bool" => tcx
.lang_items().bool_impl(),
934 "char" => tcx
.lang_items().char_impl(),