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