]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/passes/collect_intra_doc_links.rs
New upstream version 1.39.0+dfsg1
[rustc.git] / src / librustdoc / passes / collect_intra_doc_links.rs
CommitLineData
532ac7d7 1use errors::Applicability;
48663c56 2use rustc::hir::def::{Res, DefKind, Namespace::{self, *}, PerNS};
9fa01778 3use rustc::hir::def_id::DefId;
532ac7d7
XL
4use rustc::hir;
5use rustc::lint as lint;
b7449926 6use rustc::ty;
e1599b0c 7use rustc_resolve::ParentScope;
b7449926 8use syntax;
532ac7d7 9use syntax::ast::{self, Ident};
416331ca 10use syntax::ext::base::SyntaxExtensionKind;
b7449926
XL
11use syntax::feature_gate::UnstableFeatures;
12use syntax::symbol::Symbol;
9fa01778 13use syntax_pos::DUMMY_SP;
b7449926
XL
14
15use std::ops::Range;
16
9fa01778
XL
17use crate::core::DocContext;
18use crate::fold::DocFolder;
19use crate::html::markdown::markdown_links;
20use crate::clean::*;
21use crate::passes::{look_for_tests, Pass};
0bf4aa26 22
9fa01778 23use super::span_of_attrs;
b7449926 24
532ac7d7
XL
25pub const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
26 name: "collect-intra-doc-links",
27 pass: collect_intra_doc_links,
28 description: "reads a crate's documentation to resolve intra-doc-links",
29};
b7449926 30
532ac7d7 31pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate {
b7449926
XL
32 if !UnstableFeatures::from_environment().is_nightly_build() {
33 krate
34 } else {
35 let mut coll = LinkCollector::new(cx);
36
37 coll.fold_crate(krate)
38 }
39}
40
532ac7d7
XL
41struct LinkCollector<'a, 'tcx> {
42 cx: &'a DocContext<'tcx>,
48663c56 43 mod_ids: Vec<hir::HirId>,
b7449926
XL
44}
45
532ac7d7
XL
46impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
47 fn new(cx: &'a DocContext<'tcx>) -> Self {
b7449926
XL
48 LinkCollector {
49 cx,
50 mod_ids: Vec::new(),
51 }
52 }
53
532ac7d7
XL
54 /// Resolves a string as a path within a particular namespace. Also returns an optional
55 /// URL fragment in the case of variants and methods.
0bf4aa26
XL
56 fn resolve(&self,
57 path_str: &str,
532ac7d7 58 ns: Namespace,
0bf4aa26 59 current_item: &Option<String>,
48663c56
XL
60 parent_id: Option<hir::HirId>)
61 -> Result<(Res, Option<String>), ()>
b7449926
XL
62 {
63 let cx = self.cx;
64
416331ca
XL
65 // In case we're in a module, try to resolve the relative path.
66 if let Some(module_id) = parent_id.or(self.mod_ids.last().cloned()) {
67 let module_id = cx.tcx.hir().hir_to_node_id(module_id);
532ac7d7 68 let result = cx.enter_resolver(|resolver| {
416331ca 69 resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
b7449926 70 });
416331ca
XL
71 let result = match result {
72 Ok((_, Res::Err)) => Err(()),
73 _ => result,
74 };
b7449926 75
dc9dc135
XL
76 if let Ok((_, res)) = result {
77 let res = res.map_id(|_| panic!("unexpected node_id"));
b7449926 78 // In case this is a trait item, skip the
0731742a 79 // early return and try looking for the trait.
dc9dc135
XL
80 let value = match res {
81 Res::Def(DefKind::Method, _) | Res::Def(DefKind::AssocConst, _) => true,
82 Res::Def(DefKind::AssocTy, _) => false,
83 Res::Def(DefKind::Variant, _) => return handle_variant(cx, res),
0731742a 84 // Not a trait item; just return what we found.
416331ca 85 Res::PrimTy(..) => return Ok((res, Some(path_str.to_owned()))),
dc9dc135 86 _ => return Ok((res, None))
b7449926
XL
87 };
88
532ac7d7 89 if value != (ns == ValueNS) {
b7449926
XL
90 return Err(())
91 }
532ac7d7 92 } else if let Some(prim) = is_primitive(path_str, ns) {
b7449926
XL
93 return Ok((prim, Some(path_str.to_owned())))
94 } else {
95 // If resolution failed, it may still be a method
96 // because methods are not handled by the resolver
0731742a 97 // If so, bail when we're not looking for a value.
532ac7d7 98 if ns != ValueNS {
b7449926
XL
99 return Err(())
100 }
101 }
102
0731742a 103 // Try looking for methods and associated items.
b7449926
XL
104 let mut split = path_str.rsplitn(2, "::");
105 let item_name = if let Some(first) = split.next() {
48663c56 106 Symbol::intern(first)
b7449926
XL
107 } else {
108 return Err(())
109 };
110
111 let mut path = if let Some(second) = split.next() {
112 second.to_owned()
113 } else {
114 return Err(())
115 };
116
117 if path == "self" || path == "Self" {
118 if let Some(name) = current_item.as_ref() {
119 path = name.clone();
120 }
121 }
532ac7d7 122 if let Some(prim) = is_primitive(&path, TypeNS) {
9fa01778
XL
123 let did = primitive_impl(cx, &path).ok_or(())?;
124 return cx.tcx.associated_items(did)
125 .find(|item| item.ident.name == item_name)
126 .and_then(|item| match item.kind {
dc9dc135 127 ty::AssocKind::Method => Some("method"),
9fa01778
XL
128 _ => None,
129 })
130 .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name))))
131 .ok_or(());
132 }
b7449926 133
416331ca
XL
134 let (_, ty_res) = cx.enter_resolver(|resolver| {
135 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
136 })?;
137 if let Res::Err = ty_res {
138 return Err(());
139 }
dc9dc135
XL
140 let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
141 match ty_res {
48663c56
XL
142 Res::Def(DefKind::Struct, did)
143 | Res::Def(DefKind::Union, did)
144 | Res::Def(DefKind::Enum, did)
145 | Res::Def(DefKind::TyAlias, did) => {
b7449926
XL
146 let item = cx.tcx.inherent_impls(did)
147 .iter()
148 .flat_map(|imp| cx.tcx.associated_items(*imp))
149 .find(|item| item.ident.name == item_name);
150 if let Some(item) = item {
151 let out = match item.kind {
dc9dc135
XL
152 ty::AssocKind::Method if ns == ValueNS => "method",
153 ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
b7449926
XL
154 _ => return Err(())
155 };
dc9dc135 156 Ok((ty_res, Some(format!("{}.{}", out, item_name))))
b7449926
XL
157 } else {
158 match cx.tcx.type_of(did).sty {
159 ty::Adt(def, _) => {
160 if let Some(item) = if def.is_enum() {
161 def.all_fields().find(|item| item.ident.name == item_name)
162 } else {
163 def.non_enum_variant()
164 .fields
165 .iter()
166 .find(|item| item.ident.name == item_name)
167 } {
dc9dc135 168 Ok((ty_res,
b7449926
XL
169 Some(format!("{}.{}",
170 if def.is_enum() {
171 "variant"
172 } else {
173 "structfield"
174 },
175 item.ident))))
176 } else {
177 Err(())
178 }
179 }
180 _ => Err(()),
181 }
182 }
183 }
48663c56 184 Res::Def(DefKind::Trait, did) => {
b7449926
XL
185 let item = cx.tcx.associated_item_def_ids(did).iter()
186 .map(|item| cx.tcx.associated_item(*item))
187 .find(|item| item.ident.name == item_name);
188 if let Some(item) = item {
189 let kind = match item.kind {
dc9dc135
XL
190 ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
191 ty::AssocKind::Type if ns == TypeNS => "associatedtype",
192 ty::AssocKind::Method if ns == ValueNS => {
b7449926
XL
193 if item.defaultness.has_value() {
194 "method"
195 } else {
196 "tymethod"
197 }
198 }
199 _ => return Err(())
200 };
201
dc9dc135 202 Ok((ty_res, Some(format!("{}.{}", kind, item_name))))
b7449926
XL
203 } else {
204 Err(())
205 }
206 }
207 _ => Err(())
208 }
209 } else {
48663c56 210 debug!("attempting to resolve item without parent module: {}", path_str);
b7449926
XL
211 Err(())
212 }
213 }
214}
215
532ac7d7 216impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
b7449926 217 fn fold_item(&mut self, mut item: Item) -> Option<Item> {
532ac7d7
XL
218 let item_hir_id = if item.is_mod() {
219 if let Some(id) = self.cx.tcx.hir().as_local_hir_id(item.def_id) {
b7449926
XL
220 Some(id)
221 } else {
222 debug!("attempting to fold on a non-local item: {:?}", item);
223 return self.fold_item_recur(item);
224 }
225 } else {
226 None
227 };
228
0731742a 229 // FIXME: get the resolver to work with non-local resolve scopes.
48663c56 230 let parent_node = self.cx.as_local_hir_id(item.def_id).and_then(|hir_id| {
0bf4aa26 231 // FIXME: this fails hard for impls in non-module scope, but is necessary for the
0731742a 232 // current `resolve()` implementation.
48663c56
XL
233 match self.cx.tcx.hir().get_module_parent_node(hir_id) {
234 id if id != hir_id => Some(id),
0bf4aa26
XL
235 _ => None,
236 }
237 });
238
239 if parent_node.is_some() {
240 debug!("got parent node for {} {:?}, id {:?}", item.type_(), item.name, item.def_id);
241 }
242
b7449926
XL
243 let current_item = match item.inner {
244 ModuleItem(..) => {
245 if item.attrs.inner_docs {
532ac7d7 246 if item_hir_id.unwrap() != hir::CRATE_HIR_ID {
b7449926
XL
247 item.name.clone()
248 } else {
249 None
250 }
251 } else {
0bf4aa26 252 match parent_node.or(self.mod_ids.last().cloned()) {
48663c56 253 Some(parent) if parent != hir::CRATE_HIR_ID => {
0731742a 254 // FIXME: can we pull the parent module's name from elsewhere?
dc9dc135 255 Some(self.cx.tcx.hir().name(parent).to_string())
b7449926
XL
256 }
257 _ => None,
258 }
259 }
260 }
261 ImplItem(Impl { ref for_, .. }) => {
262 for_.def_id().map(|did| self.cx.tcx.item_name(did).to_string())
263 }
0731742a 264 // we don't display docs on `extern crate` items anyway, so don't process them.
b7449926
XL
265 ExternCrateItem(..) => return self.fold_item_recur(item),
266 ImportItem(Import::Simple(ref name, ..)) => Some(name.clone()),
267 MacroItem(..) => None,
268 _ => item.name.clone(),
269 };
270
271 if item.is_mod() && item.attrs.inner_docs {
48663c56 272 self.mod_ids.push(item_hir_id.unwrap());
b7449926
XL
273 }
274
275 let cx = self.cx;
276 let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);
277
a1dfa0c6 278 look_for_tests(&cx, &dox, &item, true);
0bf4aa26 279
b7449926 280 for (ori_link, link_range) in markdown_links(&dox) {
0731742a 281 // Bail early for real links.
b7449926
XL
282 if ori_link.contains('/') {
283 continue;
284 }
532ac7d7
XL
285
286 // [] is mostly likely not supposed to be a link
287 if ori_link.is_empty() {
288 continue;
289 }
290
b7449926 291 let link = ori_link.replace("`", "");
48663c56 292 let (res, fragment) = {
532ac7d7 293 let mut kind = None;
b7449926
XL
294 let path_str = if let Some(prefix) =
295 ["struct@", "enum@", "type@",
296 "trait@", "union@"].iter()
297 .find(|p| link.starts_with(**p)) {
532ac7d7 298 kind = Some(TypeNS);
0731742a 299 link.trim_start_matches(prefix)
b7449926
XL
300 } else if let Some(prefix) =
301 ["const@", "static@",
302 "value@", "function@", "mod@",
303 "fn@", "module@", "method@"]
304 .iter().find(|p| link.starts_with(**p)) {
532ac7d7 305 kind = Some(ValueNS);
0731742a 306 link.trim_start_matches(prefix)
b7449926 307 } else if link.ends_with("()") {
532ac7d7 308 kind = Some(ValueNS);
0731742a 309 link.trim_end_matches("()")
b7449926 310 } else if link.starts_with("macro@") {
532ac7d7 311 kind = Some(MacroNS);
0731742a 312 link.trim_start_matches("macro@")
b7449926 313 } else if link.ends_with('!') {
532ac7d7 314 kind = Some(MacroNS);
0731742a 315 link.trim_end_matches('!')
b7449926
XL
316 } else {
317 &link[..]
318 }.trim();
319
320 if path_str.contains(|ch: char| !(ch.is_alphanumeric() ||
321 ch == ':' || ch == '_')) {
322 continue;
323 }
324
325 match kind {
532ac7d7 326 Some(ns @ ValueNS) => {
48663c56
XL
327 if let Ok(res) = self.resolve(path_str, ns, &current_item, parent_node) {
328 res
b7449926 329 } else {
48663c56 330 resolution_failure(cx, &item, path_str, &dox, link_range);
0731742a 331 // This could just be a normal link or a broken link
b7449926 332 // we could potentially check if something is
0731742a 333 // "intra-doc-link-like" and warn in that case.
b7449926
XL
334 continue;
335 }
336 }
532ac7d7 337 Some(ns @ TypeNS) => {
48663c56
XL
338 if let Ok(res) = self.resolve(path_str, ns, &current_item, parent_node) {
339 res
b7449926 340 } else {
48663c56 341 resolution_failure(cx, &item, path_str, &dox, link_range);
0731742a 342 // This could just be a normal link.
b7449926
XL
343 continue;
344 }
345 }
532ac7d7 346 None => {
0731742a 347 // Try everything!
532ac7d7 348 let candidates = PerNS {
48663c56 349 macro_ns: macro_resolve(cx, path_str).map(|res| (res, None)),
532ac7d7
XL
350 type_ns: self
351 .resolve(path_str, TypeNS, &current_item, parent_node)
352 .ok(),
353 value_ns: self
354 .resolve(path_str, ValueNS, &current_item, parent_node)
355 .ok()
48663c56 356 .and_then(|(res, fragment)| {
532ac7d7 357 // Constructors are picked up in the type namespace.
48663c56
XL
358 match res {
359 Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None,
360 _ => Some((res, fragment))
532ac7d7
XL
361 }
362 }),
363 };
364
365 if candidates.is_empty() {
48663c56 366 resolution_failure(cx, &item, path_str, &dox, link_range);
b7449926
XL
367 // this could just be a normal link
368 continue;
369 }
532ac7d7
XL
370
371 let is_unambiguous = candidates.clone().present_items().count() == 1;
372 if is_unambiguous {
373 candidates.present_items().next().unwrap()
374 } else {
375 ambiguity_error(
376 cx,
48663c56 377 &item,
532ac7d7
XL
378 path_str,
379 &dox,
380 link_range,
48663c56 381 candidates.map(|candidate| candidate.map(|(res, _)| res)),
532ac7d7
XL
382 );
383 continue;
384 }
b7449926 385 }
532ac7d7 386 Some(MacroNS) => {
48663c56
XL
387 if let Some(res) = macro_resolve(cx, path_str) {
388 (res, None)
b7449926 389 } else {
48663c56 390 resolution_failure(cx, &item, path_str, &dox, link_range);
b7449926
XL
391 continue
392 }
393 }
394 }
395 };
396
48663c56 397 if let Res::PrimTy(_) = res {
b7449926
XL
398 item.attrs.links.push((ori_link, None, fragment));
399 } else {
48663c56 400 let id = register_res(cx, res);
b7449926
XL
401 item.attrs.links.push((ori_link, Some(id), fragment));
402 }
403 }
404
405 if item.is_mod() && !item.attrs.inner_docs {
48663c56 406 self.mod_ids.push(item_hir_id.unwrap());
b7449926
XL
407 }
408
409 if item.is_mod() {
410 let ret = self.fold_item_recur(item);
411
412 self.mod_ids.pop();
413
414 ret
415 } else {
416 self.fold_item_recur(item)
417 }
418 }
48663c56
XL
419
420 // FIXME: if we can resolve intra-doc links from other crates, we can use the stock
421 // `fold_crate`, but until then we should avoid scanning `krate.external_traits` since those
422 // will never resolve properly
423 fn fold_crate(&mut self, mut c: Crate) -> Crate {
424 c.module = c.module.take().and_then(|module| self.fold_item(module));
425
426 c
427 }
b7449926
XL
428}
429
9fa01778 430/// Resolves a string as a macro.
48663c56 431fn macro_resolve(cx: &DocContext<'_>, path_str: &str) -> Option<Res> {
416331ca 432 let path = ast::Path::from_ident(Ident::from_str(path_str));
532ac7d7 433 cx.enter_resolver(|resolver| {
416331ca 434 if let Ok((Some(ext), res)) = resolver.resolve_macro_path(
e1599b0c 435 &path, None, &ParentScope::module(resolver.graph_root), false, false
416331ca
XL
436 ) {
437 if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
438 return Some(res.map_id(|_| panic!("unexpected id")));
9fa01778 439 }
b7449926 440 }
48663c56
XL
441 if let Some(res) = resolver.all_macros.get(&Symbol::intern(path_str)) {
442 return Some(res.map_id(|_| panic!("unexpected id")));
532ac7d7
XL
443 }
444 None
445 })
b7449926
XL
446}
447
0731742a
XL
448/// Reports a resolution failure diagnostic.
449///
9fa01778
XL
450/// If we cannot find the exact source span of the resolution failure, we use the span of the
451/// documentation attributes themselves. This is a little heavy-handed, so we display the markdown
452/// line containing the failure as a note as well.
b7449926 453fn resolution_failure(
532ac7d7 454 cx: &DocContext<'_>,
48663c56 455 item: &Item,
b7449926
XL
456 path_str: &str,
457 dox: &str,
458 link_range: Option<Range<usize>>,
459) {
48663c56
XL
460 let hir_id = match cx.as_local_hir_id(item.def_id) {
461 Some(hir_id) => hir_id,
462 None => {
463 // If non-local, no need to check anything.
464 return;
465 }
466 };
467 let attrs = &item.attrs;
416331ca 468 let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
b7449926 469
532ac7d7 470 let mut diag = cx.tcx.struct_span_lint_hir(
9fa01778 471 lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
48663c56 472 hir_id,
9fa01778
XL
473 sp,
474 &format!("`[{}]` cannot be resolved, ignoring it...", path_str),
475 );
476 if let Some(link_range) = link_range {
477 if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
478 diag.set_span(sp);
b7449926
XL
479 diag.span_label(sp, "cannot be resolved, ignoring");
480 } else {
0731742a
XL
481 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
482 // ^ ~~~~
483 // | link_range
484 // last_new_line_offset
b7449926
XL
485 let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
486 let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
487
0731742a 488 // Print the line containing the `link_range` and manually mark it with '^'s.
b7449926
XL
489 diag.note(&format!(
490 "the link appears in this line:\n\n{line}\n\
491 {indicator: <before$}{indicator:^<found$}",
492 line=line,
493 indicator="",
494 before=link_range.start - last_new_line_offset,
495 found=link_range.len(),
496 ));
497 }
b7449926
XL
498 };
499 diag.help("to escape `[` and `]` characters, just add '\\' before them like \
500 `\\[` or `\\]`");
501 diag.emit();
502}
503
532ac7d7
XL
504fn ambiguity_error(
505 cx: &DocContext<'_>,
48663c56 506 item: &Item,
532ac7d7
XL
507 path_str: &str,
508 dox: &str,
509 link_range: Option<Range<usize>>,
48663c56 510 candidates: PerNS<Option<Res>>,
532ac7d7 511) {
48663c56
XL
512 let hir_id = match cx.as_local_hir_id(item.def_id) {
513 Some(hir_id) => hir_id,
514 None => {
515 // If non-local, no need to check anything.
516 return;
517 }
518 };
519 let attrs = &item.attrs;
416331ca 520 let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
b7449926 521
532ac7d7
XL
522 let mut msg = format!("`{}` is ", path_str);
523
524 let candidates = [TypeNS, ValueNS, MacroNS].iter().filter_map(|&ns| {
48663c56 525 candidates[ns].map(|res| (res, ns))
532ac7d7
XL
526 }).collect::<Vec<_>>();
527 match candidates.as_slice() {
528 [(first_def, _), (second_def, _)] => {
529 msg += &format!(
530 "both {} {} and {} {}",
531 first_def.article(),
48663c56 532 first_def.descr(),
532ac7d7 533 second_def.article(),
48663c56 534 second_def.descr(),
532ac7d7
XL
535 );
536 }
537 _ => {
538 let mut candidates = candidates.iter().peekable();
48663c56 539 while let Some((res, _)) = candidates.next() {
532ac7d7 540 if candidates.peek().is_some() {
48663c56 541 msg += &format!("{} {}, ", res.article(), res.descr());
532ac7d7 542 } else {
48663c56 543 msg += &format!("and {} {}", res.article(), res.descr());
532ac7d7
XL
544 }
545 }
546 }
b7449926 547 }
b7449926 548
532ac7d7
XL
549 let mut diag = cx.tcx.struct_span_lint_hir(
550 lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
48663c56 551 hir_id,
532ac7d7
XL
552 sp,
553 &msg,
554 );
555
556 if let Some(link_range) = link_range {
557 if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
558 diag.set_span(sp);
559 diag.span_label(sp, "ambiguous link");
560
48663c56
XL
561 for (res, ns) in candidates {
562 let (action, mut suggestion) = match res {
563 Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => {
532ac7d7
XL
564 ("add parentheses", format!("{}()", path_str))
565 }
48663c56 566 Res::Def(DefKind::Macro(..), _) => {
532ac7d7
XL
567 ("add an exclamation mark", format!("{}!", path_str))
568 }
569 _ => {
48663c56
XL
570 let type_ = match (res, ns) {
571 (Res::Def(DefKind::Const, _), _) => "const",
572 (Res::Def(DefKind::Static, _), _) => "static",
573 (Res::Def(DefKind::Struct, _), _) => "struct",
574 (Res::Def(DefKind::Enum, _), _) => "enum",
575 (Res::Def(DefKind::Union, _), _) => "union",
576 (Res::Def(DefKind::Trait, _), _) => "trait",
577 (Res::Def(DefKind::Mod, _), _) => "module",
532ac7d7
XL
578 (_, TypeNS) => "type",
579 (_, ValueNS) => "value",
580 (_, MacroNS) => "macro",
581 };
582
583 // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
584 ("prefix with the item type", format!("{}@{}", type_, path_str))
585 }
586 };
587
588 if dox.bytes().nth(link_range.start) == Some(b'`') {
589 suggestion = format!("`{}`", suggestion);
590 }
591
592 diag.span_suggestion(
593 sp,
48663c56 594 &format!("to link to the {}, {}", res.descr(), action),
532ac7d7
XL
595 suggestion,
596 Applicability::MaybeIncorrect,
597 );
598 }
599 } else {
600 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
601 // ^ ~~~~
602 // | link_range
603 // last_new_line_offset
604 let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
605 let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
606
607 // Print the line containing the `link_range` and manually mark it with '^'s.
608 diag.note(&format!(
609 "the link appears in this line:\n\n{line}\n\
610 {indicator: <before$}{indicator:^<found$}",
611 line=line,
612 indicator="",
613 before=link_range.start - last_new_line_offset,
614 found=link_range.len(),
615 ));
616 }
617 }
618
619 diag.emit();
b7449926
XL
620}
621
48663c56
XL
622/// Given an enum variant's res, return the res of its enum and the associated fragment.
623fn handle_variant(cx: &DocContext<'_>, res: Res) -> Result<(Res, Option<String>), ()> {
b7449926
XL
624 use rustc::ty::DefIdTree;
625
48663c56 626 let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) {
b7449926
XL
627 parent
628 } else {
629 return Err(())
630 };
48663c56
XL
631 let parent_def = Res::Def(DefKind::Enum, parent);
632 let variant = cx.tcx.expect_variant_res(res);
0731742a 633 Ok((parent_def, Some(format!("{}.v", variant.ident.name))))
b7449926
XL
634}
635
48663c56
XL
636const PRIMITIVES: &[(&str, Res)] = &[
637 ("u8", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U8))),
638 ("u16", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U16))),
639 ("u32", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U32))),
640 ("u64", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U64))),
641 ("u128", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U128))),
642 ("usize", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::Usize))),
643 ("i8", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I8))),
644 ("i16", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I16))),
645 ("i32", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I32))),
646 ("i64", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I64))),
647 ("i128", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I128))),
648 ("isize", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::Isize))),
649 ("f32", Res::PrimTy(hir::PrimTy::Float(syntax::ast::FloatTy::F32))),
650 ("f64", Res::PrimTy(hir::PrimTy::Float(syntax::ast::FloatTy::F64))),
651 ("str", Res::PrimTy(hir::PrimTy::Str)),
652 ("bool", Res::PrimTy(hir::PrimTy::Bool)),
653 ("char", Res::PrimTy(hir::PrimTy::Char)),
b7449926
XL
654];
655
48663c56 656fn is_primitive(path_str: &str, ns: Namespace) -> Option<Res> {
532ac7d7 657 if ns == TypeNS {
b7449926 658 PRIMITIVES.iter().find(|x| x.0 == path_str).map(|x| x.1)
532ac7d7
XL
659 } else {
660 None
b7449926
XL
661 }
662}
9fa01778 663
532ac7d7 664fn primitive_impl(cx: &DocContext<'_>, path_str: &str) -> Option<DefId> {
9fa01778
XL
665 let tcx = cx.tcx;
666 match path_str {
667 "u8" => tcx.lang_items().u8_impl(),
668 "u16" => tcx.lang_items().u16_impl(),
669 "u32" => tcx.lang_items().u32_impl(),
670 "u64" => tcx.lang_items().u64_impl(),
671 "u128" => tcx.lang_items().u128_impl(),
672 "usize" => tcx.lang_items().usize_impl(),
673 "i8" => tcx.lang_items().i8_impl(),
674 "i16" => tcx.lang_items().i16_impl(),
675 "i32" => tcx.lang_items().i32_impl(),
676 "i64" => tcx.lang_items().i64_impl(),
677 "i128" => tcx.lang_items().i128_impl(),
678 "isize" => tcx.lang_items().isize_impl(),
679 "f32" => tcx.lang_items().f32_impl(),
680 "f64" => tcx.lang_items().f64_impl(),
681 "str" => tcx.lang_items().str_impl(),
e1599b0c 682 "bool" => tcx.lang_items().bool_impl(),
9fa01778
XL
683 "char" => tcx.lang_items().char_impl(),
684 _ => None,
685 }
686}