]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/html/render/span_map.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / src / librustdoc / html / render / span_map.rs
CommitLineData
c295e0f8 1use crate::clean::{self, PrimitiveType};
94222f64
XL
2use crate::html::sources;
3
4use rustc_data_structures::fx::FxHashMap;
5use rustc_hir::def::{DefKind, Res};
6use rustc_hir::def_id::DefId;
5099ac24 7use rustc_hir::intravisit::{self, Visitor};
04454e1e 8use rustc_hir::{ExprKind, HirId, Mod, Node};
5099ac24 9use rustc_middle::hir::nested_filter;
94222f64 10use rustc_middle::ty::TyCtxt;
923072b8
FG
11use rustc_span::hygiene::MacroKind;
12use rustc_span::{BytePos, ExpnKind, Span};
94222f64
XL
13
14use std::path::{Path, PathBuf};
15
16/// This enum allows us to store two different kinds of information:
17///
18/// In case the `span` definition comes from the same crate, we can simply get the `span` and use
19/// it as is.
20///
21/// Otherwise, we store the definition `DefId` and will generate a link to the documentation page
22/// instead of the source code directly.
23#[derive(Debug)]
923072b8 24pub(crate) enum LinkFromSrc {
94222f64
XL
25 Local(clean::Span),
26 External(DefId),
c295e0f8 27 Primitive(PrimitiveType),
94222f64
XL
28}
29
30/// This function will do at most two things:
31///
32/// 1. Generate a `span` correspondance map which links an item `span` to its definition `span`.
33/// 2. Collect the source code files.
34///
35/// It returns the `krate`, the source code files and the `span` correspondance map.
36///
37/// Note about the `span` correspondance map: the keys are actually `(lo, hi)` of `span`s. We don't
38/// need the `span` context later on, only their position, so instead of keep a whole `Span`, we
39/// only keep the `lo` and `hi`.
923072b8 40pub(crate) fn collect_spans_and_sources(
94222f64 41 tcx: TyCtxt<'_>,
3c0e092e 42 krate: &clean::Crate,
94222f64
XL
43 src_root: &Path,
44 include_sources: bool,
45 generate_link_to_definition: bool,
3c0e092e 46) -> (FxHashMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) {
94222f64
XL
47 let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() };
48
49 if include_sources {
50 if generate_link_to_definition {
c295e0f8 51 tcx.hir().walk_toplevel_module(&mut visitor);
94222f64 52 }
923072b8 53 let sources = sources::collect_local_sources(tcx, src_root, krate);
3c0e092e 54 (sources, visitor.matches)
94222f64 55 } else {
3c0e092e 56 (Default::default(), Default::default())
94222f64
XL
57 }
58}
59
60struct SpanMapVisitor<'tcx> {
923072b8
FG
61 pub(crate) tcx: TyCtxt<'tcx>,
62 pub(crate) matches: FxHashMap<Span, LinkFromSrc>,
94222f64
XL
63}
64
65impl<'tcx> SpanMapVisitor<'tcx> {
66 /// This function is where we handle `hir::Path` elements and add them into the "span map".
923072b8 67 fn handle_path(&mut self, path: &rustc_hir::Path<'_>) {
94222f64 68 let info = match path.res {
923072b8
FG
69 // FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`.
70 // Would be nice to support them too alongside the other `DefKind`
94222f64 71 // (such as primitive types!).
923072b8 72 Res::Def(kind, def_id) if kind != DefKind::TyParam => Some(def_id),
94222f64 73 Res::Local(_) => None,
c295e0f8
XL
74 Res::PrimTy(p) => {
75 // FIXME: Doesn't handle "path-like" primitives like arrays or tuples.
923072b8 76 self.matches.insert(path.span, LinkFromSrc::Primitive(PrimitiveType::from(p)));
c295e0f8
XL
77 return;
78 }
94222f64
XL
79 Res::Err => return,
80 _ => return,
81 };
82 if let Some(span) = self.tcx.hir().res_span(path.res) {
923072b8 83 self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span)));
c295e0f8 84 } else if let Some(def_id) = info {
923072b8
FG
85 self.matches.insert(path.span, LinkFromSrc::External(def_id));
86 }
87 }
88
89 /// Adds the macro call into the span map. Returns `true` if the `span` was inside a macro
90 /// expansion, whether or not it was added to the span map.
91 ///
92 /// The idea for the macro support is to check if the current `Span` comes from expansion. If
93 /// so, we loop until we find the macro definition by using `outer_expn_data` in a loop.
94 /// Finally, we get the information about the macro itself (`span` if "local", `DefId`
95 /// otherwise) and store it inside the span map.
96 fn handle_macro(&mut self, span: Span) -> bool {
97 if !span.from_expansion() {
98 return false;
99 }
100 // So if the `span` comes from a macro expansion, we need to get the original
101 // macro's `DefId`.
102 let mut data = span.ctxt().outer_expn_data();
103 let mut call_site = data.call_site;
104 // Macros can expand to code containing macros, which will in turn be expanded, etc.
105 // So the idea here is to "go up" until we're back to code that was generated from
106 // macro expansion so that we can get the `DefId` of the original macro that was at the
107 // origin of this expansion.
108 while call_site.from_expansion() {
109 data = call_site.ctxt().outer_expn_data();
110 call_site = data.call_site;
94222f64 111 }
923072b8
FG
112
113 let macro_name = match data.kind {
114 ExpnKind::Macro(MacroKind::Bang, macro_name) => macro_name,
115 // Even though we don't handle this kind of macro, this `data` still comes from
116 // expansion so we return `true` so we don't go any deeper in this code.
117 _ => return true,
118 };
119 let link_from_src = match data.macro_def_id {
120 Some(macro_def_id) if macro_def_id.is_local() => {
121 LinkFromSrc::Local(clean::Span::new(data.def_site))
122 }
123 Some(macro_def_id) => LinkFromSrc::External(macro_def_id),
124 None => return true,
125 };
126 let new_span = data.call_site;
127 let macro_name = macro_name.as_str();
128 // The "call_site" includes the whole macro with its "arguments". We only want
129 // the macro name.
130 let new_span = new_span.with_hi(new_span.lo() + BytePos(macro_name.len() as u32));
131 self.matches.insert(new_span, link_from_src);
132 true
94222f64
XL
133 }
134}
135
a2a8927a 136impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
5099ac24 137 type NestedFilter = nested_filter::All;
94222f64 138
5099ac24
FG
139 fn nested_visit_map(&mut self) -> Self::Map {
140 self.tcx.hir()
94222f64
XL
141 }
142
94222f64 143 fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
923072b8
FG
144 if self.handle_macro(path.span) {
145 return;
146 }
147 self.handle_path(path);
94222f64
XL
148 intravisit::walk_path(self, path);
149 }
150
151 fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) {
152 // To make the difference between "mod foo {}" and "mod foo;". In case we "import" another
153 // file, we want to link to it. Otherwise no need to create a link.
04454e1e 154 if !span.overlaps(m.spans.inner_span) {
94222f64
XL
155 // Now that we confirmed it's a file import, we want to get the span for the module
156 // name only and not all the "mod foo;".
3c0e092e 157 if let Some(Node::Item(item)) = self.tcx.hir().find(id) {
04454e1e
FG
158 self.matches.insert(
159 item.ident.span,
160 LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)),
161 );
94222f64
XL
162 }
163 }
164 intravisit::walk_mod(self, m, id);
165 }
166
167 fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
5099ac24 168 if let ExprKind::MethodCall(segment, ..) = expr.kind {
3c0e092e
XL
169 if let Some(hir_id) = segment.hir_id {
170 let hir = self.tcx.hir();
171 let body_id = hir.enclosing_body_owner(hir_id);
5099ac24
FG
172 // FIXME: this is showing error messages for parts of the code that are not
173 // compiled (because of cfg)!
174 //
175 // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352
176 let typeck_results = self.tcx.typeck_body(
177 hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"),
178 );
3c0e092e
XL
179 if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) {
180 self.matches.insert(
5099ac24 181 segment.ident.span,
3c0e092e
XL
182 match hir.span_if_local(def_id) {
183 Some(span) => LinkFromSrc::Local(clean::Span::new(span)),
184 None => LinkFromSrc::External(def_id),
185 },
186 );
94222f64
XL
187 }
188 }
923072b8
FG
189 } else if self.handle_macro(expr.span) {
190 // We don't want to go deeper into the macro.
191 return;
94222f64
XL
192 }
193 intravisit::walk_expr(self, expr);
194 }
195
196 fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) {
923072b8
FG
197 if self.handle_macro(path.span) {
198 return;
199 }
200 self.handle_path(path);
94222f64
XL
201 intravisit::walk_use(self, path, id);
202 }
203}