rustfmt_nightly/parse/macros/
cfg_if.rs

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