rustc_passes/
debugger_visualizer.rs

1//! Detecting usage of the `#[debugger_visualizer]` attribute.
2
3use rustc_ast::ast::NodeId;
4use rustc_ast::{HasNodeId, ItemKind, ast};
5use rustc_attr_parsing::AttributeParser;
6use rustc_expand::base::resolve_path;
7use rustc_hir::Attribute;
8use rustc_hir::attrs::{AttributeKind, DebugVisualizer};
9use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
10use rustc_middle::query::{LocalCrate, Providers};
11use rustc_middle::ty::TyCtxt;
12use rustc_session::Session;
13use rustc_span::{DUMMY_SP, Span, sym};
14
15use crate::errors::DebugVisualizerUnreadable;
16
17impl DebuggerVisualizerCollector<'_> {
18    fn check_for_debugger_visualizer(
19        &mut self,
20        attrs: &[ast::Attribute],
21        span: Span,
22        node_id: NodeId,
23    ) {
24        if let Some(Attribute::Parsed(AttributeKind::DebuggerVisualizer(visualizers))) =
25            AttributeParser::parse_limited(
26                &self.sess,
27                attrs,
28                sym::debugger_visualizer,
29                span,
30                node_id,
31                None,
32            )
33        {
34            for DebugVisualizer { span, visualizer_type, path } in visualizers {
35                let file = match resolve_path(&self.sess, path.as_str(), span) {
36                    Ok(file) => file,
37                    Err(err) => {
38                        err.emit();
39                        return;
40                    }
41                };
42
43                match self.sess.source_map().load_binary_file(&file) {
44                    Ok((source, _)) => {
45                        self.visualizers.push(DebuggerVisualizerFile::new(
46                            source,
47                            visualizer_type,
48                            file,
49                        ));
50                    }
51                    Err(error) => {
52                        self.sess.dcx().emit_err(DebugVisualizerUnreadable {
53                            span,
54                            file: &file,
55                            error,
56                        });
57                    }
58                }
59            }
60        }
61    }
62}
63
64struct DebuggerVisualizerCollector<'a> {
65    sess: &'a Session,
66    visualizers: Vec<DebuggerVisualizerFile>,
67}
68
69impl<'ast> rustc_ast::visit::Visitor<'ast> for DebuggerVisualizerCollector<'_> {
70    fn visit_item(&mut self, item: &'ast rustc_ast::Item) -> Self::Result {
71        if let ItemKind::Mod(..) = item.kind {
72            self.check_for_debugger_visualizer(&item.attrs, item.span, item.node_id());
73        }
74        rustc_ast::visit::walk_item(self, item);
75    }
76    fn visit_crate(&mut self, krate: &'ast ast::Crate) -> Self::Result {
77        self.check_for_debugger_visualizer(&krate.attrs, DUMMY_SP, krate.id);
78        rustc_ast::visit::walk_crate(self, krate);
79    }
80}
81
82/// Traverses and collects the debugger visualizers for a specific crate.
83fn debugger_visualizers(tcx: TyCtxt<'_>, _: LocalCrate) -> Vec<DebuggerVisualizerFile> {
84    let resolver_and_krate = tcx.resolver_for_lowering().borrow();
85    let krate = &*resolver_and_krate.1;
86
87    let mut visitor = DebuggerVisualizerCollector { sess: tcx.sess, visualizers: Vec::new() };
88    rustc_ast::visit::Visitor::visit_crate(&mut visitor, krate);
89
90    // We are collecting visualizers in AST-order, which is deterministic,
91    // so we don't need to do any explicit sorting in order to get a
92    // deterministic query result
93    visitor.visualizers
94}
95
96pub(crate) fn provide(providers: &mut Providers) {
97    providers.debugger_visualizers = debugger_visualizers;
98}