Skip to main content

rustc_hir/attrs/
pretty_printing.rs

1use std::num::NonZero;
2use std::ops::Deref;
3use std::path::PathBuf;
4
5use rustc_abi::Align;
6use rustc_ast::ast::{Path, join_path_idents};
7use rustc_ast::attr::data_structures::CfgEntry;
8use rustc_ast::attr::version::RustcVersion;
9use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode};
10use rustc_ast::token::{CommentKind, DocFragmentKind};
11use rustc_ast::{AttrId, AttrStyle, IntTy, UintTy};
12use rustc_ast_pretty::pp::Printer;
13use rustc_data_structures::fx::FxIndexMap;
14use rustc_span::def_id::DefId;
15use rustc_span::hygiene::Transparency;
16use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
17use rustc_target::spec::SanitizerSet;
18use thin_vec::ThinVec;
19
20use crate::limit::Limit;
21
22/// This trait is used to print attributes in `rustc_hir_pretty`.
23///
24/// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`].
25/// The output will look a lot like a `Debug` implementation, but fields of several types
26/// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the
27/// representation much.
28pub trait PrintAttribute {
29    /// Whether or not this will render as something meaningful, or if it's skipped
30    /// (which will force the containing struct to also skip printing a comma
31    /// and the field name).
32    fn should_render(&self) -> bool;
33
34    fn print_attribute(&self, p: &mut Printer);
35}
36
37impl<T: PrintAttribute> PrintAttribute for &T {
38    fn should_render(&self) -> bool {
39        T::should_render(self)
40    }
41
42    fn print_attribute(&self, p: &mut Printer) {
43        T::print_attribute(self, p)
44    }
45}
46impl<T: PrintAttribute> PrintAttribute for Box<T> {
47    fn should_render(&self) -> bool {
48        self.deref().should_render()
49    }
50
51    fn print_attribute(&self, p: &mut Printer) {
52        T::print_attribute(self.deref(), p)
53    }
54}
55impl<T: PrintAttribute> PrintAttribute for Option<T> {
56    fn should_render(&self) -> bool {
57        self.as_ref().is_some_and(|x| x.should_render())
58    }
59
60    fn print_attribute(&self, p: &mut Printer) {
61        if let Some(i) = self {
62            T::print_attribute(i, p)
63        }
64    }
65}
66impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
67    fn should_render(&self) -> bool {
68        self.is_empty() || self[0].should_render()
69    }
70
71    fn print_attribute(&self, p: &mut Printer) {
72        let mut last_printed = false;
73        p.word("[");
74        for i in self {
75            if last_printed {
76                p.word_space(",");
77            }
78            i.print_attribute(p);
79            last_printed = i.should_render();
80        }
81        p.word("]");
82    }
83}
84impl<T: PrintAttribute> PrintAttribute for FxIndexMap<T, Span> {
85    fn should_render(&self) -> bool {
86        self.is_empty() || self[0].should_render()
87    }
88
89    fn print_attribute(&self, p: &mut Printer) {
90        let mut last_printed = false;
91        p.word("[");
92        for (i, _) in self {
93            if last_printed {
94                p.word_space(",");
95            }
96            i.print_attribute(p);
97            last_printed = i.should_render();
98        }
99        p.word("]");
100    }
101}
102impl PrintAttribute for PathBuf {
103    fn should_render(&self) -> bool {
104        true
105    }
106
107    fn print_attribute(&self, p: &mut Printer) {
108        p.word(self.display().to_string());
109    }
110}
111impl PrintAttribute for Path {
112    fn should_render(&self) -> bool {
113        true
114    }
115
116    fn print_attribute(&self, p: &mut Printer) {
117        p.word(join_path_idents(self.segments.iter().map(|seg| seg.ident)));
118    }
119}
120
121macro_rules! print_skip {
122    ($($t: ty),* $(,)?) => {$(
123        impl PrintAttribute for $t {
124            fn should_render(&self) -> bool { false }
125            fn print_attribute(&self, _: &mut Printer) { }
126        })*
127    };
128}
129
130macro_rules! print_disp {
131    ($($t: ty),* $(,)?) => {$(
132        impl PrintAttribute for $t {
133            fn should_render(&self) -> bool { true }
134            fn print_attribute(&self, p: &mut Printer) {
135                p.word(format!("{}", self));
136            }
137        }
138    )*};
139}
140macro_rules! print_debug {
141    ($($t: ty),* $(,)?) => {$(
142        impl PrintAttribute for $t {
143            fn should_render(&self) -> bool { true }
144            fn print_attribute(&self, p: &mut Printer) {
145                p.word(format!("{:?}", self));
146            }
147        }
148    )*};
149}
150
151macro_rules! print_tup {
152    (num_should_render $($ts: ident)*) => { 0 $(+ $ts.should_render() as usize)* };
153    () => {};
154    ($t: ident $($ts: ident)*) => {
155        #[allow(non_snake_case, unused)]
156        impl<$t: PrintAttribute, $($ts: PrintAttribute),*> PrintAttribute for ($t, $($ts),*) {
157            fn should_render(&self) -> bool {
158                let ($t, $($ts),*) = self;
159                print_tup!(num_should_render $t $($ts)*) != 0
160            }
161
162            fn print_attribute(&self, p: &mut Printer) {
163                let ($t, $($ts),*) = self;
164                let parens = print_tup!(num_should_render $t $($ts)*) > 1;
165                if parens {
166                    p.popen();
167                }
168
169                let mut printed_anything = $t.should_render();
170
171                $t.print_attribute(p);
172
173                $(
174                    if $ts.should_render() {
175                        if printed_anything {
176                            p.word_space(",");
177                        }
178                        printed_anything = true;
179                    }
180                    $ts.print_attribute(p);
181                )*
182
183                if parens {
184                    p.pclose();
185                }
186            }
187        }
188
189        print_tup!($($ts)*);
190    };
191}
192
193#[allow(non_snake_case, unused)]
impl<H: PrintAttribute> PrintAttribute for (H,) {
    fn should_render(&self) -> bool {
        let (H,) = self;
        0 + H.should_render() as usize != 0
    }
    fn print_attribute(&self, p: &mut Printer) {
        let (H,) = self;
        let parens = 0 + H.should_render() as usize > 1;
        if parens { p.popen(); }
        let mut printed_anything = H.should_render();
        H.print_attribute(p);
        if parens { p.pclose(); }
    }
}print_tup!(A B C D E F G H);
194impl PrintAttribute for AttrId {
    fn should_render(&self) -> bool { false }
    fn print_attribute(&self, _: &mut Printer) {}
}print_skip!(Span, (), ErrorGuaranteed, AttrId);
195impl PrintAttribute for Limit {
    fn should_render(&self) -> bool { true }
    fn print_attribute(&self, p: &mut Printer) {
        p.word(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("{0}", self))
                }));
    }
}print_disp!(u8, u16, u32, u128, usize, bool, NonZero<u32>, Limit);
196impl PrintAttribute for DiffMode {
    fn should_render(&self) -> bool { true }
    fn print_attribute(&self, p: &mut Printer) {
        p.word(::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("{0:?}", self))
                }));
    }
}print_debug!(
197    Symbol,
198    Ident,
199    UintTy,
200    IntTy,
201    Align,
202    AttrStyle,
203    CommentKind,
204    DocFragmentKind,
205    Transparency,
206    SanitizerSet,
207    DefId,
208    RustcVersion,
209    CfgEntry,
210    DiffActivity,
211    DiffMode,
212);