rustc_hir/attrs/
pretty_printing.rs

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