From a2f861730e5b33391c97fe7ec91470d1c9b96ba9 Mon Sep 17 00:00:00 2001
From: moe <35686186+csmoe@users.noreply.github.com>
Date: Thu, 8 Mar 2018 17:05:39 +0800
Subject: [PATCH] fix adds a trailing comma to struct-like macro (#2490)

* fix adds a trailing comma to struct-like macro
---
 src/expr.rs                | 39 +++++++++++++++++++++++++-------------
 tests/source/issue-2445.rs | 21 ++++++++++++++++++++
 tests/target/issue-2445.rs | 21 ++++++++++++++++++++
 3 files changed, 68 insertions(+), 13 deletions(-)
 create mode 100644 tests/source/issue-2445.rs
 create mode 100644 tests/target/issue-2445.rs

diff --git a/src/expr.rs b/src/expr.rs
index 2945f03a535..481baa80de6 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -20,7 +20,7 @@ use chains::rewrite_chain;
 use closures;
 use codemap::{LineRangeUtils, SpanUtils};
 use comment::{combine_strs_with_missing_comments, contains_comment, recover_comment_removed,
-              rewrite_comment, rewrite_missing_comment, FindUncommented};
+              rewrite_comment, rewrite_missing_comment, CharClasses, FindUncommented};
 use config::{Config, ControlBraceStyle, IndentStyle};
 use lists::{definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting,
             struct_lit_shape, struct_lit_tactic, write_list, ListFormatting, ListItem, Separator};
@@ -2411,20 +2411,20 @@ pub fn wrap_args_with_parens(
 /// trailing comma. This function is used when rewriting macro, as adding or removing a trailing
 /// comma from macro can potentially break the code.
 fn span_ends_with_comma(context: &RewriteContext, span: Span) -> bool {
-    let mut encountered_closing_paren = false;
-    for c in context.snippet(span).chars().rev() {
+    let mut result: bool = Default::default();
+    let mut prev_char: char = Default::default();
+
+    for (kind, c) in CharClasses::new(context.snippet(span).chars()) {
         match c {
-            ',' => return true,
-            ')' => if encountered_closing_paren {
-                return false;
-            } else {
-                encountered_closing_paren = true;
-            },
-            _ if c.is_whitespace() => continue,
-            _ => return false,
+            _ if kind.is_comment() || c.is_whitespace() => continue,
+            ')' | '}' => result = result && prev_char != c,
+            ',' => result = true,
+            _ => result = false,
         }
+        prev_char = c;
     }
-    false
+
+    result
 }
 
 fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, shape: Shape) -> Option<String> {
@@ -2608,7 +2608,20 @@ fn rewrite_struct_lit<'a>(
 
         let tactic = struct_lit_tactic(h_shape, context, &item_vec);
         let nested_shape = shape_for_tactic(tactic, h_shape, v_shape);
-        let fmt = struct_lit_formatting(nested_shape, tactic, context, base.is_some());
+
+        let ends_with_comma = span_ends_with_comma(context, span);
+        let force_no_trailing_comma = if context.inside_macro && !ends_with_comma {
+            true
+        } else {
+            false
+        };
+
+        let fmt = struct_lit_formatting(
+            nested_shape,
+            tactic,
+            context,
+            force_no_trailing_comma || base.is_some(),
+        );
 
         write_list(&item_vec, &fmt)?
     };
diff --git a/tests/source/issue-2445.rs b/tests/source/issue-2445.rs
new file mode 100644
index 00000000000..84ce6e647b8
--- /dev/null
+++ b/tests/source/issue-2445.rs
@@ -0,0 +1,21 @@
+test!(RunPassPretty {
+            // comment
+    path: "src/test/run-pass/pretty",
+    mode: "pretty",
+    suite: "run-pass",
+    default: false,
+    host: true  // should, force, , no trailing comma here
+});
+
+test!(RunPassPretty {
+            // comment
+    path: "src/test/run-pass/pretty",
+    mode: "pretty",
+    suite: "run-pass",
+    default: false,
+    host: true,         // should, , preserve, the trailing comma
+});
+
+test!(Test{
+    field: i32, // comment
+});
diff --git a/tests/target/issue-2445.rs b/tests/target/issue-2445.rs
new file mode 100644
index 00000000000..1bc7752fd16
--- /dev/null
+++ b/tests/target/issue-2445.rs
@@ -0,0 +1,21 @@
+test!(RunPassPretty {
+    // comment
+    path: "src/test/run-pass/pretty",
+    mode: "pretty",
+    suite: "run-pass",
+    default: false,
+    host: true // should, force, , no trailing comma here
+});
+
+test!(RunPassPretty {
+    // comment
+    path: "src/test/run-pass/pretty",
+    mode: "pretty",
+    suite: "run-pass",
+    default: false,
+    host: true, // should, , preserve, the trailing comma
+});
+
+test!(Test {
+    field: i32, // comment
+});