diff --git a/src/attr.rs b/src/attr.rs index 41ba9a847e6..1aed53aa443 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -8,8 +8,10 @@ use self::doc_comment::DocCommentFormatter; use crate::comment::{contains_comment, rewrite_doc_comment, CommentStyle}; use crate::config::lists::*; use crate::config::IndentStyle; +use crate::config::Version; use crate::expr::rewrite_literal; use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; +use crate::missed_spans::push_vertical_spaces; use crate::overflow; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; @@ -419,6 +421,7 @@ impl Rewrite for [ast::Attribute] { shape.with_max_width(context.config), context, 0, + false, )?; let comment = if comment.is_empty() { format!("\n{}", mlb) @@ -449,6 +452,7 @@ impl Rewrite for [ast::Attribute] { shape.with_max_width(context.config), context, 0, + false, )?; result.push_str(&comment); if let Some(next) = attrs.get(derives.len()) { @@ -476,12 +480,25 @@ impl Rewrite for [ast::Attribute] { let missing_span = attrs .get(1) .map(|next| mk_sp(attrs[0].span.hi(), next.span.lo())); + + let mut vertical_lines_pushed = 0; if let Some(missing_span) = missing_span { + let snippet = context.snippet(missing_span); + if let Some((whitespace, _)) = snippet.split_once(|c: char| !c.is_whitespace()) { + let newlines = count_newlines(whitespace); + // Version gate this change as it leads to breaking changes with Version::One + if newlines > 0 && context.config.version() == Version::Two { + vertical_lines_pushed = + push_vertical_spaces(&mut result, context.config, newlines); + } + } + // remove leading newlines if we already pushed vertical space into the result let comment = crate::comment::recover_missing_comment_in_span( missing_span, shape.with_max_width(context.config), context, 0, + vertical_lines_pushed > 0, )?; result.push_str(&comment); if let Some(next) = attrs.get(1) { diff --git a/src/comment.rs b/src/comment.rs index 4d565afc1e0..46c296e63df 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -965,6 +965,7 @@ pub(crate) fn recover_missing_comment_in_span( shape: Shape, context: &RewriteContext<'_>, used_width: usize, + remove_leading_newlines: bool, ) -> Option { let missing_comment = rewrite_missing_comment(span, shape, context)?; if missing_comment.is_empty() { @@ -976,7 +977,9 @@ pub(crate) fn recover_missing_comment_in_span( let total_width = missing_comment.len() + used_width + 1; let force_new_line_before_comment = missing_snippet[..pos].contains('\n') || total_width > context.config.max_width(); - let sep = if force_new_line_before_comment { + let sep = if remove_leading_newlines && force_new_line_before_comment { + shape.indent.to_string(context.config) + } else if force_new_line_before_comment { shape.indent.to_string_with_newline(context.config) } else { Cow::from(" ") diff --git a/src/items.rs b/src/items.rs index bab881f4b4e..7a5e990055a 100644 --- a/src/items.rs +++ b/src/items.rs @@ -713,6 +713,7 @@ pub(crate) fn format_impl( Shape::indented(offset, context.config), context, last_line_width(&result), + false, ) { Some(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); @@ -1092,6 +1093,7 @@ pub(crate) fn format_trait( Shape::indented(offset, context.config), context, last_line_width(&result), + false, ) { Some(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); @@ -2482,6 +2484,7 @@ fn rewrite_fn_base( shape, context, last_line_width(&result), + false, ) { Some(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 28edcb784b4..be2d024ff31 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -9,6 +9,7 @@ use crate::shape::{Indent, Shape}; use crate::source_map::LineRangeUtils; use crate::utils::{count_lf_crlf, count_newlines, last_line_width, mk_sp}; use crate::visitor::FmtVisitor; +use crate::Config; struct SnippetStatus { /// An offset to the current line from the beginning of the original snippet. @@ -112,27 +113,9 @@ impl<'a> FmtVisitor<'a> { } } - fn push_vertical_spaces(&mut self, mut newline_count: usize) { - let offset = self.buffer.chars().rev().take_while(|c| *c == '\n').count(); - let newline_upper_bound = self.config.blank_lines_upper_bound() + 1; - let newline_lower_bound = self.config.blank_lines_lower_bound() + 1; - - if newline_count + offset > newline_upper_bound { - if offset >= newline_upper_bound { - newline_count = 0; - } else { - newline_count = newline_upper_bound - offset; - } - } else if newline_count + offset < newline_lower_bound { - if offset >= newline_lower_bound { - newline_count = 0; - } else { - newline_count = newline_lower_bound - offset; - } - } - - let blank_lines = "\n".repeat(newline_count); - self.push_str(&blank_lines); + fn push_vertical_spaces(&mut self, newline_count: usize) { + let newlines_pushed = push_vertical_spaces(&mut self.buffer, self.config, newline_count); + self.line_number += newlines_pushed; } fn write_snippet(&mut self, span: Span, process_last_snippet: F) @@ -361,3 +344,31 @@ impl<'a> FmtVisitor<'a> { } } } + +pub(crate) fn push_vertical_spaces( + buffer: &mut String, + config: &Config, + mut newline_count: usize, +) -> usize { + let offset = buffer.chars().rev().take_while(|c| *c == '\n').count(); + let newline_upper_bound = config.blank_lines_upper_bound() + 1; + let newline_lower_bound = config.blank_lines_lower_bound() + 1; + + if newline_count + offset > newline_upper_bound { + if offset >= newline_upper_bound { + newline_count = 0; + } else { + newline_count = newline_upper_bound - offset; + } + } else if newline_count + offset < newline_lower_bound { + if offset >= newline_lower_bound { + newline_count = 0; + } else { + newline_count = newline_lower_bound - offset; + } + } + + let blank_lines = "\n".repeat(newline_count); + buffer.push_str(&blank_lines); + newline_count +} diff --git a/tests/source/issue-4082/version_one/original_example.rs b/tests/source/issue-4082/version_one/original_example.rs new file mode 100644 index 00000000000..d83cbc06c4b --- /dev/null +++ b/tests/source/issue-4082/version_one/original_example.rs @@ -0,0 +1,18 @@ +// rustfmt-version: One + +#![feature(core_intrinsics)] +#![feature(lang_items)] +#![feature(libc)] +#![feature(nll)] +#![feature(panic_unwind)] +#![feature(staged_api)] +#![feature(std_internals)] +#![feature(unwind_attributes)] +#![feature(abi_thiscall)] +#![feature(rustc_attrs)] +#![feature(raw)] +#![panic_runtime] +#![feature(panic_runtime)] + +// `real_imp` is unused with Miri, so silence warnings. +#![cfg_attr(miri, allow(dead_code))] diff --git a/tests/source/issue-4082/version_two/add_newlines.rs b/tests/source/issue-4082/version_two/add_newlines.rs new file mode 100644 index 00000000000..9bbd86d4685 --- /dev/null +++ b/tests/source/issue-4082/version_two/add_newlines.rs @@ -0,0 +1,19 @@ +// rustfmt-version: Two +// rustfmt-blank_lines_upper_bound: 2 +// rustfmt-blank_lines_lower_bound: 2 +#![feature(core_intrinsics)] +#![feature(lang_items)] +#![feature(libc)] +#![feature(nll)] +#![feature(panic_unwind)] +#![feature(staged_api)] +#![feature(std_internals)] +#![feature(unwind_attributes)] +#![feature(abi_thiscall)] +#![feature(rustc_attrs)] +#![feature(raw)] +#![panic_runtime] +#![feature(panic_runtime)] + +// `real_imp` is unused with Miri, so silence warnings. +#![cfg_attr(miri, allow(dead_code))] diff --git a/tests/source/issue-4082/version_two/remove_newlines.rs b/tests/source/issue-4082/version_two/remove_newlines.rs new file mode 100644 index 00000000000..52734ce7f43 --- /dev/null +++ b/tests/source/issue-4082/version_two/remove_newlines.rs @@ -0,0 +1,23 @@ +// rustfmt-version: Two +// rustfmt-blank_lines_upper_bound: 2 +// rustfmt-blank_lines_lower_bound: 2 +#![feature(core_intrinsics)] +#![feature(lang_items)] +#![feature(libc)] +#![feature(nll)] +#![feature(panic_unwind)] +#![feature(staged_api)] +#![feature(std_internals)] +#![feature(unwind_attributes)] +#![feature(abi_thiscall)] +#![feature(rustc_attrs)] +#![feature(raw)] +#![panic_runtime] +#![feature(panic_runtime)] + + + + + +// `real_imp` is unused with Miri, so silence warnings. +#![cfg_attr(miri, allow(dead_code))] diff --git a/tests/target/issue-4082/version_one/original_example.rs b/tests/target/issue-4082/version_one/original_example.rs new file mode 100644 index 00000000000..5b3f7862afd --- /dev/null +++ b/tests/target/issue-4082/version_one/original_example.rs @@ -0,0 +1,17 @@ +// rustfmt-version: One + +#![feature(core_intrinsics)] +#![feature(lang_items)] +#![feature(libc)] +#![feature(nll)] +#![feature(panic_unwind)] +#![feature(staged_api)] +#![feature(std_internals)] +#![feature(unwind_attributes)] +#![feature(abi_thiscall)] +#![feature(rustc_attrs)] +#![feature(raw)] +#![panic_runtime] +#![feature(panic_runtime)] +// `real_imp` is unused with Miri, so silence warnings. +#![cfg_attr(miri, allow(dead_code))] diff --git a/tests/target/issue-4082/version_two/add_newlines.rs b/tests/target/issue-4082/version_two/add_newlines.rs new file mode 100644 index 00000000000..fc39010857e --- /dev/null +++ b/tests/target/issue-4082/version_two/add_newlines.rs @@ -0,0 +1,20 @@ +// rustfmt-version: Two +// rustfmt-blank_lines_upper_bound: 2 +// rustfmt-blank_lines_lower_bound: 2 +#![feature(core_intrinsics)] +#![feature(lang_items)] +#![feature(libc)] +#![feature(nll)] +#![feature(panic_unwind)] +#![feature(staged_api)] +#![feature(std_internals)] +#![feature(unwind_attributes)] +#![feature(abi_thiscall)] +#![feature(rustc_attrs)] +#![feature(raw)] +#![panic_runtime] +#![feature(panic_runtime)] + + +// `real_imp` is unused with Miri, so silence warnings. +#![cfg_attr(miri, allow(dead_code))] diff --git a/tests/target/issue-4082/version_two/original_example.rs b/tests/target/issue-4082/version_two/original_example.rs new file mode 100644 index 00000000000..0e9dce4203d --- /dev/null +++ b/tests/target/issue-4082/version_two/original_example.rs @@ -0,0 +1,18 @@ +// rustfmt-version: Two + +#![feature(core_intrinsics)] +#![feature(lang_items)] +#![feature(libc)] +#![feature(nll)] +#![feature(panic_unwind)] +#![feature(staged_api)] +#![feature(std_internals)] +#![feature(unwind_attributes)] +#![feature(abi_thiscall)] +#![feature(rustc_attrs)] +#![feature(raw)] +#![panic_runtime] +#![feature(panic_runtime)] + +// `real_imp` is unused with Miri, so silence warnings. +#![cfg_attr(miri, allow(dead_code))] diff --git a/tests/target/issue-4082/version_two/remove_newlines.rs b/tests/target/issue-4082/version_two/remove_newlines.rs new file mode 100644 index 00000000000..fc39010857e --- /dev/null +++ b/tests/target/issue-4082/version_two/remove_newlines.rs @@ -0,0 +1,20 @@ +// rustfmt-version: Two +// rustfmt-blank_lines_upper_bound: 2 +// rustfmt-blank_lines_lower_bound: 2 +#![feature(core_intrinsics)] +#![feature(lang_items)] +#![feature(libc)] +#![feature(nll)] +#![feature(panic_unwind)] +#![feature(staged_api)] +#![feature(std_internals)] +#![feature(unwind_attributes)] +#![feature(abi_thiscall)] +#![feature(rustc_attrs)] +#![feature(raw)] +#![panic_runtime] +#![feature(panic_runtime)] + + +// `real_imp` is unused with Miri, so silence warnings. +#![cfg_attr(miri, allow(dead_code))]