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