]> git.lizzy.rs Git - rust.git/blob - src/macros.rs
Merge pull request #1633 from topecongiro/rfc/control-style
[rust.git] / src / macros.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // Format list-like macro invocations. These are invocations whose token trees
12 // can be interpreted as expressions and separated by commas.
13 // Note that these token trees do not actually have to be interpreted as
14 // expressions by the compiler. An example of an invocation we would reformat is
15 // foo!( x, y, z ). The token x may represent an identifier in the code, but we
16 // interpreted as an expression.
17 // Macro uses which are not-list like, such as bar!(key => val), will not be
18 // reformated.
19 // List-like invocations with parentheses will be formatted as function calls,
20 // and those with brackets will be formatted as array literals.
21
22 use syntax::ast;
23 use syntax::codemap::{mk_sp, BytePos};
24 use syntax::parse::token::Token;
25 use syntax::parse::tts_to_parser;
26 use syntax::symbol;
27 use syntax::util::ThinVec;
28
29 use Shape;
30 use codemap::SpanUtils;
31 use rewrite::{Rewrite, RewriteContext};
32 use expr::{rewrite_call, rewrite_array, rewrite_pair};
33 use comment::{FindUncommented, contains_comment};
34
35 const FORCED_BRACKET_MACROS: &'static [&'static str] = &["vec!"];
36
37 // FIXME: use the enum from libsyntax?
38 #[derive(Clone, Copy, PartialEq, Eq)]
39 enum MacroStyle {
40     Parens,
41     Brackets,
42     Braces,
43 }
44
45 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
46 pub enum MacroPosition {
47     Item,
48     Statement,
49     Expression,
50 }
51
52 impl MacroStyle {
53     fn opener(&self) -> &'static str {
54         match *self {
55             MacroStyle::Parens => "(",
56             MacroStyle::Brackets => "[",
57             MacroStyle::Braces => "{",
58         }
59     }
60 }
61
62 pub fn rewrite_macro(mac: &ast::Mac,
63                      extra_ident: Option<ast::Ident>,
64                      context: &RewriteContext,
65                      shape: Shape,
66                      position: MacroPosition)
67                      -> Option<String> {
68     let mut context = &mut context.clone();
69     context.inside_macro = true;
70     if context.config.use_try_shorthand() {
71         if let Some(expr) = convert_try_mac(mac, context) {
72             return expr.rewrite(context, shape);
73         }
74     }
75
76     let original_style = macro_style(mac, context);
77
78     let macro_name = match extra_ident {
79         None => format!("{}!", mac.node.path),
80         Some(ident) => {
81             if ident == symbol::keywords::Invalid.ident() {
82                 format!("{}!", mac.node.path)
83             } else {
84                 format!("{}! {}", mac.node.path, ident)
85             }
86         }
87     };
88
89     let style = if FORCED_BRACKET_MACROS.contains(&&macro_name[..]) {
90         MacroStyle::Brackets
91     } else {
92         original_style
93     };
94
95     if mac.node.tts.is_empty() && !contains_comment(&context.snippet(mac.span)) {
96         return match style {
97             MacroStyle::Parens if position == MacroPosition::Item => {
98                 Some(format!("{}();", macro_name))
99             }
100             MacroStyle::Parens => Some(format!("{}()", macro_name)),
101             MacroStyle::Brackets => Some(format!("{}[]", macro_name)),
102             MacroStyle::Braces => Some(format!("{}{{}}", macro_name)),
103         };
104     }
105
106     let mut parser = tts_to_parser(context.parse_session, mac.node.tts.clone());
107     let mut expr_vec = Vec::new();
108     let mut vec_with_semi = false;
109
110     if MacroStyle::Braces != style {
111         loop {
112             let expr = match parser.parse_expr() {
113                 Ok(expr) => {
114                     // Recovered errors.
115                     if context.parse_session.span_diagnostic.has_errors() {
116                         return Some(context.snippet(mac.span));
117                     }
118
119                     expr
120                 }
121                 Err(mut e) => {
122                     e.cancel();
123                     return Some(context.snippet(mac.span));
124                 }
125             };
126
127             expr_vec.push(expr);
128
129             match parser.token {
130                 Token::Eof => break,
131                 Token::Comma => (),
132                 Token::Semi => {
133                     // Try to parse `vec![expr; expr]`
134                     if FORCED_BRACKET_MACROS.contains(&&macro_name[..]) {
135                         parser.bump();
136                         if parser.token != Token::Eof {
137                             match parser.parse_expr() {
138                                 Ok(expr) => {
139                                     if context.parse_session.span_diagnostic.has_errors() {
140                                         return None;
141                                     }
142                                     expr_vec.push(expr);
143                                     parser.bump();
144                                     if parser.token == Token::Eof && expr_vec.len() == 2 {
145                                         vec_with_semi = true;
146                                         break;
147                                     }
148                                 }
149                                 Err(mut e) => e.cancel(),
150                             }
151                         }
152                     }
153                     return None;
154                 }
155                 _ => return None,
156             }
157
158             parser.bump();
159
160             if parser.token == Token::Eof {
161                 // vec! is a special case of bracket macro which should be formated as an array.
162                 if macro_name == "vec!" {
163                     break;
164                 } else {
165                     return None;
166                 }
167             }
168         }
169     }
170
171     match style {
172         MacroStyle::Parens => {
173             // Format macro invocation as function call, forcing no trailing
174             // comma because not all macros support them.
175             rewrite_call(context, &macro_name, &expr_vec, mac.span, shape).map(
176                 |rw| match position {
177                     MacroPosition::Item => format!("{};", rw),
178                     _ => rw,
179                 },
180             )
181         }
182         MacroStyle::Brackets => {
183             let mac_shape = try_opt!(shape.shrink_left(macro_name.len()));
184             // Handle special case: `vec![expr; expr]`
185             if vec_with_semi {
186                 let (lbr, rbr) = if context.config.spaces_within_square_brackets() {
187                     ("[ ", " ]")
188                 } else {
189                     ("[", "]")
190                 };
191                 rewrite_pair(&*expr_vec[0],
192                              &*expr_vec[1],
193                              lbr,
194                              "; ",
195                              rbr,
196                              context,
197                              mac_shape)
198                     .map(|s| format!("{}{}", macro_name, s))
199             } else {
200                 // Format macro invocation as array literal.
201                 let rewrite =
202                     try_opt!(rewrite_array(expr_vec.iter().map(|x| &**x),
203                                            mk_sp(context
204                                                      .codemap
205                                                      .span_after(mac.span, original_style.opener()),
206                                                mac.span.hi - BytePos(1)),
207                                            context,
208                                            mac_shape));
209
210                 Some(format!("{}{}", macro_name, rewrite))
211             }
212         }
213         MacroStyle::Braces => {
214             // Skip macro invocations with braces, for now.
215             None
216         }
217     }
218 }
219
220 /// Tries to convert a macro use into a short hand try expression. Returns None
221 /// when the macro is not an instance of try! (or parsing the inner expression
222 /// failed).
223 pub fn convert_try_mac(mac: &ast::Mac, context: &RewriteContext) -> Option<ast::Expr> {
224     if &format!("{}", mac.node.path)[..] == "try" {
225         let mut parser = tts_to_parser(context.parse_session, mac.node.tts.clone());
226
227         Some(ast::Expr {
228                  id: ast::NodeId::new(0), // dummy value
229                  node: ast::ExprKind::Try(try_opt!(parser.parse_expr().ok())),
230                  span: mac.span, // incorrect span, but shouldn't matter too much
231                  attrs: ThinVec::new(),
232              })
233     } else {
234         None
235     }
236 }
237
238 fn macro_style(mac: &ast::Mac, context: &RewriteContext) -> MacroStyle {
239     let snippet = context.snippet(mac.span);
240     let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value());
241     let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value());
242     let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value());
243
244     if paren_pos < bracket_pos && paren_pos < brace_pos {
245         MacroStyle::Parens
246     } else if bracket_pos < brace_pos {
247         MacroStyle::Brackets
248     } else {
249         MacroStyle::Braces
250     }
251 }