rustfmt_nightly/parse/macros/
mod.rs

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