Repository: rust-lang/rustfmt (rust)

Issue:
Incorrect comment aligning with strange unicode characters
With this code:
```rust
fn main() {}

pub struct WhitespaceCharacters {
    pub space: char,
    pub nbsp: char,
    pub tab: char,
    pub newline: char,
}

impl Default for WhitespaceCharacters {
    fn default() -> Self {
        Self {
            space: '·',    // U+00B7
            nbsp: '⍽',    // U+237D
            tab: '→',     // U+2192
            newline: '⏎', // U+23CE
        }
    }
}
```
rustfmt incorrectly aligns the comments, so that the one next to space is one character past the others.
Note that in utf8 the character next to space is 2 bytes, while the others they are 3 bytes.
Changing the chars to more basic ASCII chars resolves the issue:
```rust
impl Default for WhitespaceCharacters {
    fn default() -> Self {
        Self {
            space: 'h',   // U+00B7
            nbsp: 'i',    // U+237D
            tab: 'j',     // U+2192
            newline: 'k', // U+23CE
        }
    }
}
```
```
cargo fmt --version
rustfmt 1.5.1-stable (4b91a6e 2022-08-08)
```



Pull request:
Unicode comment align
Also added tests for this behavior.
Closes #5504
Related to #6

Patch:
diff --git a/src/lists.rs b/src/lists.rs
index e8785050..a878e6cf 100644
--- a/src/lists.rs
+++ b/src/lists.rs
@@ -297,9 +297,9 @@ where
         } else {
             inner_item.as_ref()
         };
-        let mut item_last_line_width = item_last_line.len() + item_sep_len;
+        let mut item_last_line_width = unicode_str_width(item_last_line) + item_sep_len;
         if item_last_line.starts_with(&**indent_str) {
-            item_last_line_width -= indent_str.len();
+            item_last_line_width -= unicode_str_width(indent_str);
         }
 
         if !item.is_substantial() {
@@ -449,7 +449,7 @@ where
                 } else if starts_with_newline(comment) {
                     false
                 } else {
-                    comment.trim().contains('\n') || comment.trim().len() > width
+                    comment.trim().contains('\n') || unicode_str_width(comment.trim()) > width
                 };
 
                 rewrite_comment(
@@ -465,7 +465,7 @@ where
             if !starts_with_newline(comment) {
                 if formatting.align_comments {
                     let mut comment_alignment =
-                        post_comment_alignment(item_max_width, inner_item.len());
+                        post_comment_alignment(item_max_width, unicode_str_width(inner_item));
                     if first_line_width(&formatted_comment)
                         + last_line_width(&result)
                         + comment_alignment
@@ -475,7 +475,7 @@ where
                         item_max_width = None;
                         formatted_comment = rewrite_post_comment(&mut item_max_width)?;
                         comment_alignment =
-                            post_comment_alignment(item_max_width, inner_item.len());
+                            post_comment_alignment(item_max_width, unicode_str_width(inner_item));
                     }
                     for _ in 0..=comment_alignment {
                         result.push(' ');
@@ -533,7 +533,7 @@ where
     let mut first = true;
     for item in items.clone().into_iter().skip(i) {
         let item = item.as_ref();
-        let inner_item_width = item.inner_as_ref().len();
+        let inner_item_width = unicode_str_width(item.inner_as_ref());
         if !first
             && (item.is_different_group()
                 || item.post_comment.is_none()
@@ -552,8 +552,8 @@ where
     max_width
 }
 
-fn post_comment_alignment(item_max_width: Option<usize>, inner_item_len: usize) -> usize {
-    item_max_width.unwrap_or(0).saturating_sub(inner_item_len)
+fn post_comment_alignment(item_max_width: Option<usize>, inner_item_width: usize) -> usize {
+    item_max_width.unwrap_or(0).saturating_sub(inner_item_width)
 }
 
 pub(crate) struct ListItems<'a, I, F1, F2, F3>


Test patch:
diff --git a/tests/source/comments_unicode.rs b/tests/source/comments_unicode.rs
new file mode 100644
index 00000000..e65a245b
--- /dev/null
+++ b/tests/source/comments_unicode.rs
@@ -0,0 +1,140 @@
+impl Default for WhitespaceCharacters {
+    fn default() -> Self {
+        Self {
+            space: '·',    // U+00B7
+            nbsp: '⍽',    // U+237D
+            tab: '→',     // U+2192
+            newline: '⏎', // U+23CE
+        }
+    }
+}
+
+const RAINBOWS: &[&str] = &[
+    "rаinЬοѡ",    // hue: 0
+    "raіnЬοw",     // hue: 2
+    "rаіɴЬow",    // hue: 2
+    "raіɴЬoѡ",    // hue: 8
+    "ʀainЬow",      // hue: 8
+    "ʀaіɴboѡ",    // hue: 8
+    "ʀаіnbοw",    // hue: 11
+    "rainЬoѡ",      // hue: 14
+    "raіɴbow",      // hue: 14
+    "rаiɴЬow",     // hue: 20
+    "raіnЬow",      // hue: 26
+    "ʀaiɴbοw",     // hue: 32
+    "raіɴboѡ",     // hue: 35
+    "rаiɴbow",      // hue: 35
+    "rаіnbοw",     // hue: 38
+    "rаinЬow",      // hue: 47
+    "ʀaіnboѡ",     // hue: 47
+    "ʀaіnЬoѡ",    // hue: 47
+    "ʀаіɴbοw",   // hue: 53
+    "ʀaіnЬοѡ",   // hue: 57
+    "raiɴЬoѡ",     // hue: 68
+    "ʀainbοѡ",     // hue: 68
+    "ʀаinboѡ",     // hue: 68
+    "ʀаiɴbοw",    // hue: 68
+    "ʀаіnbow",     // hue: 68
+    "rаіnЬοѡ",   // hue: 69
+    "ʀainЬοw",     // hue: 71
+    "raiɴbow",       // hue: 73
+    "raіnЬoѡ",     // hue: 74
+    "rаіɴbοw",    // hue: 77
+    "raіnЬοѡ",    // hue: 81
+    "raiɴЬow",      // hue: 83
+    "ʀainbοw",      // hue: 83
+    "ʀаinbow",      // hue: 83
+    "ʀаiɴbοѡ",   // hue: 83
+    "ʀаіnboѡ",    // hue: 83
+    "ʀаіɴЬοѡ", // hue: 84
+    "rainЬow",       // hue: 85
+    "ʀаiɴЬοw",   // hue: 86
+    "ʀаіnbοѡ",   // hue: 89
+    "ʀаіnЬοw",   // hue: 92
+    "rаiɴbοw",     // hue: 95
+    "ʀаіɴbοѡ",  // hue: 98
+    "ʀаiɴЬοѡ",  // hue: 99
+    "raіnbοw",      // hue: 101
+    "ʀаіɴЬοw",  // hue: 101
+    "ʀaiɴboѡ",     // hue: 104
+    "ʀаinbοѡ",    // hue: 104
+    "rаiɴbοѡ",    // hue: 107
+    "ʀаinЬοw",    // hue: 107
+    "rаiɴЬοw",    // hue: 110
+    "rаіnboѡ",     // hue: 110
+    "rаіnbοѡ",    // hue: 113
+    "ʀainЬοѡ",    // hue: 114
+    "rаіnЬοw",    // hue: 116
+    "ʀaіɴЬow",    // hue: 116
+    "rаinbοw",      // hue: 122
+    "ʀаіɴboѡ",   // hue: 125
+    "rаinbοѡ",     // hue: 131
+    "rainbow",        // hue: 134
+    "rаinЬοw",     // hue: 134
+    "ʀаiɴboѡ",    // hue: 140
+    "rainЬοѡ",     // hue: 141
+    "raіɴЬow",     // hue: 143
+    "ʀainЬoѡ",     // hue: 143
+    "ʀaіɴbow",     // hue: 143
+    "ʀainbow",       // hue: 148
+    "rаіɴboѡ",    // hue: 149
+    "ʀainboѡ",      // hue: 155
+    "ʀaіnbow",      // hue: 155
+    "ʀaіnЬow",     // hue: 155
+    "raiɴbοw",      // hue: 158
+    "ʀаiɴЬoѡ",   // hue: 158
+    "rainbοw",       // hue: 160
+    "rаinbow",       // hue: 160
+    "ʀaіɴbοѡ",   // hue: 164
+    "ʀаiɴbow",     // hue: 164
+    "ʀаіnЬoѡ",   // hue: 164
+    "ʀaiɴЬοѡ",   // hue: 165
+    "rаiɴboѡ",     // hue: 167
+    "ʀaіɴЬοw",   // hue: 167
+    "ʀaіɴЬοѡ",  // hue: 171
+    "raіnboѡ",      // hue: 173
+    "ʀаіɴЬoѡ",  // hue: 173
+    "rаіɴbοѡ",   // hue: 176
+    "ʀаinЬow",     // hue: 176
+    "rаiɴЬοѡ",   // hue: 177
+    "rаіɴЬοw",   // hue: 179
+    "ʀаinЬoѡ",    // hue: 179
+    "ʀаіɴbow",    // hue: 179
+    "rаiɴЬoѡ",    // hue: 182
+    "raіɴbοѡ",    // hue: 188
+    "rаіnЬoѡ",    // hue: 188
+    "raiɴЬοѡ",    // hue: 189
+    "raіɴЬοw",    // hue: 191
+    "ʀaіɴbοw",    // hue: 191
+    "ʀаіnЬow",    // hue: 191
+    "rainbοѡ",      // hue: 194
+    "rаinboѡ",      // hue: 194
+    "rаіnbow",      // hue: 194
+    "rainЬοw",      // hue: 197
+    "rаinЬoѡ",     // hue: 206
+    "rаіɴbow",     // hue: 206
+    "rаіɴЬοѡ",  // hue: 210
+    "ʀaiɴЬow",     // hue: 212
+    "raіɴbοw",     // hue: 218
+    "rаіnЬow",     // hue: 218
+    "ʀaiɴbοѡ",    // hue: 221
+    "ʀaiɴЬοw",    // hue: 224
+    "ʀaіnbοѡ",    // hue: 227
+    "raiɴboѡ",      // hue: 230
+    "ʀaіnbοw",     // hue: 230
+    "ʀaіnЬοw",    // hue: 230
+    "ʀаinЬοѡ",   // hue: 231
+    "rainboѡ",       // hue: 232
+    "raіnbow",       // hue: 232
+    "ʀаіɴЬow",   // hue: 233
+    "ʀaіɴЬoѡ",   // hue: 239
+    "ʀаіnЬοѡ",  // hue: 246
+    "raiɴbοѡ",     // hue: 248
+    "ʀаiɴЬow",    // hue: 248
+    "raіɴЬοѡ",   // hue: 249
+    "raiɴЬοw",     // hue: 251
+    "rаіɴЬoѡ",   // hue: 251
+    "ʀaiɴbow",      // hue: 251
+    "ʀаinbοw",     // hue: 251
+    "raіnbοѡ",     // hue: 254
+];
diff --git a/tests/target/comments_unicode.rs b/tests/target/comments_unicode.rs
new file mode 100644
index 00000000..3e1b6b0a
--- /dev/null
+++ b/tests/target/comments_unicode.rs
@@ -0,0 +1,140 @@
+impl Default for WhitespaceCharacters {
+    fn default() -> Self {
+        Self {
+            space: '·',   // U+00B7
+            nbsp: '⍽',    // U+237D
+            tab: '→',     // U+2192
+            newline: '⏎', // U+23CE
+        }
+    }
+}
+
+const RAINBOWS: &[&str] = &[
+    "rаinЬοѡ", // hue: 0
+    "raіnЬοw", // hue: 2
+    "rаіɴЬow", // hue: 2
+    "raіɴЬoѡ", // hue: 8
+    "ʀainЬow", // hue: 8
+    "ʀaіɴboѡ", // hue: 8
+    "ʀаіnbοw", // hue: 11
+    "rainЬoѡ", // hue: 14
+    "raіɴbow", // hue: 14
+    "rаiɴЬow", // hue: 20
+    "raіnЬow", // hue: 26
+    "ʀaiɴbοw", // hue: 32
+    "raіɴboѡ", // hue: 35
+    "rаiɴbow", // hue: 35
+    "rаіnbοw", // hue: 38
+    "rаinЬow", // hue: 47
+    "ʀaіnboѡ", // hue: 47
+    "ʀaіnЬoѡ", // hue: 47
+    "ʀаіɴbοw", // hue: 53
+    "ʀaіnЬοѡ", // hue: 57
+    "raiɴЬoѡ", // hue: 68
+    "ʀainbοѡ", // hue: 68
+    "ʀаinboѡ", // hue: 68
+    "ʀаiɴbοw", // hue: 68
+    "ʀаіnbow", // hue: 68
+    "rаіnЬοѡ", // hue: 69
+    "ʀainЬοw", // hue: 71
+    "raiɴbow", // hue: 73
+    "raіnЬoѡ", // hue: 74
+    "rаіɴbοw", // hue: 77
+    "raіnЬοѡ", // hue: 81
+    "raiɴЬow", // hue: 83
+    "ʀainbοw", // hue: 83
+    "ʀаinbow", // hue: 83
+    "ʀаiɴbοѡ", // hue: 83
+    "ʀаіnboѡ", // hue: 83
+    "ʀаіɴЬοѡ", // hue: 84
+    "rainЬow", // hue: 85
+    "ʀаiɴЬοw", // hue: 86
+    "ʀаіnbοѡ", // hue: 89
+    "ʀаіnЬοw", // hue: 92
+    "rаiɴbοw", // hue: 95
+    "ʀаіɴbοѡ", // hue: 98
+    "ʀаiɴЬοѡ", // hue: 99
+    "raіnbοw", // hue: 101
+    "ʀаіɴЬοw", // hue: 101
+    "ʀaiɴboѡ", // hue: 104
+    "ʀаinbοѡ", // hue: 104
+    "rаiɴbοѡ", // hue: 107
+    "ʀаinЬοw", // hue: 107
+    "rаiɴЬοw", // hue: 110
+    "rаіnboѡ", // hue: 110
+    "rаіnbοѡ", // hue: 113
+    "ʀainЬοѡ", // hue: 114
+    "rаіnЬοw", // hue: 116
+    "ʀaіɴЬow", // hue: 116
+    "rаinbοw", // hue: 122
+    "ʀаіɴboѡ", // hue: 125
+    "rаinbοѡ", // hue: 131
+    "rainbow", // hue: 134
+    "rаinЬοw", // hue: 134
+    "ʀаiɴboѡ", // hue: 140
+    "rainЬοѡ", // hue: 141
+    "raіɴЬow", // hue: 143
+    "ʀainЬoѡ", // hue: 143
+    "ʀaіɴbow", // hue: 143
+    "ʀainbow", // hue: 148
+    "rаіɴboѡ", // hue: 149
+    "ʀainboѡ", // hue: 155
+    "ʀaіnbow", // hue: 155
+    "ʀaіnЬow", // hue: 155
+    "raiɴbοw", // hue: 158
+    "ʀаiɴЬoѡ", // hue: 158
+    "rainbοw", // hue: 160
+    "rаinbow", // hue: 160
+    "ʀaіɴbοѡ", // hue: 164
+    "ʀаiɴbow", // hue: 164
+    "ʀаіnЬoѡ", // hue: 164
+    "ʀaiɴЬοѡ", // hue: 165
+    "rаiɴboѡ", // hue: 167
+    "ʀaіɴЬοw", // hue: 167
+    "ʀaіɴЬοѡ", // hue: 171
+    "raіnboѡ", // hue: 173
+    "ʀаіɴЬoѡ", // hue: 173
+    "rаіɴbοѡ", // hue: 176
+    "ʀаinЬow", // hue: 176
+    "rаiɴЬοѡ", // hue: 177
+    "rаіɴЬοw", // hue: 179
+    "ʀаinЬoѡ", // hue: 179
+    "ʀаіɴbow", // hue: 179
+    "rаiɴЬoѡ", // hue: 182
+    "raіɴbοѡ", // hue: 188
+    "rаіnЬoѡ", // hue: 188
+    "raiɴЬοѡ", // hue: 189
+    "raіɴЬοw", // hue: 191
+    "ʀaіɴbοw", // hue: 191
+    "ʀаіnЬow", // hue: 191
+    "rainbοѡ", // hue: 194
+    "rаinboѡ", // hue: 194
+    "rаіnbow", // hue: 194
+    "rainЬοw", // hue: 197
+    "rаinЬoѡ", // hue: 206
+    "rаіɴbow", // hue: 206
+    "rаіɴЬοѡ", // hue: 210
+    "ʀaiɴЬow", // hue: 212
+    "raіɴbοw", // hue: 218
+    "rаіnЬow", // hue: 218
+    "ʀaiɴbοѡ", // hue: 221
+    "ʀaiɴЬοw", // hue: 224
+    "ʀaіnbοѡ", // hue: 227
+    "raiɴboѡ", // hue: 230
+    "ʀaіnbοw", // hue: 230
+    "ʀaіnЬοw", // hue: 230
+    "ʀаinЬοѡ", // hue: 231
+    "rainboѡ", // hue: 232
+    "raіnbow", // hue: 232
+    "ʀаіɴЬow", // hue: 233
+    "ʀaіɴЬoѡ", // hue: 239
+    "ʀаіnЬοѡ", // hue: 246
+    "raiɴbοѡ", // hue: 248
+    "ʀаiɴЬow", // hue: 248
+    "raіɴЬοѡ", // hue: 249
+    "raiɴЬοw", // hue: 251
+    "rаіɴЬoѡ", // hue: 251
+    "ʀaiɴbow", // hue: 251
+    "ʀаinbοw", // hue: 251
+    "raіnbοѡ", // hue: 254
+];