]> git.lizzy.rs Git - rust.git/blobdiff - src/macros.rs
Merge pull request #3266 from wada314/fix-2973
[rust.git] / src / macros.rs
index 43f3071b70a696bb2c80cfd6af65bc08757b4b76..9ce5c913ca55f48a82cadc4c00923098d9025281 100644 (file)
@@ -30,7 +30,7 @@
 use syntax::symbol;
 use syntax::tokenstream::{Cursor, ThinTokenStream, TokenStream, TokenTree};
 use syntax::ThinVec;
-use syntax::{ast, ptr};
+use syntax::{ast, parse, ptr};
 
 use comment::{contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses};
 use expr::rewrite_array;
 use shape::{Indent, Shape};
 use source_map::SpanUtils;
 use spanned::Spanned;
-use utils::{format_visibility, mk_sp, remove_trailing_white_spaces, rewrite_ident, wrap_str};
+use utils::{
+    format_visibility, is_empty_line, mk_sp, remove_trailing_white_spaces, rewrite_ident,
+    trim_left_preserve_layout, wrap_str, NodeIdExt,
+};
 use visitor::FmtVisitor;
 
 const FORCED_BRACKET_MACROS: &[&str] = &["vec!"];
@@ -91,11 +94,11 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
     }
 }
 
-fn parse_macro_arg(parser: &mut Parser) -> Option<MacroArg> {
+fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
     macro_rules! parse_macro_arg {
-        ($macro_arg:ident, $parser:ident, $f:expr) => {
+        ($macro_arg:ident, $parser:expr, $f:expr) => {
             let mut cloned_parser = (*parser).clone();
-            match cloned_parser.$parser() {
+            match $parser(&mut cloned_parser) {
                 Ok(x) => {
                     if parser.sess.span_diagnostic.has_errors() {
                         parser.sess.span_diagnostic.reset_err_count();
@@ -113,11 +116,27 @@ macro_rules! parse_macro_arg {
         };
     }
 
-    parse_macro_arg!(Expr, parse_expr, |x: ptr::P<ast::Expr>| Some(x));
-    parse_macro_arg!(Ty, parse_ty, |x: ptr::P<ast::Ty>| Some(x));
-    parse_macro_arg!(Pat, parse_pat, |x: ptr::P<ast::Pat>| Some(x));
+    parse_macro_arg!(
+        Expr,
+        |parser: &mut parse::parser::Parser<'b>| parser.parse_expr(),
+        |x: ptr::P<ast::Expr>| Some(x)
+    );
+    parse_macro_arg!(
+        Ty,
+        |parser: &mut parse::parser::Parser<'b>| parser.parse_ty(),
+        |x: ptr::P<ast::Ty>| Some(x)
+    );
+    parse_macro_arg!(
+        Pat,
+        |parser: &mut parse::parser::Parser<'b>| parser.parse_pat(None),
+        |x: ptr::P<ast::Pat>| Some(x)
+    );
     // `parse_item` returns `Option<ptr::P<ast::Item>>`.
-    parse_macro_arg!(Item, parse_item, |x: Option<ptr::P<ast::Item>>| x);
+    parse_macro_arg!(
+        Item,
+        |parser: &mut parse::parser::Parser<'b>| parser.parse_item(),
+        |x: Option<ptr::P<ast::Item>>| x
+    );
 
     None
 }
@@ -141,11 +160,32 @@ fn rewrite_macro_name(
 }
 
 // Use this on failing to format the macro call.
-fn return_original_snippet_with_failure_marked(
+fn return_macro_parse_failure_fallback(
     context: &RewriteContext,
+    indent: Indent,
     span: Span,
 ) -> Option<String> {
+    // Mark this as a failure however we format it
     context.macro_rewrite_failure.replace(true);
+
+    // Heuristically determine whether the last line of the macro uses "Block" style
+    // rather than using "Visual" style, or another indentation style.
+    let is_like_block_indent_style = context
+        .snippet(span)
+        .lines()
+        .last()
+        .map(|closing_line| {
+            closing_line.trim().chars().all(|ch| match ch {
+                '}' | ')' | ']' => true,
+                _ => false,
+            })
+        })
+        .unwrap_or(false);
+    if is_like_block_indent_style {
+        return trim_left_preserve_layout(context.snippet(span), indent, &context.config);
+    }
+
+    // Return the snippet unmodified if the macro is not block-like
     Some(context.snippet(span).to_owned())
 }
 
@@ -216,7 +256,7 @@ pub fn rewrite_macro_inner(
             }
             DelimToken::Paren => Some(format!("{}()", macro_name)),
             DelimToken::Bracket => Some(format!("{}[]", macro_name)),
-            DelimToken::Brace => Some(format!("{}{{}}", macro_name)),
+            DelimToken::Brace => Some(format!("{} {{}}", macro_name)),
             _ => unreachable!(),
         };
     }
@@ -236,7 +276,9 @@ pub fn rewrite_macro_inner(
         loop {
             match parse_macro_arg(&mut parser) {
                 Some(arg) => arg_vec.push(arg),
-                None => return return_original_snippet_with_failure_marked(context, mac.span),
+                None => {
+                    return return_macro_parse_failure_fallback(context, shape.indent, mac.span);
+                }
             }
 
             match parser.token {
@@ -257,17 +299,19 @@ pub fn rewrite_macro_inner(
                                     }
                                 }
                                 None => {
-                                    return return_original_snippet_with_failure_marked(
-                                        context, mac.span,
-                                    )
+                                    return return_macro_parse_failure_fallback(
+                                        context,
+                                        shape.indent,
+                                        mac.span,
+                                    );
                                 }
                             }
                         }
                     }
-                    return return_original_snippet_with_failure_marked(context, mac.span);
+                    return return_macro_parse_failure_fallback(context, shape.indent, mac.span);
                 }
                 _ if arg_vec.last().map_or(false, MacroArg::is_item) => continue,
-                _ => return return_original_snippet_with_failure_marked(context, mac.span),
+                _ => return return_macro_parse_failure_fallback(context, shape.indent, mac.span),
             }
 
             parser.bump();
@@ -372,8 +416,15 @@ pub fn rewrite_macro_inner(
             }
         }
         DelimToken::Brace => {
-            // Skip macro invocations with braces, for now.
-            indent_macro_snippet(context, context.snippet(mac.span), shape.indent)
+            // For macro invocations with braces, always put a space between
+            // the `macro_name!` and `{ /* macro_body */ }` but skip modifying
+            // anything in between the braces (for now).
+            let snippet = context.snippet(mac.span);
+            let macro_raw = snippet.split_at(snippet.find('!')? + 1).1.trim_start();
+            match trim_left_preserve_layout(macro_raw, shape.indent, &context.config) {
+                Some(macro_body) => Some(format!("{} {}", macro_name, macro_body)),
+                None => Some(format!("{} {}", macro_name, macro_raw)),
+            }
         }
         _ => unreachable!(),
     }
@@ -1051,7 +1102,6 @@ fn next_space(tok: &Token) -> SpaceState {
         | Token::DotDot
         | Token::DotDotDot
         | Token::DotDotEq
-        | Token::DotEq
         | Token::Question => SpaceState::Punctuation,
 
         Token::ModSep
@@ -1076,7 +1126,7 @@ pub fn convert_try_mac(mac: &ast::Mac, context: &RewriteContext) -> Option<ast::
         let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect());
 
         Some(ast::Expr {
-            id: ast::NodeId::new(0), // dummy value
+            id: ast::NodeId::root(), // dummy value
             node: ast::ExprKind::Try(parser.parse_expr().ok()?),
             span: mac.span, // incorrect span, but shouldn't matter too much
             attrs: ThinVec::new(),
@@ -1101,108 +1151,6 @@ fn macro_style(mac: &ast::Mac, context: &RewriteContext) -> DelimToken {
     }
 }
 
-/// Indent each line according to the specified `indent`.
-/// e.g.
-///
-/// ```rust,ignore
-/// foo!{
-/// x,
-/// y,
-/// foo(
-///     a,
-///     b,
-///     c,
-/// ),
-/// }
-/// ```
-///
-/// will become
-///
-/// ```rust,ignore
-/// foo!{
-///     x,
-///     y,
-///     foo(
-///         a,
-///         b,
-///         c,
-///     ),
-/// }
-/// ```
-fn indent_macro_snippet(
-    context: &RewriteContext,
-    macro_str: &str,
-    indent: Indent,
-) -> Option<String> {
-    let mut lines = LineClasses::new(macro_str);
-    let first_line = lines.next().map(|(_, s)| s.trim_right().to_owned())?;
-    let mut trimmed_lines = Vec::with_capacity(16);
-
-    let mut veto_trim = false;
-    let min_prefix_space_width = lines
-        .filter_map(|(kind, line)| {
-            let mut trimmed = true;
-            let prefix_space_width = if is_empty_line(&line) {
-                None
-            } else {
-                Some(get_prefix_space_width(context, &line))
-            };
-
-            let line = if veto_trim || (kind.is_string() && !line.ends_with('\\')) {
-                veto_trim = kind.is_string() && !line.ends_with('\\');
-                trimmed = false;
-                line
-            } else {
-                line.trim().to_owned()
-            };
-            trimmed_lines.push((trimmed, line, prefix_space_width));
-
-            // when computing the minimum, do not consider lines within a string
-            match kind {
-                FullCodeCharKind::InString | FullCodeCharKind::EndString => None,
-                _ => prefix_space_width,
-            }
-        })
-        .min()?;
-
-    Some(
-        first_line
-            + "\n"
-            + &trimmed_lines
-                .iter()
-                .map(
-                    |&(trimmed, ref line, prefix_space_width)| match prefix_space_width {
-                        _ if !trimmed => line.to_owned(),
-                        Some(original_indent_width) => {
-                            let new_indent_width = indent.width()
-                                + original_indent_width.saturating_sub(min_prefix_space_width);
-                            let new_indent = Indent::from_width(context.config, new_indent_width);
-                            format!("{}{}", new_indent.to_string(context.config), line)
-                        }
-                        None => String::new(),
-                    },
-                )
-                .collect::<Vec<_>>()
-                .join("\n"),
-    )
-}
-
-fn get_prefix_space_width(context: &RewriteContext, s: &str) -> usize {
-    let mut width = 0;
-    for c in s.chars() {
-        match c {
-            ' ' => width += 1,
-            '\t' => width += context.config.tab_spaces(),
-            _ => return width,
-        }
-    }
-    width
-}
-
-fn is_empty_line(s: &str) -> bool {
-    s.is_empty() || s.chars().all(char::is_whitespace)
-}
-
 // A very simple parser that just parses a macros 2.0 definition into its branches.
 // Currently we do not attempt to parse any further than that.
 #[derive(new)]
@@ -1342,7 +1290,7 @@ fn rewrite(
 
         // Indent the body since it is in a block.
         let indent_str = body_indent.to_string(&config);
-        let mut new_body = LineClasses::new(new_body.trim_right())
+        let mut new_body = LineClasses::new(new_body.trim_end())
             .enumerate()
             .fold(
                 (String::new(), true),