rustfmt_nightly/parse/macros/
mod.rs1use 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 *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_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 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}