]> git.lizzy.rs Git - rust.git/blob - src/modules/visitor.rs
deps: update rustc-ap to v642.0.0
[rust.git] / src / modules / visitor.rs
1 use rustc_parse::{stream_to_parser_with_base_dir, Directory};
2 use rustc_session::parse::ParseSess;
3 use rustc_span::{symbol::kw, Symbol};
4 use syntax::ast;
5 use syntax::token::{DelimToken, TokenKind};
6 use syntax::visit::Visitor;
7
8 use crate::attr::MetaVisitor;
9
10 pub(crate) struct ModItem {
11     pub(crate) item: ast::Item,
12 }
13
14 /// Traverse `cfg_if!` macro and fetch modules.
15 pub(crate) struct CfgIfVisitor<'a> {
16     parse_sess: &'a ParseSess,
17     mods: Vec<ModItem>,
18     base_dir: Directory<'a>,
19 }
20
21 impl<'a> CfgIfVisitor<'a> {
22     pub(crate) fn new(parse_sess: &'a ParseSess, base_dir: Directory<'a>) -> CfgIfVisitor<'a> {
23         CfgIfVisitor {
24             mods: vec![],
25             parse_sess,
26             base_dir,
27         }
28     }
29
30     pub(crate) fn mods(self) -> Vec<ModItem> {
31         self.mods
32     }
33 }
34
35 impl<'a, 'ast: 'a> Visitor<'ast> for CfgIfVisitor<'a> {
36     fn visit_mac(&mut self, mac: &'ast ast::Mac) {
37         match self.visit_mac_inner(mac) {
38             Ok(()) => (),
39             Err(e) => debug!("{}", e),
40         }
41     }
42 }
43
44 impl<'a, 'ast: 'a> CfgIfVisitor<'a> {
45     fn visit_mac_inner(&mut self, mac: &'ast ast::Mac) -> Result<(), &'static str> {
46         // Support both:
47         // ```
48         // extern crate cfg_if;
49         // cfg_if::cfg_if! {..}
50         // ```
51         // And:
52         // ```
53         // #[macro_use]
54         // extern crate cfg_if;
55         // cfg_if! {..}
56         // ```
57         match mac.path.segments.first() {
58             Some(first_segment) => {
59                 if first_segment.ident.name != Symbol::intern("cfg_if") {
60                     return Err("Expected cfg_if");
61                 }
62             }
63             None => {
64                 return Err("Expected cfg_if");
65             }
66         };
67
68         let ts = mac.args.inner_tokens();
69         let mut parser =
70             stream_to_parser_with_base_dir(self.parse_sess, ts.clone(), self.base_dir.clone());
71         parser.cfg_mods = false;
72         let mut process_if_cfg = true;
73
74         while parser.token.kind != TokenKind::Eof {
75             if process_if_cfg {
76                 if !parser.eat_keyword(kw::If) {
77                     return Err("Expected `if`");
78                 }
79                 parser
80                     .parse_attribute(false)
81                     .map_err(|_| "Failed to parse attributes")?;
82             }
83
84             if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
85                 return Err("Expected an opening brace");
86             }
87
88             while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
89                 && parser.token.kind != TokenKind::Eof
90             {
91                 let item = match parser.parse_item() {
92                     Ok(Some(item_ptr)) => item_ptr.into_inner(),
93                     Ok(None) => continue,
94                     Err(mut err) => {
95                         err.cancel();
96                         parser.sess.span_diagnostic.reset_err_count();
97                         return Err(
98                             "Expected item inside cfg_if block, but failed to parse it as an item",
99                         );
100                     }
101                 };
102                 if let ast::ItemKind::Mod(..) = item.kind {
103                     self.mods.push(ModItem { item });
104                 }
105             }
106
107             if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
108                 return Err("Expected a closing brace");
109             }
110
111             if parser.eat(&TokenKind::Eof) {
112                 break;
113             }
114
115             if !parser.eat_keyword(kw::Else) {
116                 return Err("Expected `else`");
117             }
118
119             process_if_cfg = parser.token.is_keyword(kw::If);
120         }
121
122         Ok(())
123     }
124 }
125
126 /// Extracts `path = "foo.rs"` from attributes.
127 #[derive(Default)]
128 pub(crate) struct PathVisitor {
129     /// A list of path defined in attributes.
130     paths: Vec<String>,
131 }
132
133 impl PathVisitor {
134     pub(crate) fn paths(self) -> Vec<String> {
135         self.paths
136     }
137 }
138
139 impl<'ast> MetaVisitor<'ast> for PathVisitor {
140     fn visit_meta_name_value(&mut self, meta_item: &'ast ast::MetaItem, lit: &'ast ast::Lit) {
141         if meta_item.check_name(Symbol::intern("path")) && lit.kind.is_str() {
142             self.paths.push(lit_to_str(lit));
143         }
144     }
145 }
146
147 #[cfg(not(windows))]
148 fn lit_to_str(lit: &ast::Lit) -> String {
149     match lit.kind {
150         ast::LitKind::Str(symbol, ..) => symbol.to_string(),
151         _ => unreachable!(),
152     }
153 }
154
155 #[cfg(windows)]
156 fn lit_to_str(lit: &ast::Lit) -> String {
157     match lit.kind {
158         ast::LitKind::Str(symbol, ..) => symbol.as_str().replace("/", "\\"),
159         _ => unreachable!(),
160     }
161 }