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