Skip to main content

rustfmt_nightly/parse/macros/
mod.rs

1use rustc_ast::ast;
2use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind};
3use rustc_ast::tokenstream::TokenStream;
4use rustc_parse::MACRO_ARGUMENTS;
5use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser, Recovery};
6use rustc_session::parse::ParseSess;
7use rustc_span::symbol;
8
9use crate::macros::MacroArg;
10use crate::rewrite::RewriteContext;
11
12pub(crate) mod cfg_if;
13pub(crate) mod cfg_select;
14pub(crate) mod lazy_static;
15
16fn build_stream_parser<'a>(psess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> {
17    Parser::new(psess, tokens, MACRO_ARGUMENTS).recovery(Recovery::Forbidden)
18}
19
20fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser<'a> {
21    build_stream_parser(context.psess.inner(), tokens)
22}
23
24fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
25    macro_rules! parse_macro_arg {
26        ($macro_arg:ident, $nt_kind:expr, $try_parse:expr, $then:expr) => {
27            let mut cloned_parser = (*parser).clone();
28            if Parser::nonterminal_may_begin_with($nt_kind, &cloned_parser.token)
29                || matches!(cloned_parser.token.kind, TokenKind::DocComment(..))
30            {
31                match $try_parse(&mut cloned_parser) {
32                    Ok(x) => {
33                        if parser.psess.dcx().has_errors().is_some() {
34                            parser.psess.dcx().reset_err_count();
35                        } else {
36                            // Parsing succeeded.
37                            *parser = cloned_parser;
38                            return Some(MacroArg::$macro_arg($then(x)?));
39                        }
40                    }
41                    Err(e) => {
42                        e.cancel();
43                        parser.psess.dcx().reset_err_count();
44                    }
45                }
46            }
47        };
48    }
49
50    parse_macro_arg!(
51        Expr,
52        NonterminalKind::Expr(Expr),
53        |parser: &mut Parser<'b>| parser.parse_expr(),
54        |x: Box<ast::Expr>| Some(x)
55    );
56    parse_macro_arg!(
57        Ty,
58        NonterminalKind::Ty,
59        |parser: &mut Parser<'b>| parser.parse_ty(),
60        |x: Box<ast::Ty>| Some(x)
61    );
62    parse_macro_arg!(
63        Pat,
64        NonterminalKind::Pat(PatParam { inferred: false }),
65        |parser: &mut Parser<'b>| parser.parse_pat_no_top_alt(None, None),
66        |x: ast::Pat| Some(Box::new(x))
67    );
68    // `parse_item` returns `Option<Box<ast::Item>>`.
69    parse_macro_arg!(
70        Item,
71        NonterminalKind::Item,
72        |parser: &mut Parser<'b>| parser.parse_item(ForceCollect::No, AllowConstBlockItems::Yes),
73        |x: Option<Box<ast::Item>>| x
74    );
75
76    None
77}
78
79pub(crate) struct ParsedMacroArgs {
80    pub(crate) vec_with_semi: bool,
81    pub(crate) trailing_comma: bool,
82    pub(crate) args: Vec<MacroArg>,
83}
84
85fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
86    if parser.token.is_reserved_ident()
87        && parser.look_ahead(1, |t| *t == TokenKind::Eof || *t == TokenKind::Comma)
88    {
89        let keyword = parser.token.ident().unwrap().0.name;
90        parser.bump();
91        Some(MacroArg::Keyword(
92            symbol::Ident::with_dummy_span(keyword),
93            parser.prev_token.span,
94        ))
95    } else {
96        None
97    }
98}
99
100pub(crate) fn parse_macro_args(
101    context: &RewriteContext<'_>,
102    tokens: TokenStream,
103    style: Delimiter,
104    forced_bracket: bool,
105) -> Option<ParsedMacroArgs> {
106    let mut parser = build_parser(context, tokens);
107    let mut args = Vec::new();
108    let mut vec_with_semi = false;
109    let mut trailing_comma = false;
110
111    if Delimiter::Brace != style {
112        loop {
113            if let Some(arg) = check_keyword(&mut parser) {
114                args.push(arg);
115            } else if let Some(arg) = parse_macro_arg(&mut parser) {
116                args.push(arg);
117            } else {
118                return None;
119            }
120
121            match parser.token.kind {
122                TokenKind::Eof => break,
123                TokenKind::Comma => (),
124                TokenKind::Semi => {
125                    // Try to parse `vec![expr; expr]`
126                    if forced_bracket {
127                        parser.bump();
128                        if parser.token.kind != TokenKind::Eof {
129                            match parse_macro_arg(&mut parser) {
130                                Some(arg) => {
131                                    args.push(arg);
132                                    parser.bump();
133                                    if parser.token == TokenKind::Eof && args.len() == 2 {
134                                        vec_with_semi = true;
135                                        break;
136                                    }
137                                }
138                                None => {
139                                    return None;
140                                }
141                            }
142                        }
143                    }
144                    return None;
145                }
146                _ if args.last().map_or(false, MacroArg::is_item) => continue,
147                _ => return None,
148            }
149
150            parser.bump();
151
152            if parser.token == TokenKind::Eof {
153                trailing_comma = true;
154                break;
155            }
156        }
157    }
158
159    Some(ParsedMacroArgs {
160        vec_with_semi,
161        trailing_comma,
162        args,
163    })
164}
165
166pub(crate) fn parse_expr(
167    context: &RewriteContext<'_>,
168    tokens: TokenStream,
169) -> Option<Box<ast::Expr>> {
170    let mut parser = build_parser(context, tokens);
171    parser.parse_expr().ok()
172}