]> git.lizzy.rs Git - rust.git/blobdiff - src/lists.rs
Fixed comment dropped between & and type issue (#4482)
[rust.git] / src / lists.rs
index 0fb1eec68d0f28fa65b0cb90093b2a9381c9fc5c..bc55ef6686ac3ec45f50e13ea5ab0be341b0bb5c 100644 (file)
@@ -1,35 +1,28 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Format list-like expressions and items.
 
 use std::cmp;
 use std::iter::Peekable;
 
-use syntax::source_map::BytePos;
+use rustc_span::BytePos;
 
 use crate::comment::{find_comment_end, rewrite_comment, FindUncommented};
 use crate::config::lists::*;
 use crate::config::{Config, IndentStyle};
 use crate::rewrite::RewriteContext;
 use crate::shape::{Indent, Shape};
-use crate::utils::{count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline};
+use crate::utils::{
+    count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline,
+    unicode_str_width,
+};
 use crate::visitor::SnippetProvider;
 
-pub struct ListFormatting<'a> {
+pub(crate) struct ListFormatting<'a> {
     tactic: DefinitiveListTactic,
     separator: &'a str,
     trailing_separator: SeparatorTactic,
     separator_place: SeparatorPlace,
     shape: Shape,
-    // Non-expressions, e.g. items, will have a new line at the end of the list.
+    // Non-expressions, e.g., items, will have a new line at the end of the list.
     // Important for comment styles.
     ends_with_newline: bool,
     // Remove newlines between list elements for expressions.
@@ -42,7 +35,7 @@ pub struct ListFormatting<'a> {
 }
 
 impl<'a> ListFormatting<'a> {
-    pub fn new(shape: Shape, config: &'a Config) -> Self {
+    pub(crate) fn new(shape: Shape, config: &'a Config) -> Self {
         ListFormatting {
             tactic: DefinitiveListTactic::Vertical,
             separator: ",",
@@ -57,47 +50,47 @@ pub fn new(shape: Shape, config: &'a Config) -> Self {
         }
     }
 
-    pub fn tactic(mut self, tactic: DefinitiveListTactic) -> Self {
+    pub(crate) fn tactic(mut self, tactic: DefinitiveListTactic) -> Self {
         self.tactic = tactic;
         self
     }
 
-    pub fn separator(mut self, separator: &'a str) -> Self {
+    pub(crate) fn separator(mut self, separator: &'a str) -> Self {
         self.separator = separator;
         self
     }
 
-    pub fn trailing_separator(mut self, trailing_separator: SeparatorTactic) -> Self {
+    pub(crate) fn trailing_separator(mut self, trailing_separator: SeparatorTactic) -> Self {
         self.trailing_separator = trailing_separator;
         self
     }
 
-    pub fn separator_place(mut self, separator_place: SeparatorPlace) -> Self {
+    pub(crate) fn separator_place(mut self, separator_place: SeparatorPlace) -> Self {
         self.separator_place = separator_place;
         self
     }
 
-    pub fn ends_with_newline(mut self, ends_with_newline: bool) -> Self {
+    pub(crate) fn ends_with_newline(mut self, ends_with_newline: bool) -> Self {
         self.ends_with_newline = ends_with_newline;
         self
     }
 
-    pub fn preserve_newline(mut self, preserve_newline: bool) -> Self {
+    pub(crate) fn preserve_newline(mut self, preserve_newline: bool) -> Self {
         self.preserve_newline = preserve_newline;
         self
     }
 
-    pub fn nested(mut self, nested: bool) -> Self {
+    pub(crate) fn nested(mut self, nested: bool) -> Self {
         self.nested = nested;
         self
     }
 
-    pub fn align_comments(mut self, align_comments: bool) -> Self {
+    pub(crate) fn align_comments(mut self, align_comments: bool) -> Self {
         self.align_comments = align_comments;
         self
     }
 
-    pub fn needs_trailing_separator(&self) -> bool {
+    pub(crate) fn needs_trailing_separator(&self) -> bool {
         match self.trailing_separator {
             // We always put separator in front.
             SeparatorTactic::Always => true,
@@ -116,7 +109,7 @@ fn as_ref(&self) -> &ListItem {
 }
 
 #[derive(PartialEq, Eq, Debug, Copy, Clone)]
-pub enum ListItemCommentStyle {
+pub(crate) enum ListItemCommentStyle {
     // Try to keep the comment on the same line with the item.
     SameLine,
     // Put the comment on the previous or the next line of the item.
@@ -126,20 +119,20 @@ pub enum ListItemCommentStyle {
 }
 
 #[derive(Debug, Clone)]
-pub struct ListItem {
+pub(crate) struct ListItem {
     // None for comments mean that they are not present.
-    pub pre_comment: Option<String>,
-    pub pre_comment_style: ListItemCommentStyle,
+    pub(crate) pre_comment: Option<String>,
+    pub(crate) pre_comment_style: ListItemCommentStyle,
     // Item should include attributes and doc comments. None indicates a failed
     // rewrite.
-    pub item: Option<String>,
-    pub post_comment: Option<String>,
+    pub(crate) item: Option<String>,
+    pub(crate) post_comment: Option<String>,
     // Whether there is extra whitespace before this item.
-    pub new_lines: bool,
+    pub(crate) new_lines: bool,
 }
 
 impl ListItem {
-    pub fn empty() -> ListItem {
+    pub(crate) fn empty() -> ListItem {
         ListItem {
             pre_comment: None,
             pre_comment_style: ListItemCommentStyle::None,
@@ -149,11 +142,11 @@ pub fn empty() -> ListItem {
         }
     }
 
-    pub fn inner_as_ref(&self) -> &str {
+    pub(crate) fn inner_as_ref(&self) -> &str {
         self.item.as_ref().map_or("", |s| s)
     }
 
-    pub fn is_different_group(&self) -> bool {
+    pub(crate) fn is_different_group(&self) -> bool {
         self.inner_as_ref().contains('\n')
             || self.pre_comment.is_some()
             || self
@@ -162,7 +155,7 @@ pub fn is_different_group(&self) -> bool {
                 .map_or(false, |s| s.contains('\n'))
     }
 
-    pub fn is_multiline(&self) -> bool {
+    pub(crate) fn is_multiline(&self) -> bool {
         self.inner_as_ref().contains('\n')
             || self
                 .pre_comment
@@ -174,7 +167,7 @@ pub fn is_multiline(&self) -> bool {
                 .map_or(false, |s| s.contains('\n'))
     }
 
-    pub fn has_single_line_comment(&self) -> bool {
+    pub(crate) fn has_single_line_comment(&self) -> bool {
         self.pre_comment
             .as_ref()
             .map_or(false, |comment| comment.trim_start().starts_with("//"))
@@ -184,11 +177,11 @@ pub fn has_single_line_comment(&self) -> bool {
                 .map_or(false, |comment| comment.trim_start().starts_with("//"))
     }
 
-    pub fn has_comment(&self) -> bool {
+    pub(crate) fn has_comment(&self) -> bool {
         self.pre_comment.is_some() || self.post_comment.is_some()
     }
 
-    pub fn from_str<S: Into<String>>(s: S) -> ListItem {
+    pub(crate) fn from_str<S: Into<String>>(s: S) -> ListItem {
         ListItem {
             pre_comment: None,
             pre_comment_style: ListItemCommentStyle::None,
@@ -198,7 +191,7 @@ pub fn from_str<S: Into<String>>(s: S) -> ListItem {
         }
     }
 
-    // true if the item causes something to be written.
+    // Returns `true` if the item causes something to be written.
     fn is_substantial(&self) -> bool {
         fn empty(s: &Option<String>) -> bool {
             match *s {
@@ -213,13 +206,13 @@ fn empty(s: &Option<String>) -> bool {
 
 /// The type of separator for lists.
 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
-pub enum Separator {
+pub(crate) enum Separator {
     Comma,
     VerticalBar,
 }
 
 impl Separator {
-    pub fn len(self) -> usize {
+    pub(crate) fn len(self) -> usize {
         match self {
             // 2 = `, `
             Separator::Comma => 2,
@@ -229,7 +222,7 @@ pub fn len(self) -> usize {
     }
 }
 
-pub fn definitive_tactic<I, T>(
+pub(crate) fn definitive_tactic<I, T>(
     items: I,
     tactic: ListTactic,
     sep: Separator,
@@ -256,10 +249,7 @@ pub fn definitive_tactic<I, T>(
     let total_sep_len = sep.len() * sep_count.saturating_sub(1);
     let real_total = total_width + total_sep_len;
 
-    if real_total <= limit
-        && !pre_line_comments
-        && !items.into_iter().any(|item| item.as_ref().is_multiline())
-    {
+    if real_total <= limit && !items.into_iter().any(|item| item.as_ref().is_multiline()) {
         DefinitiveListTactic::Horizontal
     } else {
         match tactic {
@@ -270,7 +260,7 @@ pub fn definitive_tactic<I, T>(
 }
 
 // Format a list of commented items into a string.
-pub fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> Option<String>
+pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> Option<String>
 where
     I: IntoIterator<Item = T> + Clone,
     T: AsRef<ListItem>,
@@ -399,7 +389,7 @@ pub fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> Option<Str
                         result.push('\n');
                         result.push_str(indent_str);
                         // This is the width of the item (without comments).
-                        line_len = item.item.as_ref().map_or(0, |str| str.len());
+                        line_len = item.item.as_ref().map_or(0, |s| unicode_str_width(&s));
                     }
                 } else {
                     result.push(' ');
@@ -566,11 +556,11 @@ fn post_comment_alignment(item_max_width: Option<usize>, inner_item_len: usize)
     item_max_width.unwrap_or(0).saturating_sub(inner_item_len)
 }
 
-pub struct ListItems<'a, I, F1, F2, F3>
+pub(crate) struct ListItems<'a, I, F1, F2, F3>
 where
     I: Iterator,
 {
-    snippet_provider: &'a SnippetProvider<'a>,
+    snippet_provider: &'a SnippetProvider,
     inner: Peekable<I>,
     get_lo: F1,
     get_hi: F2,
@@ -582,18 +572,21 @@ pub struct ListItems<'a, I, F1, F2, F3>
     leave_last: bool,
 }
 
-pub fn extract_pre_comment(pre_snippet: &str) -> (Option<String>, ListItemCommentStyle) {
+pub(crate) fn extract_pre_comment(pre_snippet: &str) -> (Option<String>, ListItemCommentStyle) {
     let trimmed_pre_snippet = pre_snippet.trim();
-    let has_block_comment = trimmed_pre_snippet.ends_with("*/");
-    let has_single_line_comment = trimmed_pre_snippet.starts_with("//");
-    if has_block_comment {
-        let comment_end = pre_snippet.chars().rev().position(|c| c == '/').unwrap();
-        if pre_snippet
-            .chars()
-            .rev()
-            .take(comment_end + 1)
-            .any(|c| c == '\n')
-        {
+    // Both start and end are checked to support keeping a block comment inline with
+    // the item, even if there are preceeding line comments, while still supporting
+    // a snippet that starts with a block comment but also contains one or more
+    // trailing single line comments.
+    // https://github.com/rust-lang/rustfmt/issues/3025
+    // https://github.com/rust-lang/rustfmt/pull/3048
+    // https://github.com/rust-lang/rustfmt/issues/3839
+    let starts_with_block_comment = trimmed_pre_snippet.starts_with("/*");
+    let ends_with_block_comment = trimmed_pre_snippet.ends_with("*/");
+    let starts_with_single_line_comment = trimmed_pre_snippet.starts_with("//");
+    if ends_with_block_comment {
+        let comment_end = pre_snippet.rfind(|c| c == '/').unwrap();
+        if pre_snippet[comment_end..].contains('\n') {
             (
                 Some(trimmed_pre_snippet.to_owned()),
                 ListItemCommentStyle::DifferentLine,
@@ -604,7 +597,7 @@ pub fn extract_pre_comment(pre_snippet: &str) -> (Option<String>, ListItemCommen
                 ListItemCommentStyle::SameLine,
             )
         }
-    } else if has_single_line_comment {
+    } else if starts_with_single_line_comment || starts_with_block_comment {
         (
             Some(trimmed_pre_snippet.to_owned()),
             ListItemCommentStyle::DifferentLine,
@@ -614,7 +607,7 @@ pub fn extract_pre_comment(pre_snippet: &str) -> (Option<String>, ListItemCommen
     }
 }
 
-pub fn extract_post_comment(
+pub(crate) fn extract_post_comment(
     post_snippet: &str,
     comment_end: usize,
     separator: &str,
@@ -627,20 +620,28 @@ pub fn extract_post_comment(
         post_snippet[1..].trim_matches(white_space)
     } else if post_snippet.starts_with(separator) {
         post_snippet[separator.len()..].trim_matches(white_space)
-    } else if post_snippet.ends_with(',') {
+    }
+    // not comment or over two lines
+    else if post_snippet.ends_with(',')
+        && (!post_snippet.trim().starts_with("//") || post_snippet.trim().contains('\n'))
+    {
         post_snippet[..(post_snippet.len() - 1)].trim_matches(white_space)
     } else {
         post_snippet
     };
-
-    if !post_snippet_trimmed.is_empty() {
+    // FIXME(#3441): post_snippet includes 'const' now
+    // it should not include here
+    let removed_newline_snippet = post_snippet_trimmed.trim();
+    if !post_snippet_trimmed.is_empty()
+        && (removed_newline_snippet.starts_with("//") || removed_newline_snippet.starts_with("/*"))
+    {
         Some(post_snippet_trimmed.to_owned())
     } else {
         None
     }
 }
 
-pub fn get_comment_end(
+pub(crate) fn get_comment_end(
     post_snippet: &str,
     separator: &str,
     terminator: &str,
@@ -657,7 +658,7 @@ pub fn get_comment_end(
     if let Some(i) = block_open_index {
         match post_snippet.find('/') {
             Some(j) if j < i => block_open_index = None,
-            _ if i > 0 && &post_snippet[i - 1..i] == "/" => block_open_index = None,
+            _ if post_snippet[..i].ends_with('/') => block_open_index = None,
             _ => (),
         }
     }
@@ -693,13 +694,18 @@ pub fn get_comment_end(
 
 // Account for extra whitespace between items. This is fiddly
 // because of the way we divide pre- and post- comments.
-fn has_extra_newline(post_snippet: &str, comment_end: usize) -> bool {
+pub(crate) fn has_extra_newline(post_snippet: &str, comment_end: usize) -> bool {
     if post_snippet.is_empty() || comment_end == 0 {
         return false;
     }
 
+    let len_last = post_snippet[..comment_end]
+        .chars()
+        .last()
+        .unwrap()
+        .len_utf8();
     // Everything from the separator to the next item.
-    let test_snippet = &post_snippet[comment_end - 1..];
+    let test_snippet = &post_snippet[comment_end - len_last..];
     let first_newline = test_snippet
         .find('\n')
         .unwrap_or_else(|| test_snippet.len());
@@ -770,8 +776,8 @@ fn next(&mut self) -> Option<Self::Item> {
 
 #[allow(clippy::too_many_arguments)]
 // Creates an iterator over a list's items with associated comments.
-pub fn itemize_list<'a, T, I, F1, F2, F3>(
-    snippet_provider: &'a SnippetProvider<'_>,
+pub(crate) fn itemize_list<'a, T, I, F1, F2, F3>(
+    snippet_provider: &'a SnippetProvider,
     inner: I,
     terminator: &'a str,
     separator: &'a str,
@@ -814,10 +820,10 @@ fn calculate_width<I, T>(items: I) -> (usize, usize)
         .fold((0, 0), |acc, l| (acc.0 + 1, acc.1 + l))
 }
 
-pub fn total_item_width(item: &ListItem) -> usize {
+pub(crate) fn total_item_width(item: &ListItem) -> usize {
     comment_len(item.pre_comment.as_ref().map(|x| &(*x)[..]))
         + comment_len(item.post_comment.as_ref().map(|x| &(*x)[..]))
-        + item.item.as_ref().map_or(0, |str| str.len())
+        + &item.item.as_ref().map_or(0, |s| unicode_str_width(&s))
 }
 
 fn comment_len(comment: Option<&str>) -> usize {
@@ -836,7 +842,7 @@ fn comment_len(comment: Option<&str>) -> usize {
 }
 
 // Compute horizontal and vertical shapes for a struct-lit-like thing.
-pub fn struct_lit_shape(
+pub(crate) fn struct_lit_shape(
     shape: Shape,
     context: &RewriteContext<'_>,
     prefix_width: usize,
@@ -865,7 +871,7 @@ pub fn struct_lit_shape(
 }
 
 // Compute the tactic for the internals of a struct-lit-like thing.
-pub fn struct_lit_tactic(
+pub(crate) fn struct_lit_tactic(
     h_shape: Option<Shape>,
     context: &RewriteContext<'_>,
     items: &[ListItem],
@@ -884,7 +890,7 @@ pub fn struct_lit_tactic(
 
 // Given a tactic and possible shapes for horizontal and vertical layout,
 // come up with the actual shape to use.
-pub fn shape_for_tactic(
+pub(crate) fn shape_for_tactic(
     tactic: DefinitiveListTactic,
     h_shape: Option<Shape>,
     v_shape: Shape,
@@ -897,7 +903,7 @@ pub fn shape_for_tactic(
 
 // Create a ListFormatting object for formatting the internals of a
 // struct-lit-like thing, that is a series of fields.
-pub fn struct_lit_formatting<'a>(
+pub(crate) fn struct_lit_formatting<'a>(
     shape: Shape,
     tactic: DefinitiveListTactic,
     context: &'a RewriteContext<'_>,