]> git.proxmox.com Git - rustc.git/blob - src/tools/rustfmt/src/skip.rs
New upstream version 1.75.0+dfsg1
[rustc.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 }