From 83487c060ff64498fb9a3eb472ce71af1d26fe89 Mon Sep 17 00:00:00 2001
From: Manish Goregaokar <manishsmail@gmail.com>
Date: Wed, 12 Aug 2015 16:44:14 +0530
Subject: [PATCH 1/6] Add trim_multiline utility (fixes #139)

---
 src/collapsible_if.rs |  4 ++--
 src/lib.rs            |  1 +
 src/misc.rs           |  4 ++--
 src/utils.rs          | 29 +++++++++++++++++++++++++++++
 4 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/src/collapsible_if.rs b/src/collapsible_if.rs
index be34458e0dc..8a41f208938 100644
--- a/src/collapsible_if.rs
+++ b/src/collapsible_if.rs
@@ -18,7 +18,7 @@ use rustc::middle::def::*;
 use syntax::ast::*;
 use syntax::ptr::P;
 use syntax::codemap::{Span, Spanned, ExpnInfo};
-use utils::{in_macro, span_help_and_lint, snippet};
+use utils::{in_macro, span_help_and_lint, snippet, snippet_block};
 
 declare_lint! {
     pub COLLAPSIBLE_IF,
@@ -55,7 +55,7 @@ fn check_expr_expd(cx: &Context, e: &Expr, info: Option<&ExpnInfo>) {
                     "this if statement can be collapsed",
                     &format!("try\nif {} && {} {}",
                              check_to_string(cx, check), check_to_string(cx, check_inner),
-                             snippet(cx, content.span, "..")));
+                             snippet_block(cx, content.span, "..")));
             }
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 9135ecaca6c..01a2d65606c 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,6 @@
 #![feature(plugin_registrar, box_syntax)]
 #![feature(rustc_private, collections)]
+#![feature(str_split_at)]
 #![allow(unused_imports, unknown_lints)]
 
 #[macro_use]
diff --git a/src/misc.rs b/src/misc.rs
index 7372bfed6c5..861e4a73dd2 100644
--- a/src/misc.rs
+++ b/src/misc.rs
@@ -7,7 +7,7 @@ use rustc::lint::{Context, LintPass, LintArray, Lint, Level};
 use rustc::middle::ty;
 use syntax::codemap::{Span, Spanned};
 
-use utils::{match_path, snippet, span_lint, span_help_and_lint, walk_ptrs_ty};
+use utils::{match_path, snippet, snippet_block, span_lint, span_help_and_lint, walk_ptrs_ty};
 
 /// Handles uncategorized lints
 /// Currently handles linting of if-let-able matches
@@ -37,7 +37,7 @@ impl LintPass for MiscPass {
                     // an enum is extended. So we only consider cases where a `_` wildcard is used
                     if arms[1].pats[0].node == PatWild(PatWildSingle) &&
                             arms[0].pats.len() == 1 {
-                        let body_code = snippet(cx, arms[0].body.span, "..");
+                        let body_code = snippet_block(cx, arms[0].body.span, "..");
                         let suggestion = if let ExprBlock(_) = arms[0].body.node {
                             body_code.into_owned()
                         } else {
diff --git a/src/utils.rs b/src/utils.rs
index c54a7f56f7e..5b9c995589b 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -51,6 +51,35 @@ pub fn snippet<'a>(cx: &Context, span: Span, default: &'a str) -> Cow<'a, str> {
     cx.sess().codemap().span_to_snippet(span).map(From::from).unwrap_or(Cow::Borrowed(default))
 }
 
+/// convert a span (from a block) to a code snippet if available, otherwise use default, e.g.
+/// `snippet(cx, expr.span, "..")`
+/// This trims the code of indentation, except for the first line
+/// Use it for blocks or block-like things which need to be printed as such
+pub fn snippet_block<'a>(cx: &Context, span: Span, default: &'a str) -> Cow<'a, str> {
+    let snip = snippet(cx, span, default);
+    trim_multiline(snip, true)
+}
+
+/// Trim indentation from a multiline string
+/// with possibility of ignoring the first line
+pub fn trim_multiline<'a>(s: Cow<'a, str>, ignore_first: bool) -> Cow<'a, str> {
+    let x = s.lines().skip(ignore_first as usize)
+             .map(|l| l.char_indices()
+                       .find(|&(_,x)| x != ' ')
+                       .unwrap_or((l.len(),' ')).0)
+             .min().unwrap_or(0);
+    if x > 0 {
+        Cow::Owned(s.lines().enumerate().map(|(i,l)| if ignore_first && i==0 {
+                                                        l
+                                                     } else {
+                                                        l.split_at(x).1
+                                                     }).collect::<Vec<_>>()
+                                       .join("\n"))
+    } else {
+        s
+    }
+}
+
 /// get a parent expr if any – this is useful to constrain a lint
 pub fn get_parent_expr<'c>(cx: &'c Context, e: &Expr) -> Option<&'c Expr> {
     let map = &cx.tcx.map;

From fbbb44d93bb566d5b3346b180a659ebdcd815fe5 Mon Sep 17 00:00:00 2001
From: Manish Goregaokar <manishsmail@gmail.com>
Date: Thu, 13 Aug 2015 13:27:30 +0530
Subject: [PATCH 2/6] Handle tabs

---
 src/utils.rs | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/utils.rs b/src/utils.rs
index 5b9c995589b..3be6993b759 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -63,10 +63,16 @@ pub fn snippet_block<'a>(cx: &Context, span: Span, default: &'a str) -> Cow<'a,
 /// Trim indentation from a multiline string
 /// with possibility of ignoring the first line
 pub fn trim_multiline<'a>(s: Cow<'a, str>, ignore_first: bool) -> Cow<'a, str> {
+    let s = trim_multiline_inner(s, ignore_first, ' ');
+    let s = trim_multiline_inner(s, ignore_first, '\t');
+    trim_multiline_inner(s, ignore_first, ' ')
+}
+
+fn trim_multiline_inner<'a>(s: Cow<'a, str>, ignore_first: bool, ch: char) -> Cow<'a, str> {
     let x = s.lines().skip(ignore_first as usize)
              .map(|l| l.char_indices()
-                       .find(|&(_,x)| x != ' ')
-                       .unwrap_or((l.len(),' ')).0)
+                       .find(|&(_,x)| x != ch)
+                       .unwrap_or((l.len(), ch)).0)
              .min().unwrap_or(0);
     if x > 0 {
         Cow::Owned(s.lines().enumerate().map(|(i,l)| if ignore_first && i==0 {

From f4b5d215332da92256bcc4d0c4b9f8b7ef6d6f72 Mon Sep 17 00:00:00 2001
From: llogiq <bogusandre@gmail.com>
Date: Thu, 13 Aug 2015 15:46:00 +0200
Subject: [PATCH 3/6] added a few unit tests to trim_multiline

---
 tests/trim_multiline.rs | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
 create mode 100644 tests/trim_multiline.rs

diff --git a/tests/trim_multiline.rs b/tests/trim_multiline.rs
new file mode 100644
index 00000000000..988e5d1012f
--- /dev/null
+++ b/tests/trim_multiline.rs
@@ -0,0 +1,38 @@
+/// test the multiline-trim function
+#[allow(plugin_as_library)]
+extern crate clippy;
+
+use clippy::utils::trim_multiline;
+
+#[test]
+fn test_single_line() {
+    assert_eq!("", trim_multiline("".into(), false));
+    assert_eq!("...", trim_multiline("...".into(), false));
+    assert_eq!("...", trim_multiline("    ...".into(), false));
+    assert_eq!("...", trim_multiline("\t...".into(), false));
+    assert_eq!("...", trim_multiline("\t\t...".into(), false));
+}
+
+#[test]
+fn test_block() {
+    assert_eq!("\
+if x {
+    y
+} else {
+    z
+}", trim_multiline("    if x {
+        y
+    } else {
+        z
+    }".into(), false));
+    assert_eq!("\
+if x {
+\ty
+} else {
+\tz
+}", trim_multiline("    if x {
+    \ty
+    } else {
+    \tz
+    }".into(), false));
+}

From dece5a6cb53248d67a761b8bd94e47cef776fcd6 Mon Sep 17 00:00:00 2001
From: llogiq <bogusandre@gmail.com>
Date: Thu, 13 Aug 2015 15:48:48 +0200
Subject: [PATCH 4/6] added empty line test

---
 tests/trim_multiline.rs | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/tests/trim_multiline.rs b/tests/trim_multiline.rs
index 988e5d1012f..e29ee4922cd 100644
--- a/tests/trim_multiline.rs
+++ b/tests/trim_multiline.rs
@@ -36,3 +36,19 @@ if x {
     \tz
     }".into(), false));
 }
+
+#[test]
+fn test_empty_line() {
+    assert_eq!("\
+if x {
+    y
+
+} else {
+    z
+}", trim_multiline("    if x {
+        y
+
+    } else {
+        z
+    }".into(), false));
+}

From 5ce8e7ba85fa50e067a5262fccd130e071b679fb Mon Sep 17 00:00:00 2001
From: Manish Goregaokar <manishsmail@gmail.com>
Date: Thu, 13 Aug 2015 19:29:12 +0530
Subject: [PATCH 5/6] trim_multiline: ignore empty lines

---
 src/utils.rs | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/utils.rs b/src/utils.rs
index 3be6993b759..490d2f6d1f6 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -70,12 +70,15 @@ pub fn trim_multiline<'a>(s: Cow<'a, str>, ignore_first: bool) -> Cow<'a, str> {
 
 fn trim_multiline_inner<'a>(s: Cow<'a, str>, ignore_first: bool, ch: char) -> Cow<'a, str> {
     let x = s.lines().skip(ignore_first as usize)
-             .map(|l| l.char_indices()
-                       .find(|&(_,x)| x != ch)
-                       .unwrap_or((l.len(), ch)).0)
+             .filter_map(|l| { if l.len() > 0 { // ignore empty lines
+                                Some(l.char_indices()
+                                      .find(|&(_,x)| x != ch)
+                                      .unwrap_or((l.len(), ch)).0)
+                               } else {None}})
              .min().unwrap_or(0);
     if x > 0 {
-        Cow::Owned(s.lines().enumerate().map(|(i,l)| if ignore_first && i==0 {
+        Cow::Owned(s.lines().enumerate().map(|(i,l)| if (ignore_first && i == 0) ||
+                                                         l.len() == 0 {
                                                         l
                                                      } else {
                                                         l.split_at(x).1

From 763ae1f3ae9644110f0af893c79719be00288311 Mon Sep 17 00:00:00 2001
From: Manish Goregaokar <manishsmail@gmail.com>
Date: Thu, 13 Aug 2015 23:20:00 +0530
Subject: [PATCH 6/6] Fix dogfood

---
 src/utils.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/utils.rs b/src/utils.rs
index 490d2f6d1f6..67a89b067e6 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -62,13 +62,13 @@ pub fn snippet_block<'a>(cx: &Context, span: Span, default: &'a str) -> Cow<'a,
 
 /// Trim indentation from a multiline string
 /// with possibility of ignoring the first line
-pub fn trim_multiline<'a>(s: Cow<'a, str>, ignore_first: bool) -> Cow<'a, str> {
+pub fn trim_multiline(s: Cow<str>, ignore_first: bool) -> Cow<str> {
     let s = trim_multiline_inner(s, ignore_first, ' ');
     let s = trim_multiline_inner(s, ignore_first, '\t');
     trim_multiline_inner(s, ignore_first, ' ')
 }
 
-fn trim_multiline_inner<'a>(s: Cow<'a, str>, ignore_first: bool, ch: char) -> Cow<'a, str> {
+fn trim_multiline_inner(s: Cow<str>, ignore_first: bool, ch: char) -> Cow<str> {
     let x = s.lines().skip(ignore_first as usize)
              .filter_map(|l| { if l.len() > 0 { // ignore empty lines
                                 Some(l.char_indices()