]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/render/span_map.rs
remove find_use_placement
[rust.git] / src / librustdoc / html / render / span_map.rs
1 use crate::clean::{self, PrimitiveType};
2 use crate::html::sources;
3
4 use rustc_data_structures::fx::FxHashMap;
5 use rustc_hir::def::{DefKind, Res};
6 use rustc_hir::def_id::DefId;
7 use rustc_hir::intravisit::{self, Visitor};
8 use rustc_hir::{ExprKind, GenericParam, GenericParamKind, HirId, Mod, Node};
9 use rustc_middle::hir::nested_filter;
10 use rustc_middle::ty::TyCtxt;
11 use rustc_span::Span;
12
13 use std::path::{Path, PathBuf};
14
15 /// This enum allows us to store two different kinds of information:
16 ///
17 /// In case the `span` definition comes from the same crate, we can simply get the `span` and use
18 /// it as is.
19 ///
20 /// Otherwise, we store the definition `DefId` and will generate a link to the documentation page
21 /// instead of the source code directly.
22 #[derive(Debug)]
23 crate enum LinkFromSrc {
24     Local(clean::Span),
25     External(DefId),
26     Primitive(PrimitiveType),
27 }
28
29 /// This function will do at most two things:
30 ///
31 /// 1. Generate a `span` correspondance map which links an item `span` to its definition `span`.
32 /// 2. Collect the source code files.
33 ///
34 /// It returns the `krate`, the source code files and the `span` correspondance map.
35 ///
36 /// Note about the `span` correspondance map: the keys are actually `(lo, hi)` of `span`s. We don't
37 /// need the `span` context later on, only their position, so instead of keep a whole `Span`, we
38 /// only keep the `lo` and `hi`.
39 crate fn collect_spans_and_sources(
40     tcx: TyCtxt<'_>,
41     krate: &clean::Crate,
42     src_root: &Path,
43     include_sources: bool,
44     generate_link_to_definition: bool,
45 ) -> (FxHashMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) {
46     let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() };
47
48     if include_sources {
49         if generate_link_to_definition {
50             tcx.hir().walk_toplevel_module(&mut visitor);
51         }
52         let sources = sources::collect_local_sources(tcx, src_root, &krate);
53         (sources, visitor.matches)
54     } else {
55         (Default::default(), Default::default())
56     }
57 }
58
59 struct SpanMapVisitor<'tcx> {
60     crate tcx: TyCtxt<'tcx>,
61     crate matches: FxHashMap<Span, LinkFromSrc>,
62 }
63
64 impl<'tcx> SpanMapVisitor<'tcx> {
65     /// This function is where we handle `hir::Path` elements and add them into the "span map".
66     fn handle_path(&mut self, path: &rustc_hir::Path<'_>, path_span: Option<Span>) {
67         let info = match path.res {
68             // FIXME: For now, we only handle `DefKind` if it's not `DefKind::TyParam` or
69             // `DefKind::Macro`. Would be nice to support them too alongside the other `DefKind`
70             // (such as primitive types!).
71             Res::Def(kind, def_id) if kind != DefKind::TyParam => {
72                 if matches!(kind, DefKind::Macro(_)) {
73                     return;
74                 }
75                 Some(def_id)
76             }
77             Res::Local(_) => None,
78             Res::PrimTy(p) => {
79                 // FIXME: Doesn't handle "path-like" primitives like arrays or tuples.
80                 let span = path_span.unwrap_or(path.span);
81                 self.matches.insert(span, LinkFromSrc::Primitive(PrimitiveType::from(p)));
82                 return;
83             }
84             Res::Err => return,
85             _ => return,
86         };
87         if let Some(span) = self.tcx.hir().res_span(path.res) {
88             self.matches
89                 .insert(path_span.unwrap_or(path.span), LinkFromSrc::Local(clean::Span::new(span)));
90         } else if let Some(def_id) = info {
91             self.matches.insert(path_span.unwrap_or(path.span), LinkFromSrc::External(def_id));
92         }
93     }
94 }
95
96 impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
97     type NestedFilter = nested_filter::All;
98
99     fn nested_visit_map(&mut self) -> Self::Map {
100         self.tcx.hir()
101     }
102
103     fn visit_generic_param(&mut self, p: &'tcx GenericParam<'tcx>) {
104         if !matches!(p.kind, GenericParamKind::Type { .. }) {
105             return;
106         }
107         for bound in p.bounds {
108             if let Some(trait_ref) = bound.trait_ref() {
109                 self.handle_path(trait_ref.path, None);
110             }
111         }
112     }
113
114     fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
115         self.handle_path(path, None);
116         intravisit::walk_path(self, path);
117     }
118
119     fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) {
120         // To make the difference between "mod foo {}" and "mod foo;". In case we "import" another
121         // file, we want to link to it. Otherwise no need to create a link.
122         if !span.overlaps(m.spans.inner_span) {
123             // Now that we confirmed it's a file import, we want to get the span for the module
124             // name only and not all the "mod foo;".
125             if let Some(Node::Item(item)) = self.tcx.hir().find(id) {
126                 self.matches.insert(
127                     item.ident.span,
128                     LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)),
129                 );
130             }
131         }
132         intravisit::walk_mod(self, m, id);
133     }
134
135     fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
136         if let ExprKind::MethodCall(segment, ..) = expr.kind {
137             if let Some(hir_id) = segment.hir_id {
138                 let hir = self.tcx.hir();
139                 let body_id = hir.enclosing_body_owner(hir_id);
140                 // FIXME: this is showing error messages for parts of the code that are not
141                 // compiled (because of cfg)!
142                 //
143                 // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352
144                 let typeck_results = self.tcx.typeck_body(
145                     hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"),
146                 );
147                 if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) {
148                     self.matches.insert(
149                         segment.ident.span,
150                         match hir.span_if_local(def_id) {
151                             Some(span) => LinkFromSrc::Local(clean::Span::new(span)),
152                             None => LinkFromSrc::External(def_id),
153                         },
154                     );
155                 }
156             }
157         }
158         intravisit::walk_expr(self, expr);
159     }
160
161     fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) {
162         self.handle_path(path, None);
163         intravisit::walk_use(self, path, id);
164     }
165 }