]> git.lizzy.rs Git - rust.git/blob - src/macros.rs
1c9ca3057f017976c5ff7dfaf90b80f6bdae480a
[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 // reformatted.
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 std::collections::HashMap;
23 use syntax::ast;
24 use syntax::codemap::{BytePos, Span};
25 use syntax::parse::new_parser_from_tts;
26 use syntax::parse::parser::Parser;
27 use syntax::parse::token::{BinOpToken, DelimToken, Token};
28 use syntax::print::pprust;
29 use syntax::symbol;
30 use syntax::tokenstream::{Cursor, ThinTokenStream, TokenStream, TokenTree};
31 use syntax::util::ThinVec;
32
33 use codemap::SpanUtils;
34 use comment::{contains_comment, remove_trailing_white_spaces, FindUncommented};
35 use expr::{rewrite_array, rewrite_call_inner};
36 use lists::{itemize_list, write_list, DefinitiveListTactic, ListFormatting, SeparatorPlace, SeparatorTactic};
37 use rewrite::{Rewrite, RewriteContext};
38 use shape::{Indent, Shape};
39 use utils::{format_visibility, mk_sp};
40
41 const FORCED_BRACKET_MACROS: &[&str] = &["vec!"];
42
43 // FIXME: use the enum from libsyntax?
44 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
45 enum MacroStyle {
46     Parens,
47     Brackets,
48     Braces,
49 }
50
51 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
52 pub enum MacroPosition {
53     Item,
54     Statement,
55     Expression,
56     Pat,
57 }
58
59 impl MacroStyle {
60     fn opener(&self) -> &'static str {
61         match *self {
62             MacroStyle::Parens => "(",
63             MacroStyle::Brackets => "[",
64             MacroStyle::Braces => "{",
65         }
66     }
67 }
68
69 #[derive(Debug)]
70 pub enum MacroArg {
71     Expr(ast::Expr),
72     Ty(ast::Ty),
73     Pat(ast::Pat),
74 }
75
76 impl Rewrite for MacroArg {
77     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
78         match *self {
79             MacroArg::Expr(ref expr) => expr.rewrite(context, shape),
80             MacroArg::Ty(ref ty) => ty.rewrite(context, shape),
81             MacroArg::Pat(ref pat) => pat.rewrite(context, shape),
82         }
83     }
84 }
85
86 fn parse_macro_arg(parser: &mut Parser) -> Option<MacroArg> {
87     macro_rules! parse_macro_arg {
88         ($macro_arg: ident, $parser: ident) => {
89             let mut cloned_parser = (*parser).clone();
90             match cloned_parser.$parser() {
91                 Ok(x) => {
92                     if parser.sess.span_diagnostic.has_errors() {
93                         parser.sess.span_diagnostic.reset_err_count();
94                     } else {
95                         // Parsing succeeded.
96                         *parser = cloned_parser;
97                         return Some(MacroArg::$macro_arg((*x).clone()));
98                     }
99                 }
100                 Err(mut e) => {
101                     e.cancel();
102                     parser.sess.span_diagnostic.reset_err_count();
103                 }
104             }
105         }
106     }
107
108     parse_macro_arg!(Expr, parse_expr);
109     parse_macro_arg!(Ty, parse_ty);
110     parse_macro_arg!(Pat, parse_pat);
111
112     None
113 }
114
115 pub fn rewrite_macro(
116     mac: &ast::Mac,
117     extra_ident: Option<ast::Ident>,
118     context: &RewriteContext,
119     shape: Shape,
120     position: MacroPosition,
121 ) -> Option<String> {
122     let context = &mut context.clone();
123     context.inside_macro = true;
124     if context.config.use_try_shorthand() {
125         if let Some(expr) = convert_try_mac(mac, context) {
126             context.inside_macro = false;
127             return expr.rewrite(context, shape);
128         }
129     }
130
131     let original_style = macro_style(mac, context);
132
133     let macro_name = match extra_ident {
134         None => format!("{}!", mac.node.path),
135         Some(ident) => {
136             if ident == symbol::keywords::Invalid.ident() {
137                 format!("{}!", mac.node.path)
138             } else {
139                 format!("{}! {}", mac.node.path, ident)
140             }
141         }
142     };
143
144     let style = if FORCED_BRACKET_MACROS.contains(&&macro_name[..]) {
145         MacroStyle::Brackets
146     } else {
147         original_style
148     };
149
150     let ts: TokenStream = mac.node.stream();
151     if ts.is_empty() && !contains_comment(context.snippet(mac.span)) {
152         return match style {
153             MacroStyle::Parens if position == MacroPosition::Item => {
154                 Some(format!("{}();", macro_name))
155             }
156             MacroStyle::Parens => Some(format!("{}()", macro_name)),
157             MacroStyle::Brackets => Some(format!("{}[]", macro_name)),
158             MacroStyle::Braces => Some(format!("{}{{}}", macro_name)),
159         };
160     }
161
162     let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect());
163     let mut arg_vec = Vec::new();
164     let mut vec_with_semi = false;
165     let mut trailing_comma = false;
166
167     if MacroStyle::Braces != style {
168         loop {
169             match parse_macro_arg(&mut parser) {
170                 Some(arg) => arg_vec.push(arg),
171                 None => return Some(context.snippet(mac.span).to_owned()),
172             }
173
174             match parser.token {
175                 Token::Eof => break,
176                 Token::Comma => (),
177                 Token::Semi => {
178                     // Try to parse `vec![expr; expr]`
179                     if FORCED_BRACKET_MACROS.contains(&&macro_name[..]) {
180                         parser.bump();
181                         if parser.token != Token::Eof {
182                             match parse_macro_arg(&mut parser) {
183                                 Some(arg) => {
184                                     arg_vec.push(arg);
185                                     parser.bump();
186                                     if parser.token == Token::Eof && arg_vec.len() == 2 {
187                                         vec_with_semi = true;
188                                         break;
189                                     }
190                                 }
191                                 None => return Some(context.snippet(mac.span).to_owned()),
192                             }
193                         }
194                     }
195                     return Some(context.snippet(mac.span).to_owned());
196                 }
197                 _ => return Some(context.snippet(mac.span).to_owned()),
198             }
199
200             parser.bump();
201
202             if parser.token == Token::Eof {
203                 trailing_comma = true;
204                 break;
205             }
206         }
207     }
208
209     match style {
210         MacroStyle::Parens => {
211             // Format macro invocation as function call, forcing no trailing
212             // comma because not all macros support them.
213             rewrite_call_inner(
214                 context,
215                 &macro_name,
216                 &arg_vec.iter().map(|e| &*e).collect::<Vec<_>>()[..],
217                 mac.span,
218                 shape,
219                 context.config.width_heuristics().fn_call_width,
220                 trailing_comma,
221             ).map(|rw| match position {
222                 MacroPosition::Item => format!("{};", rw),
223                 _ => rw,
224             })
225         }
226         MacroStyle::Brackets => {
227             let mac_shape = shape.offset_left(macro_name.len())?;
228             // Handle special case: `vec![expr; expr]`
229             if vec_with_semi {
230                 let (lbr, rbr) = if context.config.spaces_within_parens_and_brackets() {
231                     ("[ ", " ]")
232                 } else {
233                     ("[", "]")
234                 };
235                 // 6 = `vec!` + `; `
236                 let total_overhead = lbr.len() + rbr.len() + 6;
237                 let nested_shape = mac_shape.block_indent(context.config.tab_spaces());
238                 let lhs = arg_vec[0].rewrite(context, nested_shape)?;
239                 let rhs = arg_vec[1].rewrite(context, nested_shape)?;
240                 if !lhs.contains('\n') && !rhs.contains('\n')
241                     && lhs.len() + rhs.len() + total_overhead <= shape.width
242                 {
243                     Some(format!("{}{}{}; {}{}", macro_name, lbr, lhs, rhs, rbr))
244                 } else {
245                     Some(format!(
246                         "{}{}\n{}{};\n{}{}\n{}{}",
247                         macro_name,
248                         lbr,
249                         nested_shape.indent.to_string(context.config),
250                         lhs,
251                         nested_shape.indent.to_string(context.config),
252                         rhs,
253                         shape.indent.to_string(context.config),
254                         rbr
255                     ))
256                 }
257             } else {
258                 // If we are rewriting `vec!` macro or other special macros,
259                 // then we can rewrite this as an usual array literal.
260                 // Otherwise, we must preserve the original existence of trailing comma.
261                 if FORCED_BRACKET_MACROS.contains(&macro_name.as_str()) {
262                     context.inside_macro = false;
263                     trailing_comma = false;
264                 }
265                 // Convert `MacroArg` into `ast::Expr`, as `rewrite_array` only accepts the latter.
266                 let sp = mk_sp(
267                     context
268                         .codemap
269                         .span_after(mac.span, original_style.opener()),
270                     mac.span.hi() - BytePos(1),
271                 );
272                 let arg_vec = &arg_vec.iter().map(|e| &*e).collect::<Vec<_>>()[..];
273                 let rewrite = rewrite_array(arg_vec, sp, context, mac_shape, trailing_comma)?;
274
275                 Some(format!("{}{}", macro_name, rewrite))
276             }
277         }
278         MacroStyle::Braces => {
279             // Skip macro invocations with braces, for now.
280             indent_macro_snippet(context, context.snippet(mac.span), shape.indent)
281         }
282     }
283 }
284
285 pub fn rewrite_macro_def(
286     context: &RewriteContext,
287     shape: Shape,
288     indent: Indent,
289     def: &ast::MacroDef,
290     ident: ast::Ident,
291     vis: &ast::Visibility,
292     span: Span,
293 ) -> Option<String> {
294     let snippet = Some(remove_trailing_white_spaces(context.snippet(span)));
295
296     let mut parser = MacroParser::new(def.stream().into_trees());
297     let parsed_def = match parser.parse() {
298         Some(def) => def,
299         None => return snippet,
300     };
301
302     let mut result = if def.legacy {
303         String::from("macro_rules!")
304     } else {
305         format!("{}macro", format_visibility(vis))
306     };
307
308     result += " ";
309     result += &ident.name.as_str();
310
311     let multi_branch_style = def.legacy || parsed_def.branches.len() != 1;
312
313     let mac_indent = if multi_branch_style {
314         result += " {";
315         indent.block_indent(context.config)
316     } else {
317         indent
318     };
319
320     let mac_indent_str = mac_indent.to_string(context.config);
321
322     let branch_items = itemize_list(
323         context.codemap,
324         parsed_def.branches.iter(),
325         "",
326         "",
327         |branch| branch.args_span.lo(),
328         |branch| branch.body.hi(),
329         |branch| {
330             let mut result = String::new();
331
332             // Only attempt to format function-like macros.
333             if branch.args_paren_kind != DelimToken::Paren {
334                 // FIXME(#1539): implement for non-sugared macros.
335                 return None;
336             }
337
338             let args = format_macro_args(branch.args.clone())?;
339
340             if multi_branch_style {
341                 result += "\n";
342                 result += &mac_indent_str;
343                 result += &args;
344                 result += " =>";
345             } else {
346                 result += &args;
347             }
348
349             // The macro body is the most interesting part. It might end up as various
350             // AST nodes, but also has special variables (e.g, `$foo`) which can't be
351             // parsed as regular Rust code (and note that these can be escaped using
352             // `$$`). We'll try and format like an AST node, but we'll substitute
353             // variables for new names with the same length first.
354
355             let old_body = context.snippet(branch.body).trim();
356             let (body_str, substs) = match replace_names(old_body) {
357                 Some(result) => result,
358                 None => return snippet,
359             };
360
361             let mut config = context.config.clone();
362             config.set().hide_parse_errors(true);
363
364             result += " {";
365
366             let has_block_body = old_body.starts_with('{');
367
368             let body_indent = if has_block_body {
369                 mac_indent
370             } else {
371                 // We'll hack the indent below, take this into account when formatting,
372                 let body_indent = mac_indent.block_indent(&config);
373                 let new_width = config.max_width() - body_indent.width();
374                 config.set().max_width(new_width);
375                 body_indent
376             };
377
378             // First try to format as items, then as statements.
379             let new_body = match ::format_snippet(&body_str, &config) {
380                 Some(new_body) => new_body,
381                 None => match ::format_code_block(&body_str, &config) {
382                     Some(new_body) => new_body,
383                     None => return None,
384                 },
385             };
386
387             // Indent the body since it is in a block.
388             let indent_str = body_indent.to_string(&config);
389             let mut new_body = new_body
390                 .trim_right()
391                 .lines()
392                 .fold(String::new(), |mut s, l| {
393                     if !l.is_empty() {
394                         s += &indent_str;
395                     }
396                     s + l + "\n"
397                 });
398
399             // Undo our replacement of macro variables.
400             // FIXME: this could be *much* more efficient.
401             for (old, new) in &substs {
402                 if old_body.find(new).is_some() {
403                     debug!(
404                         "rewrite_macro_def: bailing matching variable: `{}` in `{}`",
405                         new, ident
406                     );
407                     return None;
408                 }
409                 new_body = new_body.replace(new, old);
410             }
411
412             if has_block_body {
413                 result += new_body.trim();
414             } else if !new_body.is_empty() {
415                 result += "\n";
416                 result += &new_body;
417                 result += &mac_indent_str;
418             }
419
420             result += "}";
421             if def.legacy {
422                 result += ";";
423             }
424             result += "\n";
425             Some(result)
426         },
427         span.lo(),
428         span.hi(),
429         false
430     ).collect::<Vec<_>>();
431
432     let arm_shape = shape
433         .block_indent(context.config.tab_spaces())
434         .with_max_width(context.config);
435
436     let fmt = ListFormatting {
437         tactic: DefinitiveListTactic::Vertical,
438         separator: "",
439         trailing_separator: SeparatorTactic::Never,
440         separator_place: SeparatorPlace::Back,
441         shape: arm_shape,
442         ends_with_newline: false,
443         preserve_newline: true,
444         config: context.config,
445     };
446
447     result += write_list(&branch_items, &fmt)?.as_str();
448
449     if multi_branch_style {
450         result += &indent.to_string(context.config);
451         result += "}";
452     }
453
454     Some(result)
455 }
456
457 // Replaces `$foo` with `zfoo`. We must check for name overlap to ensure we
458 // aren't causing problems.
459 // This should also work for escaped `$` variables, where we leave earlier `$`s.
460 fn replace_names(input: &str) -> Option<(String, HashMap<String, String>)> {
461     // Each substitution will require five or six extra bytes.
462     let mut result = String::with_capacity(input.len() + 64);
463     let mut substs = HashMap::new();
464     let mut dollar_count = 0;
465     let mut cur_name = String::new();
466
467     for c in input.chars() {
468         if c == '$' {
469             dollar_count += 1;
470         } else if dollar_count == 0 {
471             result.push(c);
472         } else if !c.is_alphanumeric() && !cur_name.is_empty() {
473             // Terminates a name following one or more dollars.
474             let mut new_name = String::new();
475             let mut old_name = String::new();
476             old_name.push('$');
477             for _ in 0..(dollar_count - 1) {
478                 new_name.push('$');
479                 old_name.push('$');
480             }
481             new_name.push('z');
482             new_name.push_str(&cur_name);
483             old_name.push_str(&cur_name);
484
485             result.push_str(&new_name);
486             substs.insert(old_name, new_name);
487
488             result.push(c);
489
490             dollar_count = 0;
491             cur_name = String::new();
492         } else if c == '(' && cur_name.is_empty() {
493             // FIXME: Support macro def with repeat.
494             return None;
495         } else if c.is_alphanumeric() {
496             cur_name.push(c);
497         }
498     }
499
500     // FIXME: duplicate code
501     if !cur_name.is_empty() {
502         let mut new_name = String::new();
503         let mut old_name = String::new();
504         old_name.push('$');
505         for _ in 0..(dollar_count - 1) {
506             new_name.push('$');
507             old_name.push('$');
508         }
509         new_name.push('z');
510         new_name.push_str(&cur_name);
511         old_name.push_str(&cur_name);
512
513         result.push_str(&new_name);
514         substs.insert(old_name, new_name);
515     }
516
517     debug!("replace_names `{}` {:?}", result, substs);
518
519     Some((result, substs))
520 }
521
522 // This is a bit sketchy. The token rules probably need tweaking, but it works
523 // for some common cases. I hope the basic logic is sufficient. Note that the
524 // meaning of some tokens is a bit different here from usual Rust, e.g., `*`
525 // and `(`/`)` have special meaning.
526 //
527 // We always try and format on one line.
528 fn format_macro_args(toks: ThinTokenStream) -> Option<String> {
529     let mut result = String::with_capacity(128);
530     let mut insert_space = SpaceState::Never;
531
532     for tok in (toks.into(): TokenStream).trees() {
533         match tok {
534             TokenTree::Token(_, t) => {
535                 if !result.is_empty() && force_space_before(&t) {
536                     insert_space = SpaceState::Always;
537                 }
538                 if force_no_space_before(&t) {
539                     insert_space = SpaceState::Never;
540                 }
541                 match (insert_space, ident_like(&t)) {
542                     (SpaceState::Always, _)
543                     | (SpaceState::Punctuation, false)
544                     | (SpaceState::Ident, true) => {
545                         result.push(' ');
546                     }
547                     _ => {}
548                 }
549                 result.push_str(&pprust::token_to_string(&t));
550                 insert_space = next_space(&t);
551             }
552             TokenTree::Delimited(_, d) => {
553                 if let SpaceState::Always = insert_space {
554                     result.push(' ');
555                 }
556                 let formatted = format_macro_args(d.tts)?;
557                 match d.delim {
558                     DelimToken::Paren => {
559                         result.push_str(&format!("({})", formatted));
560                         insert_space = SpaceState::Always;
561                     }
562                     DelimToken::Bracket => {
563                         result.push_str(&format!("[{}]", formatted));
564                         insert_space = SpaceState::Always;
565                     }
566                     DelimToken::Brace => {
567                         result.push_str(&format!(" {{ {} }}", formatted));
568                         insert_space = SpaceState::Always;
569                     }
570                     DelimToken::NoDelim => {
571                         result.push_str(&format!("{}", formatted));
572                         insert_space = SpaceState::Always;
573                     }
574                 }
575             }
576         }
577     }
578
579     Some(result)
580 }
581
582 // We should insert a space if the next token is a:
583 #[derive(Copy, Clone)]
584 enum SpaceState {
585     Never,
586     Punctuation,
587     Ident, // Or ident/literal-like thing.
588     Always,
589 }
590
591 fn force_space_before(tok: &Token) -> bool {
592     match *tok {
593         Token::Eq
594         | Token::Lt
595         | Token::Le
596         | Token::EqEq
597         | Token::Ne
598         | Token::Ge
599         | Token::Gt
600         | Token::AndAnd
601         | Token::OrOr
602         | Token::Not
603         | Token::Tilde
604         | Token::BinOpEq(_)
605         | Token::At
606         | Token::RArrow
607         | Token::LArrow
608         | Token::FatArrow
609         | Token::Pound
610         | Token::Dollar => true,
611         Token::BinOp(bot) => bot != BinOpToken::Star,
612         _ => false,
613     }
614 }
615
616 fn force_no_space_before(tok: &Token) -> bool {
617     match *tok {
618         Token::Semi | Token::Comma | Token::Dot => true,
619         Token::BinOp(bot) => bot == BinOpToken::Star,
620         _ => false,
621     }
622 }
623 fn ident_like(tok: &Token) -> bool {
624     match *tok {
625         Token::Ident(_) | Token::Literal(..) | Token::Lifetime(_) => true,
626         _ => false,
627     }
628 }
629
630 fn next_space(tok: &Token) -> SpaceState {
631     match *tok {
632         Token::Not
633         | Token::Tilde
634         | Token::At
635         | Token::Comma
636         | Token::Dot
637         | Token::DotDot
638         | Token::DotDotDot
639         | Token::DotDotEq
640         | Token::DotEq
641         | Token::Question
642         | Token::Underscore
643         | Token::BinOp(_) => SpaceState::Punctuation,
644
645         Token::ModSep
646         | Token::Pound
647         | Token::Dollar
648         | Token::OpenDelim(_)
649         | Token::CloseDelim(_)
650         | Token::Whitespace => SpaceState::Never,
651
652         Token::Literal(..) | Token::Ident(_) | Token::Lifetime(_) => SpaceState::Ident,
653
654         _ => SpaceState::Always,
655     }
656 }
657
658 /// Tries to convert a macro use into a short hand try expression. Returns None
659 /// when the macro is not an instance of try! (or parsing the inner expression
660 /// failed).
661 pub fn convert_try_mac(mac: &ast::Mac, context: &RewriteContext) -> Option<ast::Expr> {
662     if &format!("{}", mac.node.path)[..] == "try" {
663         let ts: TokenStream = mac.node.tts.clone().into();
664         let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect());
665
666         Some(ast::Expr {
667             id: ast::NodeId::new(0), // dummy value
668             node: ast::ExprKind::Try(parser.parse_expr().ok()?),
669             span: mac.span, // incorrect span, but shouldn't matter too much
670             attrs: ThinVec::new(),
671         })
672     } else {
673         None
674     }
675 }
676
677 fn macro_style(mac: &ast::Mac, context: &RewriteContext) -> MacroStyle {
678     let snippet = context.snippet(mac.span);
679     let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value());
680     let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value());
681     let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value());
682
683     if paren_pos < bracket_pos && paren_pos < brace_pos {
684         MacroStyle::Parens
685     } else if bracket_pos < brace_pos {
686         MacroStyle::Brackets
687     } else {
688         MacroStyle::Braces
689     }
690 }
691
692 /// Indent each line according to the specified `indent`.
693 /// e.g.
694 /// ```rust
695 /// foo!{
696 /// x,
697 /// y,
698 /// foo(
699 ///     a,
700 ///     b,
701 ///     c,
702 /// ),
703 /// }
704 /// ```
705 /// will become
706 /// ```rust
707 /// foo!{
708 ///     x,
709 ///     y,
710 ///     foo(
711 ///         a,
712 ///         b,
713 ///         c,
714 //      ),
715 /// }
716 /// ```
717 fn indent_macro_snippet(
718     context: &RewriteContext,
719     macro_str: &str,
720     indent: Indent,
721 ) -> Option<String> {
722     let mut lines = macro_str.lines();
723     let first_line = lines.next().map(|s| s.trim_right())?;
724     let mut trimmed_lines = Vec::with_capacity(16);
725
726     let min_prefix_space_width = lines
727         .filter_map(|line| {
728             let prefix_space_width = if is_empty_line(line) {
729                 None
730             } else {
731                 Some(get_prefix_space_width(context, line))
732             };
733             trimmed_lines.push((line.trim(), prefix_space_width));
734             prefix_space_width
735         })
736         .min()?;
737
738     Some(
739         String::from(first_line) + "\n"
740             + &trimmed_lines
741                 .iter()
742                 .map(|&(line, prefix_space_width)| match prefix_space_width {
743                     Some(original_indent_width) => {
744                         let new_indent_width = indent.width()
745                             + original_indent_width
746                                 .checked_sub(min_prefix_space_width)
747                                 .unwrap_or(0);
748                         let new_indent = Indent::from_width(context.config, new_indent_width);
749                         format!("{}{}", new_indent.to_string(context.config), line.trim())
750                     }
751                     None => String::new(),
752                 })
753                 .collect::<Vec<_>>()
754                 .join("\n"),
755     )
756 }
757
758 fn get_prefix_space_width(context: &RewriteContext, s: &str) -> usize {
759     let mut width = 0;
760     for c in s.chars() {
761         match c {
762             ' ' => width += 1,
763             '\t' => width += context.config.tab_spaces(),
764             _ => return width,
765         }
766     }
767     width
768 }
769
770 fn is_empty_line(s: &str) -> bool {
771     s.is_empty() || s.chars().all(char::is_whitespace)
772 }
773
774 // A very simple parser that just parses a macros 2.0 definition into its branches.
775 // Currently we do not attempt to parse any further than that.
776 #[derive(new)]
777 struct MacroParser {
778     toks: Cursor,
779 }
780
781 impl MacroParser {
782     // (`(` ... `)` `=>` `{` ... `}`)*
783     fn parse(&mut self) -> Option<Macro> {
784         let mut branches = vec![];
785         while self.toks.look_ahead(1).is_some() {
786             branches.push(self.parse_branch()?);
787         }
788
789         Some(Macro { branches })
790     }
791
792     // `(` ... `)` `=>` `{` ... `}`
793     fn parse_branch(&mut self) -> Option<MacroBranch> {
794         let tok = self.toks.next()?;
795         let (args_span, args_paren_kind) = match tok {
796             TokenTree::Token(..) => return None,
797             TokenTree::Delimited(sp, ref d) => (sp, d.delim),
798         };
799         let args = tok.joint().into();
800         match self.toks.next()? {
801             TokenTree::Token(_, Token::FatArrow) => {}
802             _ => return None,
803         }
804         let body = match self.toks.next()? {
805             TokenTree::Token(..) => return None,
806             TokenTree::Delimited(sp, _) => {
807                 let data = sp.data();
808                 Span::new(data.lo + BytePos(1), data.hi - BytePos(1), data.ctxt)
809             }
810         };
811         if let Some(TokenTree::Token(_, Token::Semi)) = self.toks.look_ahead(0) {
812             self.toks.next();
813         }
814         Some(MacroBranch {
815             args_paren_kind,
816             args_span,
817             args,
818             body,
819         })
820     }
821 }
822
823 // A parsed macros 2.0 macro definition.
824 struct Macro {
825     branches: Vec<MacroBranch>,
826 }
827
828 // FIXME: it would be more efficient to use references to the token streams
829 // rather than clone them, if we can make the borrowing work out.
830 struct MacroBranch {
831     args_paren_kind: DelimToken,
832     args_span: Span,
833     args: ThinTokenStream,
834     body: Span,
835 }
836
837 #[cfg(test)]
838 mod test {
839     use super::*;
840     use syntax::parse::{parse_stream_from_source_str, ParseSess};
841     use syntax::codemap::{FileName, FilePathMapping};
842
843     fn format_macro_args_str(s: &str) -> String {
844         let input = parse_stream_from_source_str(
845             FileName::Custom("stdin".to_owned()),
846             s.to_owned(),
847             &ParseSess::new(FilePathMapping::empty()),
848             None,
849         );
850         format_macro_args(input.into()).unwrap()
851     }
852
853     #[test]
854     fn test_format_macro_args() {
855         assert_eq!(format_macro_args_str(""), "".to_owned());
856         assert_eq!(format_macro_args_str("$ x : ident"), "$x: ident".to_owned());
857         assert_eq!(
858             format_macro_args_str("$ m1 : ident , $ m2 : ident , $ x : ident"),
859             "$m1: ident, $m2: ident, $x: ident".to_owned()
860         );
861         assert_eq!(
862             format_macro_args_str("$($beginning:ident),*;$middle:ident;$($end:ident),*"),
863             "$($beginning: ident),*; $middle: ident; $($end: ident),*".to_owned()
864         );
865         assert_eq!(
866             format_macro_args_str(
867                 "$ name : ident ( $ ( $ dol : tt $ var : ident ) * ) $ ( $ body : tt ) *"
868             ),
869             "$name: ident($($dol: tt $var: ident)*) $($body: tt)*".to_owned()
870         );
871     }
872 }