Skip to main content

rustfmt_nightly/modules/
visitor.rs

1use rustc_ast::ast;
2use rustc_ast::visit::Visitor;
3use rustc_span::{Symbol, sym};
4use tracing::debug;
5
6use crate::attr::MetaVisitor;
7use crate::parse::macros::cfg_if::parse_cfg_if;
8use crate::parse::macros::cfg_match::parse_cfg_match;
9use crate::parse::session::ParseSess;
10
11pub(crate) struct ModItem {
12    pub(crate) item: ast::Item,
13}
14
15/// Traverse `cfg_if!` macro and fetch modules.
16pub(crate) struct CfgIfVisitor<'a> {
17    psess: &'a ParseSess,
18    mods: Vec<ModItem>,
19}
20
21impl<'a> CfgIfVisitor<'a> {
22    pub(crate) fn new(psess: &'a ParseSess) -> CfgIfVisitor<'a> {
23        CfgIfVisitor {
24            mods: vec![],
25            psess,
26        }
27    }
28
29    pub(crate) fn mods(self) -> Vec<ModItem> {
30        self.mods
31    }
32}
33
34impl<'a, 'ast: 'a> Visitor<'ast> for CfgIfVisitor<'a> {
35    fn visit_mac_call(&mut self, mac: &'ast ast::MacCall) {
36        match self.visit_mac_inner(mac) {
37            Ok(()) => (),
38            Err(e) => debug!("{}", e),
39        }
40    }
41}
42
43impl<'a, 'ast: 'a> CfgIfVisitor<'a> {
44    fn visit_mac_inner(&mut self, mac: &'ast ast::MacCall) -> Result<(), &'static str> {
45        // Support both:
46        // ```
47        // extern crate cfg_if;
48        // cfg_if::cfg_if! {..}
49        // ```
50        // And:
51        // ```
52        // #[macro_use]
53        // extern crate cfg_if;
54        // cfg_if! {..}
55        // ```
56        match mac.path.segments.first() {
57            Some(first_segment) => {
58                if first_segment.ident.name != Symbol::intern("cfg_if") {
59                    return Err("Expected cfg_if");
60                }
61            }
62            None => {
63                return Err("Expected cfg_if");
64            }
65        };
66
67        let items = parse_cfg_if(self.psess, mac)?;
68        self.mods
69            .append(&mut items.into_iter().map(|item| ModItem { item }).collect());
70
71        Ok(())
72    }
73}
74
75/// Traverse `cfg_match!` macro and fetch modules.
76pub(crate) struct CfgMatchVisitor<'a> {
77    psess: &'a ParseSess,
78    mods: Vec<ModItem>,
79}
80
81impl<'a> CfgMatchVisitor<'a> {
82    pub(crate) fn new(psess: &'a ParseSess) -> CfgMatchVisitor<'a> {
83        CfgMatchVisitor {
84            mods: vec![],
85            psess,
86        }
87    }
88
89    pub(crate) fn mods(self) -> Vec<ModItem> {
90        self.mods
91    }
92}
93
94impl<'a, 'ast: 'a> Visitor<'ast> for CfgMatchVisitor<'a> {
95    fn visit_mac_call(&mut self, mac: &'ast ast::MacCall) {
96        match self.visit_mac_inner(mac) {
97            Ok(()) => (),
98            Err(e) => debug!("{}", e),
99        }
100    }
101}
102
103impl<'a, 'ast: 'a> CfgMatchVisitor<'a> {
104    fn visit_mac_inner(&mut self, mac: &'ast ast::MacCall) -> Result<(), &'static str> {
105        // Support both:
106        // ```
107        // std::cfg_match! {..}
108        // core::cfg_match! {..}
109        // ```
110        // And:
111        // ```
112        // use std::cfg_match;
113        // cfg_match! {..}
114        // ```
115        match mac.path.segments.last() {
116            Some(last_segment) => {
117                if last_segment.ident.name != Symbol::intern("cfg_match") {
118                    return Err("Expected cfg_match");
119                }
120            }
121            None => {
122                return Err("Expected cfg_match");
123            }
124        };
125
126        let items = parse_cfg_match(self.psess, mac)?;
127        self.mods
128            .append(&mut items.into_iter().map(|item| ModItem { item }).collect());
129
130        Ok(())
131    }
132}
133
134/// Extracts `path = "foo.rs"` from attributes.
135#[derive(Default)]
136pub(crate) struct PathVisitor {
137    /// A list of path defined in attributes.
138    paths: Vec<String>,
139}
140
141impl PathVisitor {
142    pub(crate) fn paths(self) -> Vec<String> {
143        self.paths
144    }
145}
146
147impl<'ast> MetaVisitor<'ast> for PathVisitor {
148    fn visit_meta_name_value(
149        &mut self,
150        meta_item: &'ast ast::MetaItem,
151        lit: &'ast ast::MetaItemLit,
152    ) {
153        if meta_item.has_name(sym::path) && lit.kind.is_str() {
154            self.paths.push(meta_item_lit_to_str(lit));
155        }
156    }
157}
158
159#[cfg(not(windows))]
160fn meta_item_lit_to_str(lit: &ast::MetaItemLit) -> String {
161    match lit.kind {
162        ast::LitKind::Str(symbol, ..) => symbol.to_string(),
163        _ => unreachable!(),
164    }
165}
166
167#[cfg(windows)]
168fn meta_item_lit_to_str(lit: &ast::MetaItemLit) -> String {
169    match lit.kind {
170        ast::LitKind::Str(symbol, ..) => symbol.as_str().replace("/", "\\"),
171        _ => unreachable!(),
172    }
173}