]> git.lizzy.rs Git - rust.git/blob - src/tools/rustfmt/src/skip.rs
Auto merge of #103659 - clubby789:improve-partialord-derive, r=nagisa
[rust.git] / src / tools / rustfmt / src / skip.rs
1 //! Module that contains skip related stuffs.
2
3 use rustc_ast::ast;
4 use rustc_ast_pretty::pprust;
5 use std::collections::HashSet;
6
7 /// Track which blocks of code are to be skipped when formatting.
8 ///
9 /// You can update it by:
10 ///
11 /// - attributes slice
12 /// - manually feeding values into the underlying contexts
13 ///
14 /// Query this context to know if you need to skip a block.
15 #[derive(Default, Clone)]
16 pub(crate) struct SkipContext {
17     pub(crate) macros: SkipNameContext,
18     pub(crate) attributes: SkipNameContext,
19 }
20
21 impl SkipContext {
22     pub(crate) fn update_with_attrs(&mut self, attrs: &[ast::Attribute]) {
23         self.macros.extend(get_skip_names("macros", attrs));
24         self.attributes.extend(get_skip_names("attributes", attrs));
25     }
26
27     pub(crate) fn update(&mut self, other: SkipContext) {
28         let SkipContext { macros, attributes } = other;
29         self.macros.update(macros);
30         self.attributes.update(attributes);
31     }
32 }
33
34 /// Track which names to skip.
35 ///
36 /// Query this context with a string to know whether to skip it.
37 #[derive(Clone)]
38 pub(crate) enum SkipNameContext {
39     All,
40     Values(HashSet<String>),
41 }
42
43 impl Default for SkipNameContext {
44     fn default() -> Self {
45         Self::Values(Default::default())
46     }
47 }
48
49 impl Extend<String> for SkipNameContext {
50     fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
51         match self {
52             Self::All => {}
53             Self::Values(values) => values.extend(iter),
54         }
55     }
56 }
57
58 impl SkipNameContext {
59     pub(crate) fn update(&mut self, other: Self) {
60         match (self, other) {
61             // If we're already skipping everything, nothing more can be added
62             (Self::All, _) => {}
63             // If we want to skip all, set it
64             (this, Self::All) => {
65                 *this = Self::All;
66             }
67             // If we have some new values to skip, add them
68             (Self::Values(existing_values), Self::Values(new_values)) => {
69                 existing_values.extend(new_values)
70             }
71         }
72     }
73
74     pub(crate) fn skip(&self, name: &str) -> bool {
75         match self {
76             Self::All => true,
77             Self::Values(values) => values.contains(name),
78         }
79     }
80
81     pub(crate) fn skip_all(&mut self) {
82         *self = Self::All;
83     }
84 }
85
86 static RUSTFMT: &str = "rustfmt";
87 static SKIP: &str = "skip";
88
89 /// Say if you're playing with `rustfmt`'s skip attribute
90 pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool {
91     if segments.len() < 2 || segments[0].ident.to_string() != RUSTFMT {
92         return false;
93     }
94     match segments.len() {
95         2 => segments[1].ident.to_string() == SKIP,
96         3 => {
97             segments[1].ident.to_string() == SKIP
98                 && ["macros", "attributes"]
99                     .iter()
100                     .any(|&n| n == pprust::path_segment_to_string(&segments[2]))
101         }
102         _ => false,
103     }
104 }
105
106 fn get_skip_names(kind: &str, attrs: &[ast::Attribute]) -> Vec<String> {
107     let mut skip_names = vec![];
108     let path = format!("{}::{}::{}", RUSTFMT, SKIP, kind);
109     for attr in attrs {
110         // rustc_ast::ast::Path is implemented partialEq
111         // but it is designed for segments.len() == 1
112         if let ast::AttrKind::Normal(normal) = &attr.kind {
113             if pprust::path_to_string(&normal.item.path) != path {
114                 continue;
115             }
116         }
117
118         if let Some(list) = attr.meta_item_list() {
119             for nested_meta_item in list {
120                 if let Some(name) = nested_meta_item.ident() {
121                     skip_names.push(name.to_string());
122                 }
123             }
124         }
125     }
126     skip_names
127 }