rustc_hir/attrs/
pretty_printing.rs

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