]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_driver/src/pretty.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / compiler / rustc_driver / src / pretty.rs
1 //! The various pretty-printing routines.
2
3 use rustc_ast as ast;
4 use rustc_ast_pretty::pprust;
5 use rustc_errors::ErrorReported;
6 use rustc_hir as hir;
7 use rustc_hir::def_id::LOCAL_CRATE;
8 use rustc_hir_pretty as pprust_hir;
9 use rustc_middle::hir::map as hir_map;
10 use rustc_middle::ty::{self, TyCtxt};
11 use rustc_mir::util::{write_mir_graphviz, write_mir_pretty};
12 use rustc_mir_build::thir;
13 use rustc_session::config::{Input, PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
14 use rustc_session::Session;
15 use rustc_span::symbol::Ident;
16 use rustc_span::FileName;
17
18 use std::cell::Cell;
19 use std::fmt::Write;
20 use std::path::Path;
21
22 pub use self::PpMode::*;
23 pub use self::PpSourceMode::*;
24 use crate::abort_on_err;
25
26 // This slightly awkward construction is to allow for each PpMode to
27 // choose whether it needs to do analyses (which can consume the
28 // Session) and then pass through the session (now attached to the
29 // analysis results) on to the chosen pretty-printer, along with the
30 // `&PpAnn` object.
31 //
32 // Note that since the `&PrinterSupport` is freshly constructed on each
33 // call, it would not make sense to try to attach the lifetime of `self`
34 // to the lifetime of the `&PrinterObject`.
35
36 /// Constructs a `PrinterSupport` object and passes it to `f`.
37 fn call_with_pp_support<'tcx, A, F>(
38 ppmode: &PpSourceMode,
39 sess: &'tcx Session,
40 tcx: Option<TyCtxt<'tcx>>,
41 f: F,
42 ) -> A
43 where
44 F: FnOnce(&dyn PrinterSupport) -> A,
45 {
46 match *ppmode {
47 Normal | EveryBodyLoops | Expanded => {
48 let annotation = NoAnn { sess, tcx };
49 f(&annotation)
50 }
51
52 Identified | ExpandedIdentified => {
53 let annotation = IdentifiedAnnotation { sess, tcx };
54 f(&annotation)
55 }
56 ExpandedHygiene => {
57 let annotation = HygieneAnnotation { sess };
58 f(&annotation)
59 }
60 }
61 }
62 fn call_with_pp_support_hir<A, F>(ppmode: &PpHirMode, tcx: TyCtxt<'_>, f: F) -> A
63 where
64 F: FnOnce(&dyn HirPrinterSupport<'_>, &hir::Crate<'_>) -> A,
65 {
66 match *ppmode {
67 PpHirMode::Normal => {
68 let annotation = NoAnn { sess: tcx.sess, tcx: Some(tcx) };
69 f(&annotation, tcx.hir().krate())
70 }
71
72 PpHirMode::Identified => {
73 let annotation = IdentifiedAnnotation { sess: tcx.sess, tcx: Some(tcx) };
74 f(&annotation, tcx.hir().krate())
75 }
76 PpHirMode::Typed => {
77 abort_on_err(tcx.analysis(LOCAL_CRATE), tcx.sess);
78
79 let annotation = TypedAnnotation { tcx, maybe_typeck_results: Cell::new(None) };
80 tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir().krate()))
81 }
82 }
83 }
84
85 trait PrinterSupport: pprust::PpAnn {
86 /// Provides a uniform interface for re-extracting a reference to a
87 /// `Session` from a value that now owns it.
88 fn sess(&self) -> &Session;
89
90 /// Produces the pretty-print annotation object.
91 ///
92 /// (Rust does not yet support upcasting from a trait object to
93 /// an object for one of its super-traits.)
94 fn pp_ann(&self) -> &dyn pprust::PpAnn;
95 }
96
97 trait HirPrinterSupport<'hir>: pprust_hir::PpAnn {
98 /// Provides a uniform interface for re-extracting a reference to a
99 /// `Session` from a value that now owns it.
100 fn sess(&self) -> &Session;
101
102 /// Provides a uniform interface for re-extracting a reference to an
103 /// `hir_map::Map` from a value that now owns it.
104 fn hir_map(&self) -> Option<hir_map::Map<'hir>>;
105
106 /// Produces the pretty-print annotation object.
107 ///
108 /// (Rust does not yet support upcasting from a trait object to
109 /// an object for one of its super-traits.)
110 fn pp_ann(&self) -> &dyn pprust_hir::PpAnn;
111
112 /// Computes an user-readable representation of a path, if possible.
113 fn node_path(&self, id: hir::HirId) -> Option<String> {
114 self.hir_map().and_then(|map| map.def_path_from_hir_id(id)).map(|path| {
115 path.data.into_iter().map(|elem| elem.data.to_string()).collect::<Vec<_>>().join("::")
116 })
117 }
118 }
119
120 struct NoAnn<'hir> {
121 sess: &'hir Session,
122 tcx: Option<TyCtxt<'hir>>,
123 }
124
125 impl<'hir> PrinterSupport for NoAnn<'hir> {
126 fn sess(&self) -> &Session {
127 self.sess
128 }
129
130 fn pp_ann(&self) -> &dyn pprust::PpAnn {
131 self
132 }
133 }
134
135 impl<'hir> HirPrinterSupport<'hir> for NoAnn<'hir> {
136 fn sess(&self) -> &Session {
137 self.sess
138 }
139
140 fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
141 self.tcx.map(|tcx| tcx.hir())
142 }
143
144 fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
145 self
146 }
147 }
148
149 impl<'hir> pprust::PpAnn for NoAnn<'hir> {}
150 impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> {
151 fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
152 if let Some(tcx) = self.tcx {
153 pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
154 }
155 }
156 }
157
158 struct IdentifiedAnnotation<'hir> {
159 sess: &'hir Session,
160 tcx: Option<TyCtxt<'hir>>,
161 }
162
163 impl<'hir> PrinterSupport for IdentifiedAnnotation<'hir> {
164 fn sess(&self) -> &Session {
165 self.sess
166 }
167
168 fn pp_ann(&self) -> &dyn pprust::PpAnn {
169 self
170 }
171 }
172
173 impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> {
174 fn pre(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
175 if let pprust::AnnNode::Expr(_) = node {
176 s.popen();
177 }
178 }
179 fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
180 match node {
181 pprust::AnnNode::Crate(_) | pprust::AnnNode::Ident(_) | pprust::AnnNode::Name(_) => {}
182
183 pprust::AnnNode::Item(item) => {
184 s.s.space();
185 s.synth_comment(item.id.to_string())
186 }
187 pprust::AnnNode::SubItem(id) => {
188 s.s.space();
189 s.synth_comment(id.to_string())
190 }
191 pprust::AnnNode::Block(blk) => {
192 s.s.space();
193 s.synth_comment(format!("block {}", blk.id))
194 }
195 pprust::AnnNode::Expr(expr) => {
196 s.s.space();
197 s.synth_comment(expr.id.to_string());
198 s.pclose()
199 }
200 pprust::AnnNode::Pat(pat) => {
201 s.s.space();
202 s.synth_comment(format!("pat {}", pat.id));
203 }
204 }
205 }
206 }
207
208 impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> {
209 fn sess(&self) -> &Session {
210 self.sess
211 }
212
213 fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
214 self.tcx.map(|tcx| tcx.hir())
215 }
216
217 fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
218 self
219 }
220 }
221
222 impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> {
223 fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
224 if let Some(ref tcx) = self.tcx {
225 pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
226 }
227 }
228 fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
229 if let pprust_hir::AnnNode::Expr(_) = node {
230 s.popen();
231 }
232 }
233 fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
234 match node {
235 pprust_hir::AnnNode::Name(_) => {}
236 pprust_hir::AnnNode::Item(item) => {
237 s.s.space();
238 s.synth_comment(format!("hir_id: {}", item.hir_id()));
239 }
240 pprust_hir::AnnNode::SubItem(id) => {
241 s.s.space();
242 s.synth_comment(id.to_string());
243 }
244 pprust_hir::AnnNode::Block(blk) => {
245 s.s.space();
246 s.synth_comment(format!("block hir_id: {}", blk.hir_id));
247 }
248 pprust_hir::AnnNode::Expr(expr) => {
249 s.s.space();
250 s.synth_comment(format!("expr hir_id: {}", expr.hir_id));
251 s.pclose();
252 }
253 pprust_hir::AnnNode::Pat(pat) => {
254 s.s.space();
255 s.synth_comment(format!("pat hir_id: {}", pat.hir_id));
256 }
257 pprust_hir::AnnNode::Arm(arm) => {
258 s.s.space();
259 s.synth_comment(format!("arm hir_id: {}", arm.hir_id));
260 }
261 }
262 }
263 }
264
265 struct HygieneAnnotation<'a> {
266 sess: &'a Session,
267 }
268
269 impl<'a> PrinterSupport for HygieneAnnotation<'a> {
270 fn sess(&self) -> &Session {
271 self.sess
272 }
273
274 fn pp_ann(&self) -> &dyn pprust::PpAnn {
275 self
276 }
277 }
278
279 impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
280 fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
281 match node {
282 pprust::AnnNode::Ident(&Ident { name, span }) => {
283 s.s.space();
284 s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt()))
285 }
286 pprust::AnnNode::Name(&name) => {
287 s.s.space();
288 s.synth_comment(name.as_u32().to_string())
289 }
290 pprust::AnnNode::Crate(_) => {
291 s.s.hardbreak();
292 let verbose = self.sess.verbose();
293 s.synth_comment(rustc_span::hygiene::debug_hygiene_data(verbose));
294 s.s.hardbreak_if_not_bol();
295 }
296 _ => {}
297 }
298 }
299 }
300
301 struct TypedAnnotation<'tcx> {
302 tcx: TyCtxt<'tcx>,
303 maybe_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>,
304 }
305
306 impl<'tcx> TypedAnnotation<'tcx> {
307 /// Gets the type-checking results for the current body.
308 /// As this will ICE if called outside bodies, only call when working with
309 /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
310 #[track_caller]
311 fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
312 self.maybe_typeck_results
313 .get()
314 .expect("`TypedAnnotation::typeck_results` called outside of body")
315 }
316 }
317
318 impl<'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'tcx> {
319 fn sess(&self) -> &Session {
320 &self.tcx.sess
321 }
322
323 fn hir_map(&self) -> Option<hir_map::Map<'tcx>> {
324 Some(self.tcx.hir())
325 }
326
327 fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
328 self
329 }
330
331 fn node_path(&self, id: hir::HirId) -> Option<String> {
332 Some(self.tcx.def_path_str(self.tcx.hir().local_def_id(id).to_def_id()))
333 }
334 }
335
336 impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> {
337 fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
338 let old_maybe_typeck_results = self.maybe_typeck_results.get();
339 if let pprust_hir::Nested::Body(id) = nested {
340 self.maybe_typeck_results.set(Some(self.tcx.typeck_body(id)));
341 }
342 let pp_ann = &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>);
343 pprust_hir::PpAnn::nested(pp_ann, state, nested);
344 self.maybe_typeck_results.set(old_maybe_typeck_results);
345 }
346 fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
347 if let pprust_hir::AnnNode::Expr(_) = node {
348 s.popen();
349 }
350 }
351 fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
352 if let pprust_hir::AnnNode::Expr(expr) = node {
353 s.s.space();
354 s.s.word("as");
355 s.s.space();
356 s.s.word(self.typeck_results().expr_ty(expr).to_string());
357 s.pclose();
358 }
359 }
360 }
361
362 fn get_source(input: &Input, sess: &Session) -> (String, FileName) {
363 let src_name = input.source_name();
364 let src = String::clone(
365 &sess
366 .source_map()
367 .get_source_file(&src_name)
368 .expect("get_source_file")
369 .src
370 .as_ref()
371 .expect("src"),
372 );
373 (src, src_name)
374 }
375
376 fn write_or_print(out: &str, ofile: Option<&Path>) {
377 match ofile {
378 None => print!("{}", out),
379 Some(p) => {
380 if let Err(e) = std::fs::write(p, out) {
381 panic!("print-print failed to write {} due to {}", p.display(), e);
382 }
383 }
384 }
385 }
386
387 pub fn print_after_parsing(
388 sess: &Session,
389 input: &Input,
390 krate: &ast::Crate,
391 ppm: PpMode,
392 ofile: Option<&Path>,
393 ) {
394 let (src, src_name) = get_source(input, sess);
395
396 let out = match ppm {
397 Source(s) => {
398 // Silently ignores an identified node.
399 call_with_pp_support(&s, sess, None, move |annotation| {
400 debug!("pretty printing source code {:?}", s);
401 let sess = annotation.sess();
402 let parse = &sess.parse_sess;
403 pprust::print_crate(
404 sess.source_map(),
405 krate,
406 src_name,
407 src,
408 annotation.pp_ann(),
409 false,
410 parse.edition,
411 )
412 })
413 }
414 AstTree(PpAstTreeMode::Normal) => {
415 debug!("pretty printing AST tree");
416 format!("{:#?}", krate)
417 }
418 _ => unreachable!(),
419 };
420
421 write_or_print(&out, ofile);
422 }
423
424 pub fn print_after_hir_lowering<'tcx>(
425 tcx: TyCtxt<'tcx>,
426 input: &Input,
427 krate: &ast::Crate,
428 ppm: PpMode,
429 ofile: Option<&Path>,
430 ) {
431 if ppm.needs_analysis() {
432 abort_on_err(print_with_analysis(tcx, ppm, ofile), tcx.sess);
433 return;
434 }
435
436 let (src, src_name) = get_source(input, tcx.sess);
437
438 let out = match ppm {
439 Source(s) => {
440 // Silently ignores an identified node.
441 call_with_pp_support(&s, tcx.sess, Some(tcx), move |annotation| {
442 debug!("pretty printing source code {:?}", s);
443 let sess = annotation.sess();
444 let parse = &sess.parse_sess;
445 pprust::print_crate(
446 sess.source_map(),
447 krate,
448 src_name,
449 src,
450 annotation.pp_ann(),
451 true,
452 parse.edition,
453 )
454 })
455 }
456
457 AstTree(PpAstTreeMode::Expanded) => {
458 debug!("pretty-printing expanded AST");
459 format!("{:#?}", krate)
460 }
461
462 Hir(s) => call_with_pp_support_hir(&s, tcx, move |annotation, krate| {
463 debug!("pretty printing HIR {:?}", s);
464 let sess = annotation.sess();
465 let sm = sess.source_map();
466 pprust_hir::print_crate(sm, krate, src_name, src, annotation.pp_ann())
467 }),
468
469 HirTree => call_with_pp_support_hir(&PpHirMode::Normal, tcx, move |_annotation, krate| {
470 debug!("pretty printing HIR tree");
471 format!("{:#?}", krate)
472 }),
473
474 ThirTree => {
475 let mut out = String::new();
476 abort_on_err(rustc_typeck::check_crate(tcx), tcx.sess);
477 debug!("pretty printing THIR tree");
478 for did in tcx.body_owners() {
479 let hir = tcx.hir();
480 let body = hir.body(hir.body_owned_by(hir.local_def_id_to_hir_id(did)));
481 let arena = thir::Arena::default();
482 let thir =
483 thir::build_thir(tcx, ty::WithOptConstParam::unknown(did), &arena, &body.value);
484 let _ = writeln!(out, "{:?}:\n{:#?}\n", did, thir);
485 }
486 out
487 }
488
489 _ => unreachable!(),
490 };
491
492 write_or_print(&out, ofile);
493 }
494
495 // In an ideal world, this would be a public function called by the driver after
496 // analysis is performed. However, we want to call `phase_3_run_analysis_passes`
497 // with a different callback than the standard driver, so that isn't easy.
498 // Instead, we call that function ourselves.
499 fn print_with_analysis(
500 tcx: TyCtxt<'_>,
501 ppm: PpMode,
502 ofile: Option<&Path>,
503 ) -> Result<(), ErrorReported> {
504 let mut out = Vec::new();
505
506 tcx.analysis(LOCAL_CRATE)?;
507
508 match ppm {
509 Mir => write_mir_pretty(tcx, None, &mut out).unwrap(),
510 MirCFG => write_mir_graphviz(tcx, None, &mut out).unwrap(),
511 _ => unreachable!(),
512 }
513
514 let out = std::str::from_utf8(&out).unwrap();
515 write_or_print(out, ofile);
516
517 Ok(())
518 }