1 //! Module that contains skip related stuffs.
4 use rustc_ast_pretty::pprust;
5 use std::collections::HashSet;
7 /// Track which blocks of code are to be skipped when formatting.
9 /// You can update it by:
11 /// - attributes slice
12 /// - manually feeding values into the underlying contexts
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,
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));
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);
34 /// Track which names to skip.
36 /// Query this context with a string to know whether to skip it.
38 pub(crate) enum SkipNameContext {
40 Values(HashSet<String>),
43 impl Default for SkipNameContext {
44 fn default() -> Self {
45 Self::Values(Default::default())
49 impl Extend<String> for SkipNameContext {
50 fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
53 Self::Values(values) => values.extend(iter),
58 impl SkipNameContext {
59 pub(crate) fn update(&mut self, other: Self) {
61 // If we're already skipping everything, nothing more can be added
63 // If we want to skip all, set it
64 (this, Self::All) => {
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)
74 pub(crate) fn skip(&self, name: &str) -> bool {
77 Self::Values(values) => values.contains(name),
81 pub(crate) fn skip_all(&mut self) {
86 static RUSTFMT: &str = "rustfmt";
87 static SKIP: &str = "skip";
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 {
94 match segments.len() {
95 2 => segments[1].ident.to_string() == SKIP,
97 segments[1].ident.to_string() == SKIP
98 && ["macros", "attributes"]
100 .any(|&n| n == pprust::path_segment_to_string(&segments[2]))
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);
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 {
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());