1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use std::panic::{catch_unwind, AssertUnwindSafe};

use rustc_ast::ast;
use rustc_ast::token::{Delimiter, TokenKind};
use rustc_parse::parser::ForceCollect;
use rustc_span::symbol::kw;

use crate::parse::macros::build_stream_parser;
use crate::parse::session::ParseSess;

pub(crate) fn parse_cfg_if<'a>(
    sess: &'a ParseSess,
    mac: &'a ast::MacCall,
) -> Result<Vec<ast::Item>, &'static str> {
    match catch_unwind(AssertUnwindSafe(|| parse_cfg_if_inner(sess, mac))) {
        Ok(Ok(items)) => Ok(items),
        Ok(err @ Err(_)) => err,
        Err(..) => Err("failed to parse cfg_if!"),
    }
}

fn parse_cfg_if_inner<'a>(
    sess: &'a ParseSess,
    mac: &'a ast::MacCall,
) -> Result<Vec<ast::Item>, &'static str> {
    let ts = mac.args.tokens.clone();
    let mut parser = build_stream_parser(sess.inner(), ts);

    let mut items = vec![];
    let mut process_if_cfg = true;

    while parser.token.kind != TokenKind::Eof {
        if process_if_cfg {
            if !parser.eat_keyword(kw::If) {
                return Err("Expected `if`");
            }

            if !matches!(parser.token.kind, TokenKind::Pound) {
                return Err("Failed to parse attributes");
            }

            // Inner attributes are not actually syntactically permitted here, but we don't
            // care about inner vs outer attributes in this position. Our purpose with this
            // special case parsing of cfg_if macros is to ensure we can correctly resolve
            // imported modules that may have a custom `path` defined.
            //
            // As such, we just need to advance the parser past the attribute and up to
            // to the opening brace.
            // See also https://github.com/rust-lang/rust/pull/79433
            parser
                .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
                .map_err(|e| {
                    e.cancel();
                    "Failed to parse attributes"
                })?;
        }

        if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) {
            return Err("Expected an opening brace");
        }

        while parser.token != TokenKind::CloseDelim(Delimiter::Brace)
            && parser.token.kind != TokenKind::Eof
        {
            let item = match parser.parse_item(ForceCollect::No) {
                Ok(Some(item_ptr)) => item_ptr.into_inner(),
                Ok(None) => continue,
                Err(err) => {
                    err.cancel();
                    parser.sess.span_diagnostic.reset_err_count();
                    return Err(
                        "Expected item inside cfg_if block, but failed to parse it as an item",
                    );
                }
            };
            if let ast::ItemKind::Mod(..) = item.kind {
                items.push(item);
            }
        }

        if !parser.eat(&TokenKind::CloseDelim(Delimiter::Brace)) {
            return Err("Expected a closing brace");
        }

        if parser.eat(&TokenKind::Eof) {
            break;
        }

        if !parser.eat_keyword(kw::Else) {
            return Err("Expected `else`");
        }

        process_if_cfg = parser.token.is_keyword(kw::If);
    }

    Ok(items)
}