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