diff --git a/src/comment.rs b/src/comment.rs index 87c6b65865b..a811d4a26ed 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -142,6 +142,11 @@ fn comment_style(orig: &str, normalize_comments: bool) -> CommentStyle<'_> { } } +/// Returns true if the last line of the passed string finishes with a block-comment. +pub fn is_last_comment_block(s: &str) -> bool { + s.trim_end().ends_with("*/") +} + /// Combine `prev_str` and `next_str` into a single `String`. `span` may contain /// comments between two strings. If there are such comments, then that will be /// recovered. If `allow_extend` is true and there is no comment between the two diff --git a/src/items.rs b/src/items.rs index c907d34b47c..57bded72604 100644 --- a/src/items.rs +++ b/src/items.rs @@ -10,8 +10,9 @@ use syntax::visit; use syntax::{ast, ptr, symbol}; use crate::comment::{ - combine_strs_with_missing_comments, contains_comment, recover_comment_removed, - recover_missing_comment_in_span, rewrite_missing_comment, FindUncommented, + combine_strs_with_missing_comments, contains_comment, is_last_comment_block, + recover_comment_removed, recover_missing_comment_in_span, rewrite_missing_comment, + FindUncommented, }; use crate::config::lists::*; use crate::config::{BraceStyle, Config, Density, IndentStyle, Version}; @@ -1173,11 +1174,7 @@ fn format_unit_struct( ) -> Option { let header_str = format_header(context, p.prefix, p.ident, p.vis); let generics_str = if let Some(generics) = p.generics { - let hi = if generics.where_clause.predicates.is_empty() { - generics.span.hi() - } else { - generics.where_clause.span.hi() - }; + let hi = context.snippet_provider.span_before(p.span, ";"); format_generics( context, generics, @@ -2711,19 +2708,19 @@ fn format_generics( let shape = Shape::legacy(context.budget(used_width + offset.width()), offset); let mut result = rewrite_generics(context, "", generics, shape)?; - let same_line_brace = if !generics.where_clause.predicates.is_empty() || result.contains('\n') { + // If the generics are not parameterized then generics.span.hi() == 0, + // so we use span.lo(), which is the position after `struct Foo`. + let span_end_before_where = if !generics.params.is_empty() { + generics.span.hi() + } else { + span.lo() + }; + let (same_line_brace, missed_comments) = if !generics.where_clause.predicates.is_empty() { let budget = context.budget(last_line_used_width(&result, offset.width())); let mut option = WhereClauseOption::snuggled(&result); if brace_pos == BracePos::None { option.suppress_comma = true; } - // If the generics are not parameterized then generics.span.hi() == 0, - // so we use span.lo(), which is the position after `struct Foo`. - let span_end_before_where = if !generics.params.is_empty() { - generics.span.hi() - } else { - span.lo() - }; let where_clause_str = rewrite_where_clause( context, &generics.where_clause, @@ -2737,15 +2734,41 @@ fn format_generics( false, )?; result.push_str(&where_clause_str); - brace_pos == BracePos::ForceSameLine - || brace_style == BraceStyle::PreferSameLine - || (generics.where_clause.predicates.is_empty() - && trimmed_last_line_width(&result) == 1) + ( + brace_pos == BracePos::ForceSameLine || brace_style == BraceStyle::PreferSameLine, + // missed comments are taken care of in #rewrite_where_clause + None, + ) } else { - brace_pos == BracePos::ForceSameLine - || trimmed_last_line_width(&result) == 1 - || brace_style != BraceStyle::AlwaysNextLine + ( + brace_pos == BracePos::ForceSameLine + || (result.contains('\n') && brace_style == BraceStyle::PreferSameLine + || brace_style != BraceStyle::AlwaysNextLine) + || trimmed_last_line_width(&result) == 1, + rewrite_missing_comment( + mk_sp( + span_end_before_where, + if brace_pos == BracePos::None { + span.hi() + } else { + context.snippet_provider.span_before(span, "{") + }, + ), + shape, + context, + ), + ) }; + // add missing comments + let missed_line_comments = missed_comments + .filter(|missed_comments| !missed_comments.is_empty()) + .map_or(false, |missed_comments| { + let is_block = is_last_comment_block(&missed_comments); + let sep = if is_block { " " } else { "\n" }; + result.push_str(sep); + result.push_str(&missed_comments); + !is_block + }); if brace_pos == BracePos::None { return Some(result); } @@ -2761,7 +2784,7 @@ fn format_generics( // 2 = ` {` 2 }; - let forbid_same_line_brace = overhead > remaining_budget; + let forbid_same_line_brace = missed_line_comments || overhead > remaining_budget; if !forbid_same_line_brace && same_line_brace { result.push(' '); } else { diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 70ca898f2af..0f459571d42 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use syntax::source_map::{BytePos, Pos, Span}; -use crate::comment::{rewrite_comment, CodeCharKind, CommentCodeSlices}; +use crate::comment::{is_last_comment_block, rewrite_comment, CodeCharKind, CommentCodeSlices}; use crate::config::file_lines::FileLines; use crate::config::{EmitMode, FileName}; use crate::shape::{Indent, Shape}; @@ -288,7 +288,7 @@ impl<'a> FmtVisitor<'a> { .next() { Some('\n') | Some('\r') => { - if !subslice.trim_end().ends_with("*/") { + if !is_last_comment_block(subslice) { self.push_str("\n"); } } diff --git a/tests/target/issue-1096.rs b/tests/target/issue-1096.rs new file mode 100644 index 00000000000..de78e736483 --- /dev/null +++ b/tests/target/issue-1096.rs @@ -0,0 +1,71 @@ +struct StructA /* comment 1 */ { + t: T, +} + +struct StructB /* comment 2 */; + +struct StructC /* comment 3 */; + +struct StructD /* comment 4 */ { + t: usize, +} + +struct StructE +/* comment 5 */ +where + T: Clone, +{ + t: usize, +} + +struct StructF +/* comment 6 */ +where + T: Clone, +{ + t: usize, +} + +struct StructG +/* comment 7 */ +// why a line comment?? +{ + t: T, +} + +struct StructH +/* comment 8 */ +// why a line comment?? +where + T: Clone, +{ + t: T, +} + +enum EnumA /* comment 8 */ { + Field(T), +} + +enum EnumB /* comment 9 */ { + Field, +} + +// Issue 2781 +struct StructX1 +// where +// T: Clone +{ + inner: String, +} + +struct StructX2< + T, + U: Iterator, + V: Iterator, + W: Iterator, +> +// where +// T: Clone +{ + inner: String, +}