diff --git a/src/formatting.rs b/src/formatting.rs index 98d206fc47d..41ae61f64c2 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -318,6 +318,10 @@ pub(crate) struct FormattingError { is_comment: bool, is_string: bool, pub(crate) line_buffer: String, + // Byte offset in `line_buffer` where a `LineOverflow` annotation begins. + // The widths in `LineOverflow` are visual columns (a tab counts as + // `tab_spaces`), so they can't be used to index into the buffer. + overflow_start: usize, } impl FormattingError { @@ -328,6 +332,7 @@ impl FormattingError { kind, is_string: false, line_buffer: psess.span_to_first_line_string(span), + overflow_start: 0, } } @@ -353,10 +358,13 @@ impl FormattingError { } } - // (space, target) + // (annotation start, annotation length) in bytes of `line_buffer` pub(crate) fn format_len(&self) -> (usize, usize) { match self.kind { - ErrorKind::LineOverflow(found, max) => (max, found - max), + ErrorKind::LineOverflow(..) => ( + self.overflow_start, + self.line_buffer.len() - self.overflow_start, + ), ErrorKind::TrailingWhitespace | ErrorKind::DeprecatedAttr | ErrorKind::BadAttr @@ -603,15 +611,38 @@ impl<'a> FormatLines<'a> { } fn push_err(&mut self, kind: ErrorKind, is_comment: bool, is_string: bool) { + let overflow_start = match kind { + ErrorKind::LineOverflow(..) => self.overflow_start(), + _ => 0, + }; self.errors.push(FormattingError { line: self.cur_line, kind, is_comment, is_string, line_buffer: self.line_buffer.clone(), + overflow_start, }); } + /// Returns the byte offset in `line_buffer` of the first character that extends past + /// `max_width`, counting a tab as `tab_spaces` columns like `line_len` does. + fn overflow_start(&self) -> usize { + let max_width = self.config.max_width(); + let mut width = 0; + for (offset, c) in self.line_buffer.char_indices() { + width += if c == '\t' { + self.config.tab_spaces() + } else { + 1 + }; + if width > max_width { + return offset; + } + } + self.line_buffer.len() + } + fn should_report_error(&self, char_kind: FullCodeCharKind, error_kind: &ErrorKind) -> bool { let allow_error_report = if char_kind.is_comment() || self.current_line_contains_string_literal diff --git a/src/test/mod.rs b/src/test/mod.rs index 6089c9a08ea..31c7ca2c346 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -262,7 +262,11 @@ fn warning_tests() { for file in &files { let snapshot_name = file.file_stem().unwrap().to_str().unwrap(); - let (parsing_errors, _, report) = format_file(file, config.clone()); + let mut config = config.clone(); + for (key, val) in read_significant_comments(file) { + config.override_value(&key, &val); + } + let (parsing_errors, _, report) = format_file(file, config); assert!(!parsing_errors, "{} failed to parse", file.display()); assert!( report.has_warnings(), diff --git a/tests/warning/snapshots/issue_6442.snap b/tests/warning/snapshots/issue_6442.snap new file mode 100644 index 00000000000..75e7888b3cc --- /dev/null +++ b/tests/warning/snapshots/issue_6442.snap @@ -0,0 +1,12 @@ +--- +source: src/test/mod.rs +--- +error[internal]: line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option), found: 125) + --> tests/warning/source/issue_6442.rs:4:4:98 + | +4 | // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: set `error_on_unformatted = false` to suppress the warning against comments or string literals + +warning: rustfmt has failed to format. See previous 1 errors. diff --git a/tests/warning/source/issue_6442.rs b/tests/warning/source/issue_6442.rs new file mode 100644 index 00000000000..f3760c97231 --- /dev/null +++ b/tests/warning/source/issue_6442.rs @@ -0,0 +1,5 @@ +// rustfmt-hard_tabs: true + +fn main() { + // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +}