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::token::{CommentKind, DocFragmentKind};
10use rustc_ast::{AttrId, AttrStyle, IntTy, UintTy};
11use rustc_ast_pretty::pp::Printer;
12use rustc_data_structures::fx::FxIndexMap;
13use rustc_span::def_id::DefId;
14use rustc_span::hygiene::Transparency;
15use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
16use rustc_target::spec::SanitizerSet;
17use thin_vec::ThinVec;
18
19use crate::limit::Limit;
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> PrintAttribute for FxIndexMap<T, Span> {
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, u128, usize, bool, NonZero<u32>, Limit);
195impl 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!(
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);