]> git.lizzy.rs Git - rust.git/blob - src/macros.rs
Format
[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         indent.block_indent(context.config)
315     } else {
316         indent
317     };
318
319     let mac_indent_str = mac_indent.to_string(context.config);
320
321     let branch_items = itemize_list(
322         context.codemap,
323         parsed_def.branches.iter(),
324         "}",
325         ";",
326         |branch| branch.span.lo(),
327         |branch| branch.span.hi(),
328         |branch| {
329             // Only attempt to format function-like macros.
330             if branch.args_paren_kind != DelimToken::Paren {
331                 // FIXME(#1539): implement for non-sugared macros.
332                 return None;
333             }
334
335             let mut result = format_macro_args(branch.args.clone())?;
336
337             if multi_branch_style {
338                 result += " =>";
339             }
340
341             // The macro body is the most interesting part. It might end up as various
342             // AST nodes, but also has special variables (e.g, `$foo`) which can't be
343             // parsed as regular Rust code (and note that these can be escaped using
344             // `$$`). We'll try and format like an AST node, but we'll substitute
345             // variables for new names with the same length first.
346
347             let old_body = context.snippet(branch.body).trim();
348             let (body_str, substs) = match replace_names(old_body) {
349                 Some(result) => result,
350                 None => return snippet,
351             };
352
353             let mut config = context.config.clone();
354             config.set().hide_parse_errors(true);
355
356             result += " {";
357
358             let has_block_body = old_body.starts_with('{');
359
360             let body_indent = if has_block_body {
361                 mac_indent
362             } else {
363                 // We'll hack the indent below, take this into account when formatting,
364                 let body_indent = mac_indent.block_indent(&config);
365                 let new_width = config.max_width() - body_indent.width();
366                 config.set().max_width(new_width);
367                 body_indent
368             };
369
370             // First try to format as items, then as statements.
371             let new_body = match ::format_snippet(&body_str, &config) {
372                 Some(new_body) => new_body,
373                 None => match ::format_code_block(&body_str, &config) {
374                     Some(new_body) => new_body,
375                     None => return None,
376                 },
377             };
378
379             // Indent the body since it is in a block.
380             let indent_str = body_indent.to_string(&config);
381             let mut new_body = new_body
382                 .trim_right()
383                 .lines()
384                 .fold(String::new(), |mut s, l| {
385                     if !l.is_empty() {
386                         s += &indent_str;
387                     }
388                     s + l + "\n"
389                 });
390
391             // Undo our replacement of macro variables.
392             // FIXME: this could be *much* more efficient.
393             for (old, new) in &substs {
394                 if old_body.find(new).is_some() {
395                     debug!(
396                         "rewrite_macro_def: bailing matching variable: `{}` in `{}`",
397                         new, ident
398                     );
399                     return None;
400                 }
401                 new_body = new_body.replace(new, old);
402             }
403
404             if has_block_body {
405                 result += new_body.trim();
406             } else if !new_body.is_empty() {
407                 result += "\n";
408                 result += &new_body;
409                 result += &mac_indent_str;
410             }
411
412             result += "}";
413
414             Some(result)
415         },
416         context.codemap.span_after(span, "{"),
417         span.hi(),
418         false,
419     ).collect::<Vec<_>>();
420
421     let arm_shape = shape
422         .block_indent(context.config.tab_spaces())
423         .with_max_width(context.config);
424
425     let fmt = ListFormatting {
426         tactic: DefinitiveListTactic::Vertical,
427         separator: if def.legacy { ";" } else { "" },
428         trailing_separator: SeparatorTactic::Always,
429         separator_place: SeparatorPlace::Back,
430         shape: arm_shape,
431         ends_with_newline: true,
432         preserve_newline: true,
433         config: context.config,
434     };
435
436     if multi_branch_style {
437         result += " {\n";
438         result += &mac_indent_str;
439     }
440
441     result += write_list(&branch_items, &fmt)?.as_str();
442
443     if multi_branch_style {
444         result += "\n";
445         result += &indent.to_string(context.config);
446         result += "}";
447     }
448
449     Some(result)
450 }
451
452 // Replaces `$foo` with `zfoo`. We must check for name overlap to ensure we
453 // aren't causing problems.
454 // This should also work for escaped `$` variables, where we leave earlier `$`s.
455 fn replace_names(input: &str) -> Option<(String, HashMap<String, String>)> {
456     // Each substitution will require five or six extra bytes.
457     let mut result = String::with_capacity(input.len() + 64);
458     let mut substs = HashMap::new();
459     let mut dollar_count = 0;
460     let mut cur_name = String::new();
461
462     for c in input.chars() {
463         if c == '$' {
464             dollar_count += 1;
465         } else if dollar_count == 0 {
466             result.push(c);
467         } else if !c.is_alphanumeric() && !cur_name.is_empty() {
468             // Terminates a name following one or more dollars.
469             let mut new_name = String::new();
470             let mut old_name = String::new();
471             old_name.push('$');
472             for _ in 0..(dollar_count - 1) {
473                 new_name.push('$');
474                 old_name.push('$');
475             }
476             new_name.push('z');
477             new_name.push_str(&cur_name);
478             old_name.push_str(&cur_name);
479
480             result.push_str(&new_name);
481             substs.insert(old_name, new_name);
482
483             result.push(c);
484
485             dollar_count = 0;
486             cur_name = String::new();
487         } else if c == '(' && cur_name.is_empty() {
488             // FIXME: Support macro def with repeat.
489             return None;
490         } else if c.is_alphanumeric() {
491             cur_name.push(c);
492         }
493     }
494
495     // FIXME: duplicate code
496     if !cur_name.is_empty() {
497         let mut new_name = String::new();
498         let mut old_name = String::new();
499         old_name.push('$');
500         for _ in 0..(dollar_count - 1) {
501             new_name.push('$');
502             old_name.push('$');
503         }
504         new_name.push('z');
505         new_name.push_str(&cur_name);
506         old_name.push_str(&cur_name);
507
508         result.push_str(&new_name);
509         substs.insert(old_name, new_name);
510     }
511
512     debug!("replace_names `{}` {:?}", result, substs);
513
514     Some((result, substs))
515 }
516
517 // This is a bit sketchy. The token rules probably need tweaking, but it works
518 // for some common cases. I hope the basic logic is sufficient. Note that the
519 // meaning of some tokens is a bit different here from usual Rust, e.g., `*`
520 // and `(`/`)` have special meaning.
521 //
522 // We always try and format on one line.
523 fn format_macro_args(toks: ThinTokenStream) -> Option<String> {
524     let mut result = String::with_capacity(128);
525     let mut insert_space = SpaceState::Never;
526
527     for tok in (toks.into(): TokenStream).trees() {
528         match tok {
529             TokenTree::Token(_, t) => {
530                 if !result.is_empty() && force_space_before(&t) {
531                     insert_space = SpaceState::Always;
532                 }
533                 if force_no_space_before(&t) {
534                     insert_space = SpaceState::Never;
535                 }
536                 match (insert_space, ident_like(&t)) {
537                     (SpaceState::Always, _)
538                     | (SpaceState::Punctuation, false)
539                     | (SpaceState::Ident, true) => {
540                         result.push(' ');
541                     }
542                     _ => {}
543                 }
544                 result.push_str(&pprust::token_to_string(&t));
545                 insert_space = next_space(&t);
546             }
547             TokenTree::Delimited(_, d) => {
548                 if let SpaceState::Always = insert_space {
549                     result.push(' ');
550                 }
551                 let formatted = format_macro_args(d.tts)?;
552                 match d.delim {
553                     DelimToken::Paren => {
554                         result.push_str(&format!("({})", formatted));
555                         insert_space = SpaceState::Always;
556                     }
557                     DelimToken::Bracket => {
558                         result.push_str(&format!("[{}]", formatted));
559                         insert_space = SpaceState::Always;
560                     }
561                     DelimToken::Brace => {
562                         result.push_str(&format!(" {{ {} }}", formatted));
563                         insert_space = SpaceState::Always;
564                     }
565                     DelimToken::NoDelim => {
566                         result.push_str(&format!("{}", formatted));
567                         insert_space = SpaceState::Always;
568                     }
569                 }
570             }
571         }
572     }
573
574     Some(result)
575 }
576
577 // We should insert a space if the next token is a:
578 #[derive(Copy, Clone)]
579 enum SpaceState {
580     Never,
581     Punctuation,
582     Ident, // Or ident/literal-like thing.
583     Always,
584 }
585
586 fn force_space_before(tok: &Token) -> bool {
587     match *tok {
588         Token::Eq
589         | Token::Lt
590         | Token::Le
591         | Token::EqEq
592         | Token::Ne
593         | Token::Ge
594         | Token::Gt
595         | Token::AndAnd
596         | Token::OrOr
597         | Token::Not
598         | Token::Tilde
599         | Token::BinOpEq(_)
600         | Token::At
601         | Token::RArrow
602         | Token::LArrow
603         | Token::FatArrow
604         | Token::Pound
605         | Token::Dollar => true,
606         Token::BinOp(bot) => bot != BinOpToken::Star,
607         _ => false,
608     }
609 }
610
611 fn force_no_space_before(tok: &Token) -> bool {
612     match *tok {
613         Token::Semi | Token::Comma | Token::Dot => true,
614         Token::BinOp(bot) => bot == BinOpToken::Star,
615         _ => false,
616     }
617 }
618 fn ident_like(tok: &Token) -> bool {
619     match *tok {
620         Token::Ident(_) | Token::Literal(..) | Token::Lifetime(_) => true,
621         _ => false,
622     }
623 }
624
625 fn next_space(tok: &Token) -> SpaceState {
626     match *tok {
627         Token::Not
628         | Token::Tilde
629         | Token::At
630         | Token::Comma
631         | Token::Dot
632         | Token::DotDot
633         | Token::DotDotDot
634         | Token::DotDotEq
635         | Token::DotEq
636         | Token::Question
637         | Token::Underscore
638         | Token::BinOp(_) => SpaceState::Punctuation,
639
640         Token::ModSep
641         | Token::Pound
642         | Token::Dollar
643         | Token::OpenDelim(_)
644         | Token::CloseDelim(_)
645         | Token::Whitespace => SpaceState::Never,
646
647         Token::Literal(..) | Token::Ident(_) | Token::Lifetime(_) => SpaceState::Ident,
648
649         _ => SpaceState::Always,
650     }
651 }
652
653 /// Tries to convert a macro use into a short hand try expression. Returns None
654 /// when the macro is not an instance of try! (or parsing the inner expression
655 /// failed).
656 pub fn convert_try_mac(mac: &ast::Mac, context: &RewriteContext) -> Option<ast::Expr> {
657     if &format!("{}", mac.node.path)[..] == "try" {
658         let ts: TokenStream = mac.node.tts.clone().into();
659         let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect());
660
661         Some(ast::Expr {
662             id: ast::NodeId::new(0), // dummy value
663             node: ast::ExprKind::Try(parser.parse_expr().ok()?),
664             span: mac.span, // incorrect span, but shouldn't matter too much
665             attrs: ThinVec::new(),
666         })
667     } else {
668         None
669     }
670 }
671
672 fn macro_style(mac: &ast::Mac, context: &RewriteContext) -> MacroStyle {
673     let snippet = context.snippet(mac.span);
674     let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value());
675     let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value());
676     let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value());
677
678     if paren_pos < bracket_pos && paren_pos < brace_pos {
679         MacroStyle::Parens
680     } else if bracket_pos < brace_pos {
681         MacroStyle::Brackets
682     } else {
683         MacroStyle::Braces
684     }
685 }
686
687 /// Indent each line according to the specified `indent`.
688 /// e.g.
689 /// ```rust
690 /// foo!{
691 /// x,
692 /// y,
693 /// foo(
694 ///     a,
695 ///     b,
696 ///     c,
697 /// ),
698 /// }
699 /// ```
700 /// will become
701 /// ```rust
702 /// foo!{
703 ///     x,
704 ///     y,
705 ///     foo(
706 ///         a,
707 ///         b,
708 ///         c,
709 //      ),
710 /// }
711 /// ```
712 fn indent_macro_snippet(
713     context: &RewriteContext,
714     macro_str: &str,
715     indent: Indent,
716 ) -> Option<String> {
717     let mut lines = macro_str.lines();
718     let first_line = lines.next().map(|s| s.trim_right())?;
719     let mut trimmed_lines = Vec::with_capacity(16);
720
721     let min_prefix_space_width = lines
722         .filter_map(|line| {
723             let prefix_space_width = if is_empty_line(line) {
724                 None
725             } else {
726                 Some(get_prefix_space_width(context, line))
727             };
728             trimmed_lines.push((line.trim(), prefix_space_width));
729             prefix_space_width
730         })
731         .min()?;
732
733     Some(
734         String::from(first_line) + "\n"
735             + &trimmed_lines
736                 .iter()
737                 .map(|&(line, prefix_space_width)| match prefix_space_width {
738                     Some(original_indent_width) => {
739                         let new_indent_width = indent.width()
740                             + original_indent_width
741                                 .checked_sub(min_prefix_space_width)
742                                 .unwrap_or(0);
743                         let new_indent = Indent::from_width(context.config, new_indent_width);
744                         format!("{}{}", new_indent.to_string(context.config), line.trim())
745                     }
746                     None => String::new(),
747                 })
748                 .collect::<Vec<_>>()
749                 .join("\n"),
750     )
751 }
752
753 fn get_prefix_space_width(context: &RewriteContext, s: &str) -> usize {
754     let mut width = 0;
755     for c in s.chars() {
756         match c {
757             ' ' => width += 1,
758             '\t' => width += context.config.tab_spaces(),
759             _ => return width,
760         }
761     }
762     width
763 }
764
765 fn is_empty_line(s: &str) -> bool {
766     s.is_empty() || s.chars().all(char::is_whitespace)
767 }
768
769 // A very simple parser that just parses a macros 2.0 definition into its branches.
770 // Currently we do not attempt to parse any further than that.
771 #[derive(new)]
772 struct MacroParser {
773     toks: Cursor,
774 }
775
776 impl MacroParser {
777     // (`(` ... `)` `=>` `{` ... `}`)*
778     fn parse(&mut self) -> Option<Macro> {
779         let mut branches = vec![];
780         while self.toks.look_ahead(1).is_some() {
781             branches.push(self.parse_branch()?);
782         }
783
784         Some(Macro { branches })
785     }
786
787     // `(` ... `)` `=>` `{` ... `}`
788     fn parse_branch(&mut self) -> Option<MacroBranch> {
789         let tok = self.toks.next()?;
790         let (lo, args_paren_kind) = match tok {
791             TokenTree::Token(..) => return None,
792             TokenTree::Delimited(sp, ref d) => (sp.lo(), d.delim),
793         };
794         let args = tok.joint().into();
795         match self.toks.next()? {
796             TokenTree::Token(_, Token::FatArrow) => {}
797             _ => return None,
798         }
799         let (mut hi, body) = match self.toks.next()? {
800             TokenTree::Token(..) => return None,
801             TokenTree::Delimited(sp, _) => {
802                 let data = sp.data();
803                 (
804                     data.hi,
805                     Span::new(data.lo + BytePos(1), data.hi - BytePos(1), data.ctxt),
806                 )
807             }
808         };
809         if let Some(TokenTree::Token(sp, Token::Semi)) = self.toks.look_ahead(0) {
810             self.toks.next();
811             hi = sp.hi();
812         }
813         Some(MacroBranch {
814             span: mk_sp(lo, hi),
815             args_paren_kind,
816             args,
817             body,
818         })
819     }
820 }
821
822 // A parsed macros 2.0 macro definition.
823 struct Macro {
824     branches: Vec<MacroBranch>,
825 }
826
827 // FIXME: it would be more efficient to use references to the token streams
828 // rather than clone them, if we can make the borrowing work out.
829 struct MacroBranch {
830     span: Span,
831     args_paren_kind: DelimToken,
832     args: ThinTokenStream,
833     body: Span,
834 }
835
836 #[cfg(test)]
837 mod test {
838     use super::*;
839     use syntax::parse::{parse_stream_from_source_str, ParseSess};
840     use syntax::codemap::{FileName, FilePathMapping};
841
842     fn format_macro_args_str(s: &str) -> String {
843         let input = parse_stream_from_source_str(
844             FileName::Custom("stdin".to_owned()),
845             s.to_owned(),
846             &ParseSess::new(FilePathMapping::empty()),
847             None,
848         );
849         format_macro_args(input.into()).unwrap()
850     }
851
852     #[test]
853     fn test_format_macro_args() {
854         assert_eq!(format_macro_args_str(""), "".to_owned());
855         assert_eq!(format_macro_args_str("$ x : ident"), "$x: ident".to_owned());
856         assert_eq!(
857             format_macro_args_str("$ m1 : ident , $ m2 : ident , $ x : ident"),
858             "$m1: ident, $m2: ident, $x: ident".to_owned()
859         );
860         assert_eq!(
861             format_macro_args_str("$($beginning:ident),*;$middle:ident;$($end:ident),*"),
862             "$($beginning: ident),*; $middle: ident; $($end: ident),*".to_owned()
863         );
864         assert_eq!(
865             format_macro_args_str(
866                 "$ name : ident ( $ ( $ dol : tt $ var : ident ) * ) $ ( $ body : tt ) *"
867             ),
868             "$name: ident($($dol: tt $var: ident)*) $($body: tt)*".to_owned()
869         );
870     }
871 }