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