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