Repository: rust-lang/rustfmt (Rust)

Issue:
rustfmt complains about long documentation lines after certain lifetime usages
A reduced test case is shown below. The first long documentation line is accepted, as it should be. The second line produces an `error: line exceeded maximum width`.

```rust
// This is a very long documentation line that does *not* bother rustfmt, because it does not come after the lifetime below.
fn foo() -> F<'a> {
    bar()
}
// This is a very long documentation line that *does* bother rustfmt, because it comes after the lifetime above.
```

This can occur in practice if the lifetime is `'static`, or if the snippet is inside an `impl<'a>`.


Discussion:
This issue is for some reason hidden by some usages of a single quote after the problem lifetime statement.

```rust
// This is a very long documentation line that does *not* bother rustfmt, because it does not come after the lifetime below.
fn foo() -> F<'a> {
    bar()
}
fn hide_issue<'a>() {}
// This is a very long documentation line that now does *not* bother rustfmt, because the issue is hidden by hide_issue.
```

Another example, this time the quote inside a comment:
```rust
// This is a very long documentation line that does *not* bother rustfmt, because it does not come after the lifetime below.
fn foo() -> F<'a> {
    bar()
}
// ' <- single quote
// This is a very long documentation line that now does *not* bother rustfmt, because the issue is hidden by the commented single quote.
```
The implementation of `Iterator` for `CharClasses` assumes that the occurence of `'` is a part of the literal char, which is wrong.



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index e865f2d1b53..e4e9a9ddd75 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -89,6 +89,11 @@ name = "dtoa"
 version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "either"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "ena"
 version = "0.9.2"
@@ -144,6 +149,14 @@ dependencies = [
  "quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "itertools"
+version = "0.7.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "itoa"
 version = "0.3.4"
@@ -367,6 +380,7 @@ dependencies = [
  "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -615,6 +629,7 @@ dependencies = [
 "checksum derive-new 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "92f8b8e1d6c8a5f5ea0849a0e4c55941576115c62d3fc425e96918bbbeb3d3c2"
 "checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
+"checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3"
 "checksum ena 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b449f3b18c89d2dbe40548d2ee4fa58ea0a08b761992da6ecb9788e4688834"
 "checksum env_logger 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f3cc21490995c841d68e00276eba02071ebb269ec24011d5728bd00eabd39e31"
 "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
@@ -622,6 +637,7 @@ dependencies = [
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
 "checksum getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "b900c08c1939860ce8b54dc6a89e26e00c04c380fd0e09796799bd7f12861e05"
 "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
+"checksum itertools 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "23d53b4c7394338044c3b9c8c5b2caaf7b40ae049ecd321578ebdc2e13738cd1"
 "checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
 "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
diff --git a/Cargo.toml b/Cargo.toml
index 3be0c4ff7a8..ecd8db0b2be 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,6 +35,7 @@ cargo-fmt = []
 rustfmt-format-diff = []
 
 [dependencies]
+itertools = "0.7"
 toml = "0.4"
 serde = "1.0"
 serde_derive = "1.0"
diff --git a/src/comment.rs b/src/comment.rs
index 74a14511be8..e3e52eda815 100644
--- a/src/comment.rs
+++ b/src/comment.rs
@@ -12,6 +12,7 @@
 
 use std::{self, iter, borrow::Cow};
 
+use itertools::{multipeek, MultiPeek};
 use syntax::codemap::Span;
 
 use config::Config;
@@ -684,7 +685,7 @@ where
     T: Iterator,
     T::Item: RichChar,
 {
-    base: iter::Peekable<T>,
+    base: MultiPeek<T>,
     status: CharClassesStatus,
 }
 
@@ -776,7 +777,7 @@ where
 {
     pub fn new(base: T) -> CharClasses<T> {
         CharClasses {
-            base: base.peekable(),
+            base: multipeek(base),
             status: CharClassesStatus::Normal,
         }
     }
@@ -820,7 +821,21 @@ where
                     char_kind = FullCodeCharKind::InString;
                     CharClassesStatus::LitString
                 }
-                '\'' => CharClassesStatus::LitChar,
+                '\'' => {
+                    // HACK: Work around mut borrow.
+                    match self.base.peek() {
+                        Some(next) if next.get_char() == '\\' => {
+                            self.status = CharClassesStatus::LitChar;
+                            return Some((char_kind, item));
+                        }
+                        _ => (),
+                    }
+
+                    match self.base.peek() {
+                        Some(next) if next.get_char() == '\'' => CharClassesStatus::LitChar,
+                        _ => CharClassesStatus::Normal,
+                    }
+                }
                 '/' => match self.base.peek() {
                     Some(next) if next.get_char() == '*' => {
                         self.status = CharClassesStatus::BlockCommentOpening(1);
diff --git a/src/lib.rs b/src/lib.rs
index 9447155ee92..b70c6e6663c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -16,6 +16,7 @@
 #[macro_use]
 extern crate derive_new;
 extern crate diff;
+extern crate itertools;
 #[macro_use]
 extern crate log;
 extern crate regex;


Test patch:
diff --git a/tests/target/issue-2526.rs b/tests/target/issue-2526.rs
new file mode 100644
index 00000000000..7dd58aba318
--- /dev/null
+++ b/tests/target/issue-2526.rs
@@ -0,0 +1,8 @@
+// Test that rustfmt will not warn about comments exceeding max width around lifetime.
+// See #2526.
+
+// comment comment comment comment comment comment comment comment comment comment comment comment comment
+fn foo() -> F<'a> {
+    bar()
+}
+// comment comment comment comment comment comment comment comment comment comment comment comment comment