]> git.lizzy.rs Git - rust.git/blob - src/parse/macros/cfg_if.rs
09b3e32df312d5b026be722102e2e20e526ad1a8
[rust.git] / src / parse / macros / cfg_if.rs
1 use std::panic::{catch_unwind, AssertUnwindSafe};
2
3 use rustc_ast::ast;
4 use rustc_ast::token::{Delimiter, TokenKind};
5 use rustc_parse::parser::ForceCollect;
6 use rustc_span::symbol::kw;
7
8 use crate::parse::macros::build_stream_parser;
9 use crate::parse::session::ParseSess;
10
11 pub(crate) fn parse_cfg_if<'a>(
12     sess: &'a ParseSess,
13     mac: &'a ast::MacCall,
14 ) -> Result<Vec<ast::Item>, &'static str> {
15     match catch_unwind(AssertUnwindSafe(|| parse_cfg_if_inner(sess, mac))) {
16         Ok(Ok(items)) => Ok(items),
17         Ok(err @ Err(_)) => err,
18         Err(..) => Err("failed to parse cfg_if!"),
19     }
20 }
21
22 fn parse_cfg_if_inner<'a>(
23     sess: &'a ParseSess,
24     mac: &'a ast::MacCall,
25 ) -> Result<Vec<ast::Item>, &'static str> {
26     let ts = mac.args.inner_tokens();
27     let mut parser = build_stream_parser(sess.inner(), ts);
28
29     let mut items = vec![];
30     let mut process_if_cfg = true;
31
32     while parser.token.kind != TokenKind::Eof {
33         if process_if_cfg {
34             if !parser.eat_keyword(kw::If) {
35                 return Err("Expected `if`");
36             }
37             // Inner attributes are not actually syntactically permitted here, but we don't
38             // care about inner vs outer attributes in this position. Our purpose with this
39             // special case parsing of cfg_if macros is to ensure we can correctly resolve
40             // imported modules that may have a custom `path` defined.
41             //
42             // As such, we just need to advance the parser past the attribute and up to
43             // to the opening brace.
44             // See also https://github.com/rust-lang/rust/pull/79433
45             parser
46                 .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
47                 .map_err(|_| "Failed to parse attributes")?;
48         }
49
50         if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) {
51             return Err("Expected an opening brace");
52         }
53
54         while parser.token != TokenKind::CloseDelim(Delimiter::Brace)
55             && parser.token.kind != TokenKind::Eof
56         {
57             let item = match parser.parse_item(ForceCollect::No) {
58                 Ok(Some(item_ptr)) => item_ptr.into_inner(),
59                 Ok(None) => continue,
60                 Err(err) => {
61                     err.cancel();
62                     parser.sess.span_diagnostic.reset_err_count();
63                     return Err(
64                         "Expected item inside cfg_if block, but failed to parse it as an item",
65                     );
66                 }
67             };
68             if let ast::ItemKind::Mod(..) = item.kind {
69                 items.push(item);
70             }
71         }
72
73         if !parser.eat(&TokenKind::CloseDelim(Delimiter::Brace)) {
74             return Err("Expected a closing brace");
75         }
76
77         if parser.eat(&TokenKind::Eof) {
78             break;
79         }
80
81         if !parser.eat_keyword(kw::Else) {
82             return Err("Expected `else`");
83         }
84
85         process_if_cfg = parser.token.is_keyword(kw::If);
86     }
87
88     Ok(items)
89 }