mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-22 20:03:37 +00:00
keep track of lines which formatting was disabled in order to prevent indentation which would cause code right-shifting
This commit is contained in:
parent
9c75a15f4c
commit
2f5d864c08
@ -620,7 +620,7 @@ impl<'a> CommentRewrite<'a> {
|
||||
let mut config = self.fmt.config.clone();
|
||||
config.set().format_doc_comments(false);
|
||||
match ::format_code_block(&self.code_block_buffer, &config) {
|
||||
Some(ref s) => trim_custom_comment_prefix(s),
|
||||
Some(ref s) => trim_custom_comment_prefix(&s.snippet),
|
||||
None => trim_custom_comment_prefix(&self.code_block_buffer),
|
||||
}
|
||||
}
|
||||
|
@ -173,6 +173,8 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
|
||||
if visitor.macro_rewrite_failure {
|
||||
self.report.add_macro_format_failure();
|
||||
}
|
||||
self.report
|
||||
.add_non_formatted_ranges(visitor.skipped_range.clone());
|
||||
|
||||
self.handler
|
||||
.handle_formatted_file(path, visitor.buffer.to_owned(), &mut self.report)
|
||||
|
83
src/lib.rs
83
src/lib.rs
@ -144,6 +144,34 @@ impl From<io::Error> for ErrorKind {
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of formatting a snippet of code along with ranges of lines that didn't get formatted,
|
||||
/// i.e., that got returned as they were originally.
|
||||
#[derive(Debug)]
|
||||
struct FormattedSnippet {
|
||||
snippet: String,
|
||||
non_formatted_ranges: Vec<(usize, usize)>,
|
||||
}
|
||||
|
||||
impl FormattedSnippet {
|
||||
/// In case the snippet needed to be wrapped in a function, this shifts down the ranges of
|
||||
/// non-formatted code.
|
||||
fn unwrap_code_block(&mut self) {
|
||||
self.non_formatted_ranges
|
||||
.iter_mut()
|
||||
.for_each(|(low, high)| {
|
||||
*low -= 1;
|
||||
*high -= 1;
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns true if the line n did not get formatted.
|
||||
fn is_line_non_formatted(&self, n: usize) -> bool {
|
||||
self.non_formatted_ranges
|
||||
.iter()
|
||||
.any(|(low, high)| *low <= n && n <= *high)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reports on any issues that occurred during a run of Rustfmt.
|
||||
///
|
||||
/// Can be reported to the user via its `Display` implementation of `print_fancy`.
|
||||
@ -151,15 +179,21 @@ impl From<io::Error> for ErrorKind {
|
||||
pub struct FormatReport {
|
||||
// Maps stringified file paths to their associated formatting errors.
|
||||
internal: Rc<RefCell<(FormatErrorMap, ReportedErrors)>>,
|
||||
non_formatted_ranges: Vec<(usize, usize)>,
|
||||
}
|
||||
|
||||
impl FormatReport {
|
||||
fn new() -> FormatReport {
|
||||
FormatReport {
|
||||
internal: Rc::new(RefCell::new((HashMap::new(), ReportedErrors::default()))),
|
||||
non_formatted_ranges: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_non_formatted_ranges(&mut self, mut ranges: Vec<(usize, usize)>) {
|
||||
self.non_formatted_ranges.append(&mut ranges);
|
||||
}
|
||||
|
||||
fn append(&self, f: FileName, mut v: Vec<FormattingError>) {
|
||||
self.track_errors(&v);
|
||||
self.internal
|
||||
@ -349,37 +383,44 @@ impl fmt::Display for FormatReport {
|
||||
|
||||
/// Format the given snippet. The snippet is expected to be *complete* code.
|
||||
/// When we cannot parse the given snippet, this function returns `None`.
|
||||
fn format_snippet(snippet: &str, config: &Config) -> Option<String> {
|
||||
fn format_snippet(snippet: &str, config: &Config) -> Option<FormattedSnippet> {
|
||||
let mut config = config.clone();
|
||||
let out = panic::catch_unwind(|| {
|
||||
panic::catch_unwind(|| {
|
||||
let mut out: Vec<u8> = Vec::with_capacity(snippet.len() * 2);
|
||||
config.set().emit_mode(config::EmitMode::Stdout);
|
||||
config.set().verbose(Verbosity::Quiet);
|
||||
config.set().hide_parse_errors(true);
|
||||
let formatting_error = {
|
||||
|
||||
let (formatting_error, result) = {
|
||||
let input = Input::Text(snippet.into());
|
||||
let mut session = Session::new(config, Some(&mut out));
|
||||
let result = session.format(input);
|
||||
session.errors.has_macro_format_failure
|
||||
|| session.out.as_ref().unwrap().is_empty() && !snippet.is_empty()
|
||||
|| result.is_err()
|
||||
(
|
||||
session.errors.has_macro_format_failure
|
||||
|| session.out.as_ref().unwrap().is_empty() && !snippet.is_empty()
|
||||
|| result.is_err(),
|
||||
result,
|
||||
)
|
||||
};
|
||||
if formatting_error {
|
||||
None
|
||||
} else {
|
||||
Some(out)
|
||||
String::from_utf8(out).ok().map(|snippet| FormattedSnippet {
|
||||
snippet,
|
||||
non_formatted_ranges: result.unwrap().non_formatted_ranges,
|
||||
})
|
||||
}
|
||||
})
|
||||
.ok()??; // The first try operator handles the error from catch_unwind,
|
||||
// whereas the second one handles None from the closure.
|
||||
String::from_utf8(out).ok()
|
||||
// Discard panics encountered while formatting the snippet
|
||||
// The ? operator is needed to remove the extra Option
|
||||
.ok()?
|
||||
}
|
||||
|
||||
/// Format the given code block. Mainly targeted for code block in comment.
|
||||
/// The code block may be incomplete (i.e. parser may be unable to parse it).
|
||||
/// To avoid panic in parser, we wrap the code block with a dummy function.
|
||||
/// The returned code block does *not* end with newline.
|
||||
fn format_code_block(code_snippet: &str, config: &Config) -> Option<String> {
|
||||
fn format_code_block(code_snippet: &str, config: &Config) -> Option<FormattedSnippet> {
|
||||
const FN_MAIN_PREFIX: &str = "fn main() {\n";
|
||||
|
||||
fn enclose_in_main_block(s: &str, config: &Config) -> String {
|
||||
@ -412,13 +453,18 @@ fn format_code_block(code_snippet: &str, config: &Config) -> Option<String> {
|
||||
config_with_unix_newline
|
||||
.set()
|
||||
.newline_style(NewlineStyle::Unix);
|
||||
let formatted = format_snippet(&snippet, &config_with_unix_newline)?;
|
||||
let mut formatted = format_snippet(&snippet, &config_with_unix_newline)?;
|
||||
// Remove wrapping main block
|
||||
formatted.unwrap_code_block();
|
||||
|
||||
// Trim "fn main() {" on the first line and "}" on the last line,
|
||||
// then unindent the whole code block.
|
||||
let block_len = formatted.rfind('}').unwrap_or(formatted.len());
|
||||
let block_len = formatted
|
||||
.snippet
|
||||
.rfind('}')
|
||||
.unwrap_or(formatted.snippet.len());
|
||||
let mut is_indented = true;
|
||||
for (kind, ref line) in LineClasses::new(&formatted[FN_MAIN_PREFIX.len()..block_len]) {
|
||||
for (kind, ref line) in LineClasses::new(&formatted.snippet[FN_MAIN_PREFIX.len()..block_len]) {
|
||||
if !is_first {
|
||||
result.push('\n');
|
||||
} else {
|
||||
@ -451,7 +497,10 @@ fn format_code_block(code_snippet: &str, config: &Config) -> Option<String> {
|
||||
result.push_str(trimmed_line);
|
||||
is_indented = !kind.is_string() || line.ends_with('\\');
|
||||
}
|
||||
Some(result)
|
||||
Some(FormattedSnippet {
|
||||
snippet: result,
|
||||
non_formatted_ranges: formatted.non_formatted_ranges,
|
||||
})
|
||||
}
|
||||
|
||||
/// A session is a run of rustfmt across a single or multiple inputs.
|
||||
@ -571,10 +620,10 @@ mod unit_tests {
|
||||
|
||||
fn test_format_inner<F>(formatter: F, input: &str, expected: &str) -> bool
|
||||
where
|
||||
F: Fn(&str, &Config) -> Option<String>,
|
||||
F: Fn(&str, &Config) -> Option<FormattedSnippet>,
|
||||
{
|
||||
let output = formatter(input, &Config::default());
|
||||
output.is_some() && output.unwrap() == expected
|
||||
output.is_some() && output.unwrap().snippet == expected
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1323,7 +1323,7 @@ impl MacroBranch {
|
||||
config.set().max_width(new_width);
|
||||
|
||||
// First try to format as items, then as statements.
|
||||
let new_body = match ::format_snippet(&body_str, &config) {
|
||||
let new_body_snippet = match ::format_snippet(&body_str, &config) {
|
||||
Some(new_body) => new_body,
|
||||
None => {
|
||||
let new_width = new_width + config.tab_spaces();
|
||||
@ -1334,15 +1334,23 @@ impl MacroBranch {
|
||||
}
|
||||
}
|
||||
};
|
||||
let new_body = wrap_str(new_body, config.max_width(), shape)?;
|
||||
let new_body = wrap_str(
|
||||
new_body_snippet.snippet.to_string(),
|
||||
config.max_width(),
|
||||
shape,
|
||||
)?;
|
||||
|
||||
// Indent the body since it is in a block.
|
||||
let indent_str = body_indent.to_string(&config);
|
||||
let mut new_body = LineClasses::new(new_body.trim_right())
|
||||
.enumerate()
|
||||
.fold(
|
||||
(String::new(), true),
|
||||
|(mut s, need_indent), (kind, ref l)| {
|
||||
if !l.is_empty() && need_indent {
|
||||
|(mut s, need_indent), (i, (kind, ref l))| {
|
||||
if !l.is_empty()
|
||||
&& need_indent
|
||||
&& !new_body_snippet.is_line_non_formatted(i + 1)
|
||||
{
|
||||
s += &indent_str;
|
||||
}
|
||||
(s + l + "\n", !kind.is_string() || l.ends_with('\\'))
|
||||
|
@ -29,7 +29,7 @@ use source_map::{LineRangeUtils, SpanUtils};
|
||||
use spanned::Spanned;
|
||||
use utils::{
|
||||
self, contains_skip, count_newlines, inner_attributes, mk_sp, ptr_vec_to_ref_vec,
|
||||
rewrite_ident, trim_left_preserve_layout, DEPR_SKIP_ANNOTATION,
|
||||
rewrite_ident, DEPR_SKIP_ANNOTATION,
|
||||
};
|
||||
use {ErrorKind, FormatReport, FormattingError};
|
||||
|
||||
@ -71,6 +71,8 @@ pub struct FmtVisitor<'a> {
|
||||
pub is_if_else_block: bool,
|
||||
pub snippet_provider: &'a SnippetProvider<'a>,
|
||||
pub line_number: usize,
|
||||
/// List of 1-based line ranges which were annotated with skip
|
||||
/// Both bounds are inclusifs.
|
||||
pub skipped_range: Vec<(usize, usize)>,
|
||||
pub macro_rewrite_failure: bool,
|
||||
pub(crate) report: FormatReport,
|
||||
@ -109,8 +111,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
self.format_missing(stmt.span.hi());
|
||||
}
|
||||
ast::StmtKind::Local(..) | ast::StmtKind::Expr(..) | ast::StmtKind::Semi(..) => {
|
||||
if contains_skip(get_attrs_from_stmt(stmt)) {
|
||||
self.push_skipped_with_span(stmt.span());
|
||||
let attrs = get_attrs_from_stmt(stmt);
|
||||
if contains_skip(attrs) {
|
||||
self.push_skipped_with_span(attrs, stmt.span());
|
||||
} else {
|
||||
let shape = self.shape();
|
||||
let rewrite = self.with_context(|ctx| stmt.rewrite(&ctx, shape));
|
||||
@ -120,7 +123,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
ast::StmtKind::Mac(ref mac) => {
|
||||
let (ref mac, _macro_style, ref attrs) = **mac;
|
||||
if self.visit_attrs(attrs, ast::AttrStyle::Outer) {
|
||||
self.push_skipped_with_span(stmt.span());
|
||||
self.push_skipped_with_span(attrs, stmt.span());
|
||||
} else {
|
||||
self.visit_mac(mac, None, MacroPosition::Statement);
|
||||
}
|
||||
@ -328,14 +331,14 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
// For use items, skip rewriting attributes. Just check for a skip attribute.
|
||||
ast::ItemKind::Use(..) => {
|
||||
if contains_skip(attrs) {
|
||||
self.push_skipped_with_span(item.span());
|
||||
self.push_skipped_with_span(attrs.as_slice(), item.span());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Module is inline, in this case we treat it like any other item.
|
||||
_ if !is_mod_decl(item) => {
|
||||
if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
|
||||
self.push_skipped_with_span(item.span());
|
||||
self.push_skipped_with_span(item.attrs.as_slice(), item.span());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -354,7 +357,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
}
|
||||
_ => {
|
||||
if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
|
||||
self.push_skipped_with_span(item.span());
|
||||
self.push_skipped_with_span(item.attrs.as_slice(), item.span());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -471,7 +474,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
skip_out_of_file_lines_range_visitor!(self, ti.span);
|
||||
|
||||
if self.visit_attrs(&ti.attrs, ast::AttrStyle::Outer) {
|
||||
self.push_skipped_with_span(ti.span());
|
||||
self.push_skipped_with_span(ti.attrs.as_slice(), ti.span());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -515,7 +518,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
skip_out_of_file_lines_range_visitor!(self, ii.span);
|
||||
|
||||
if self.visit_attrs(&ii.attrs, ast::AttrStyle::Outer) {
|
||||
self.push_skipped_with_span(ii.span());
|
||||
self.push_skipped_with_span(ii.attrs.as_slice(), ii.span());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -574,16 +577,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn push_rewrite_inner(&mut self, span: Span, rewrite: Option<String>, is_skipped: bool) {
|
||||
fn push_rewrite_inner(&mut self, span: Span, rewrite: Option<String>) {
|
||||
if let Some(ref s) = rewrite {
|
||||
self.push_str(s);
|
||||
} else if is_skipped {
|
||||
// in case the code block (e.g., inside a macro or a doc) is skipped a minimum of
|
||||
// leading whitespaces is trimmed so that the code layout is kept but allows it to
|
||||
// be indented as necessary
|
||||
let snippet =
|
||||
trim_left_preserve_layout(self.snippet(span), &self.block_indent, self.config);
|
||||
self.push_str(&snippet);
|
||||
} else {
|
||||
let snippet = self.snippet(span);
|
||||
self.push_str(snippet);
|
||||
@ -593,13 +589,20 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
||||
|
||||
pub fn push_rewrite(&mut self, span: Span, rewrite: Option<String>) {
|
||||
self.format_missing_with_indent(source!(self, span).lo());
|
||||
self.push_rewrite_inner(span, rewrite, false);
|
||||
self.push_rewrite_inner(span, rewrite);
|
||||
}
|
||||
|
||||
pub fn push_skipped_with_span(&mut self, span: Span) {
|
||||
self.format_missing_with_indent(source!(self, span).lo());
|
||||
let lo = self.line_number + 1;
|
||||
self.push_rewrite_inner(span, None, true);
|
||||
pub fn push_skipped_with_span(&mut self, attrs: &[ast::Attribute], item_span: Span) {
|
||||
self.format_missing_with_indent(source!(self, item_span).lo());
|
||||
// do not take into account the lines with attributes as part of the skipped range
|
||||
let attrs_end = attrs
|
||||
.iter()
|
||||
.map(|attr| self.source_map.lookup_char_pos(attr.span().hi()).line)
|
||||
.max()
|
||||
.unwrap_or(1);
|
||||
// Add 1 to get the line past the last attribute
|
||||
let lo = attrs_end + 1;
|
||||
self.push_rewrite_inner(item_span, None);
|
||||
let hi = self.line_number + 1;
|
||||
self.skipped_range.push((lo, hi));
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
// rustfmt-wrap_comments: true
|
||||
|
||||
/// ```
|
||||
/// pub unsafe fn _mm256_shufflehi_epi16(a: __m256i, imm8: i32) -> __m256i {
|
||||
/// let imm8 = (imm8 & 0xFF) as u8;
|
||||
/// let a = a.as_i16x16();
|
||||
/// macro_rules! shuffle_done {
|
||||
/// ($x01:expr, $x23:expr, $x45:expr, $x67:expr) => {
|
||||
/// #[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
/// simd_shuffle16(a, a, [
|
||||
/// 0, 1, 2, 3, 4+$x01, 4+$x23, 4+$x45, 4+$x67,
|
||||
/// 8, 9, 10, 11, 12+$x01, 12+$x23, 12+$x45, 12+$x67
|
||||
/// ]);
|
||||
/// };
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub unsafe fn _mm256_shufflehi_epi16(a: __m256i, imm8: i32) -> __m256i {
|
||||
let imm8 = (imm8 & 0xFF) as u8;
|
||||
let a = a.as_i16x16();
|
||||
macro_rules! shuffle_done {
|
||||
($x01:expr, $x23:expr, $x45:expr, $x67:expr) => {
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
simd_shuffle16(a, a, [
|
||||
0, 1, 2, 3, 4+$x01, 4+$x23, 4+$x45, 4+$x67,
|
||||
8, 9, 10, 11, 12+$x01, 12+$x23, 12+$x45, 12+$x67
|
||||
]);
|
||||
};
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
// rustfmt-wrap_comments: true
|
||||
|
||||
/// Although the indentation of the skipped method is off, it shouldn't be
|
||||
/// changed.
|
||||
///
|
||||
/// ```
|
||||
/// pub unsafe fn _mm256_shufflehi_epi16(a: __m256i, imm8: i32) -> __m256i {
|
||||
/// let imm8 = (imm8 & 0xFF) as u8;
|
||||
@ -7,10 +10,10 @@
|
||||
/// macro_rules! shuffle_done {
|
||||
/// ($x01:expr, $x23:expr, $x45:expr, $x67:expr) => {
|
||||
/// #[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
/// simd_shuffle16(a, a, [
|
||||
/// 0, 1, 2, 3, 4+$x01, 4+$x23, 4+$x45, 4+$x67,
|
||||
/// 8, 9, 10, 11, 12+$x01, 12+$x23, 12+$x45, 12+$x67
|
||||
/// ]);
|
||||
/// simd_shuffle16(a, a, [
|
||||
/// 0, 1, 2, 3, 4+$x01, 4+$x23, 4+$x45, 4+$x67,
|
||||
/// 8, 9, 10, 11, 12+$x01, 12+$x23, 12+$x45, 12+$x67
|
||||
/// ]);
|
||||
/// };
|
||||
/// }
|
||||
/// }
|
||||
@ -21,7 +24,22 @@ pub unsafe fn _mm256_shufflehi_epi16(a: __m256i, imm8: i32) -> __m256i {
|
||||
macro_rules! shuffle_done {
|
||||
($x01:expr, $x23:expr, $x45:expr, $x67:expr) => {
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
simd_shuffle16(a, a, [
|
||||
simd_shuffle16(a, a, [
|
||||
0, 1, 2, 3, 4+$x01, 4+$x23, 4+$x45, 4+$x67,
|
||||
8, 9, 10, 11, 12+$x01, 12+$x23, 12+$x45, 12+$x67
|
||||
]);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// The skipped method shouldn't right-shift
|
||||
pub unsafe fn _mm256_shufflehi_epi32(a: __m256i, imm8: i32) -> __m256i {
|
||||
let imm8 = (imm8 & 0xFF) as u8;
|
||||
let a = a.as_i16x16();
|
||||
macro_rules! shuffle_done {
|
||||
($x01:expr, $x23:expr, $x45:expr, $x67:expr) => {
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
simd_shuffle32(a, a, [
|
||||
0, 1, 2, 3, 4+$x01, 4+$x23, 4+$x45, 4+$x67,
|
||||
8, 9, 10, 11, 12+$x01, 12+$x23, 12+$x45, 12+$x67
|
||||
]);
|
||||
|
Loading…
Reference in New Issue
Block a user