1
Fork 0

Create AnnotationColumn struct to fix hard tab column numbers in errors

This commit is contained in:
pommicket 2023-03-28 08:12:36 -04:00
parent dd19135b04
commit b82608aa56
6 changed files with 117 additions and 36 deletions

View file

@ -202,7 +202,10 @@ impl AnnotateSnippetEmitterWriter {
annotations: annotations annotations: annotations
.iter() .iter()
.map(|annotation| SourceAnnotation { .map(|annotation| SourceAnnotation {
range: (annotation.start_col, annotation.end_col), range: (
annotation.start_col.display,
annotation.end_col.display,
),
label: annotation.label.as_deref().unwrap_or_default(), label: annotation.label.as_deref().unwrap_or_default(),
annotation_type: annotation_type_for_level(*level), annotation_type: annotation_type_for_level(*level),
}) })

View file

@ -12,7 +12,9 @@ use Destination::*;
use rustc_span::source_map::SourceMap; use rustc_span::source_map::SourceMap;
use rustc_span::{FileLines, SourceFile, Span}; use rustc_span::{FileLines, SourceFile, Span};
use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::snippet::{
Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString,
};
use crate::styled_buffer::StyledBuffer; use crate::styled_buffer::StyledBuffer;
use crate::translation::{to_fluent_args, Translate}; use crate::translation::{to_fluent_args, Translate};
use crate::{ use crate::{
@ -858,7 +860,7 @@ impl EmitterWriter {
let mut short_start = true; let mut short_start = true;
for ann in &line.annotations { for ann in &line.annotations {
if let AnnotationType::MultilineStart(depth) = ann.annotation_type { if let AnnotationType::MultilineStart(depth) = ann.annotation_type {
if source_string.chars().take(ann.start_col).all(|c| c.is_whitespace()) { if source_string.chars().take(ann.start_col.display).all(|c| c.is_whitespace()) {
let style = if ann.is_primary { let style = if ann.is_primary {
Style::UnderlinePrimary Style::UnderlinePrimary
} else { } else {
@ -1093,15 +1095,15 @@ impl EmitterWriter {
'_', '_',
line_offset + pos, line_offset + pos,
width_offset + depth, width_offset + depth,
(code_offset + annotation.start_col).saturating_sub(left), (code_offset + annotation.start_col.display).saturating_sub(left),
style, style,
); );
} }
_ if self.teach => { _ if self.teach => {
buffer.set_style_range( buffer.set_style_range(
line_offset, line_offset,
(code_offset + annotation.start_col).saturating_sub(left), (code_offset + annotation.start_col.display).saturating_sub(left),
(code_offset + annotation.end_col).saturating_sub(left), (code_offset + annotation.end_col.display).saturating_sub(left),
style, style,
annotation.is_primary, annotation.is_primary,
); );
@ -1133,7 +1135,7 @@ impl EmitterWriter {
for p in line_offset + 1..=line_offset + pos { for p in line_offset + 1..=line_offset + pos {
buffer.putc( buffer.putc(
p, p,
(code_offset + annotation.start_col).saturating_sub(left), (code_offset + annotation.start_col.display).saturating_sub(left),
'|', '|',
style, style,
); );
@ -1169,9 +1171,9 @@ impl EmitterWriter {
let style = let style =
if annotation.is_primary { Style::LabelPrimary } else { Style::LabelSecondary }; if annotation.is_primary { Style::LabelPrimary } else { Style::LabelSecondary };
let (pos, col) = if pos == 0 { let (pos, col) = if pos == 0 {
(pos + 1, (annotation.end_col + 1).saturating_sub(left)) (pos + 1, (annotation.end_col.display + 1).saturating_sub(left))
} else { } else {
(pos + 2, annotation.start_col.saturating_sub(left)) (pos + 2, annotation.start_col.display.saturating_sub(left))
}; };
if let Some(ref label) = annotation.label { if let Some(ref label) = annotation.label {
buffer.puts(line_offset + pos, code_offset + col, label, style); buffer.puts(line_offset + pos, code_offset + col, label, style);
@ -1208,7 +1210,7 @@ impl EmitterWriter {
} else { } else {
('-', Style::UnderlineSecondary) ('-', Style::UnderlineSecondary)
}; };
for p in annotation.start_col..annotation.end_col { for p in annotation.start_col.display..annotation.end_col.display {
buffer.putc( buffer.putc(
line_offset + 1, line_offset + 1,
(code_offset + p).saturating_sub(left), (code_offset + p).saturating_sub(left),
@ -1459,7 +1461,7 @@ impl EmitterWriter {
&annotated_file.file.name, &annotated_file.file.name,
line.line_index line.line_index
), ),
annotations[0].start_col + 1, annotations[0].start_col.file + 1,
), ),
Style::LineAndColumn, Style::LineAndColumn,
); );
@ -1546,7 +1548,7 @@ impl EmitterWriter {
buffer.prepend(buffer_msg_line_offset + 1, "::: ", Style::LineNumber); buffer.prepend(buffer_msg_line_offset + 1, "::: ", Style::LineNumber);
let loc = if let Some(first_line) = annotated_file.lines.first() { let loc = if let Some(first_line) = annotated_file.lines.first() {
let col = if let Some(first_annotation) = first_line.annotations.first() { let col = if let Some(first_annotation) = first_line.annotations.first() {
format!(":{}", first_annotation.start_col + 1) format!(":{}", first_annotation.start_col.file + 1)
} else { } else {
String::new() String::new()
}; };
@ -1607,8 +1609,8 @@ impl EmitterWriter {
let mut span_left_margin = usize::MAX; let mut span_left_margin = usize::MAX;
for line in &annotated_file.lines { for line in &annotated_file.lines {
for ann in &line.annotations { for ann in &line.annotations {
span_left_margin = min(span_left_margin, ann.start_col); span_left_margin = min(span_left_margin, ann.start_col.display);
span_left_margin = min(span_left_margin, ann.end_col); span_left_margin = min(span_left_margin, ann.end_col.display);
} }
} }
if span_left_margin == usize::MAX { if span_left_margin == usize::MAX {
@ -1625,11 +1627,12 @@ impl EmitterWriter {
annotated_file.file.get_line(line.line_index - 1).map_or(0, |s| s.len()), annotated_file.file.get_line(line.line_index - 1).map_or(0, |s| s.len()),
); );
for ann in &line.annotations { for ann in &line.annotations {
span_right_margin = max(span_right_margin, ann.start_col); span_right_margin = max(span_right_margin, ann.start_col.display);
span_right_margin = max(span_right_margin, ann.end_col); span_right_margin = max(span_right_margin, ann.end_col.display);
// FIXME: account for labels not in the same line // FIXME: account for labels not in the same line
let label_right = ann.label.as_ref().map_or(0, |l| l.len() + 1); let label_right = ann.label.as_ref().map_or(0, |l| l.len() + 1);
label_right_margin = max(label_right_margin, ann.end_col + label_right); label_right_margin =
max(label_right_margin, ann.end_col.display + label_right);
} }
} }
@ -2352,8 +2355,8 @@ impl FileWithAnnotatedLines {
depth: 1, depth: 1,
line_start: lo.line, line_start: lo.line,
line_end: hi.line, line_end: hi.line,
start_col: lo.col_display, start_col: AnnotationColumn::from_loc(&lo),
end_col: hi.col_display, end_col: AnnotationColumn::from_loc(&hi),
is_primary, is_primary,
label, label,
overlaps_exactly: false, overlaps_exactly: false,
@ -2361,8 +2364,8 @@ impl FileWithAnnotatedLines {
multiline_annotations.push((lo.file, ml)); multiline_annotations.push((lo.file, ml));
} else { } else {
let ann = Annotation { let ann = Annotation {
start_col: lo.col_display, start_col: AnnotationColumn::from_loc(&lo),
end_col: hi.col_display, end_col: AnnotationColumn::from_loc(&hi),
is_primary, is_primary,
label, label,
annotation_type: AnnotationType::Singleline, annotation_type: AnnotationType::Singleline,
@ -2551,7 +2554,13 @@ fn num_overlap(
(b_start..b_end + extra).contains(&a_start) || (a_start..a_end + extra).contains(&b_start) (b_start..b_end + extra).contains(&a_start) || (a_start..a_end + extra).contains(&b_start)
} }
fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool { fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool {
num_overlap(a1.start_col, a1.end_col + padding, a2.start_col, a2.end_col, false) num_overlap(
a1.start_col.display,
a1.end_col.display + padding,
a2.start_col.display,
a2.end_col.display,
false,
)
} }
fn emit_to_destination( fn emit_to_destination(

View file

@ -1,6 +1,6 @@
// Code for annotating snippets. // Code for annotating snippets.
use crate::Level; use crate::{Level, Loc};
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct Line { pub struct Line {
@ -8,13 +8,39 @@ pub struct Line {
pub annotations: Vec<Annotation>, pub annotations: Vec<Annotation>,
} }
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Default)]
pub struct AnnotationColumn {
/// the (0-indexed) column for *display* purposes, counted in characters, not utf-8 bytes
pub display: usize,
/// the (0-indexed) column in the file, counted in characters, not utf-8 bytes.
///
/// this may be different from `self.display`,
/// e.g. if the file contains hard tabs, because we convert tabs to spaces for error messages.
///
/// for example:
/// ```text
/// (hard tab)hello
/// ^ this is display column 4, but file column 1
/// ```
///
/// we want to keep around the correct file offset so that column numbers in error messages
/// are correct. (motivated by <https://github.com/rust-lang/rust/issues/109537>)
pub file: usize,
}
impl AnnotationColumn {
pub fn from_loc(loc: &Loc) -> AnnotationColumn {
AnnotationColumn { display: loc.col_display, file: loc.col.0 }
}
}
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct MultilineAnnotation { pub struct MultilineAnnotation {
pub depth: usize, pub depth: usize,
pub line_start: usize, pub line_start: usize,
pub line_end: usize, pub line_end: usize,
pub start_col: usize, pub start_col: AnnotationColumn,
pub end_col: usize, pub end_col: AnnotationColumn,
pub is_primary: bool, pub is_primary: bool,
pub label: Option<String>, pub label: Option<String>,
pub overlaps_exactly: bool, pub overlaps_exactly: bool,
@ -36,7 +62,12 @@ impl MultilineAnnotation {
pub fn as_start(&self) -> Annotation { pub fn as_start(&self) -> Annotation {
Annotation { Annotation {
start_col: self.start_col, start_col: self.start_col,
end_col: self.start_col + 1, end_col: AnnotationColumn {
// these might not correspond to the same place anymore,
// but that's okay for our purposes
display: self.start_col.display + 1,
file: self.start_col.file + 1,
},
is_primary: self.is_primary, is_primary: self.is_primary,
label: None, label: None,
annotation_type: AnnotationType::MultilineStart(self.depth), annotation_type: AnnotationType::MultilineStart(self.depth),
@ -45,7 +76,12 @@ impl MultilineAnnotation {
pub fn as_end(&self) -> Annotation { pub fn as_end(&self) -> Annotation {
Annotation { Annotation {
start_col: self.end_col.saturating_sub(1), start_col: AnnotationColumn {
// these might not correspond to the same place anymore,
// but that's okay for our purposes
display: self.end_col.display.saturating_sub(1),
file: self.end_col.file.saturating_sub(1),
},
end_col: self.end_col, end_col: self.end_col,
is_primary: self.is_primary, is_primary: self.is_primary,
label: self.label.clone(), label: self.label.clone(),
@ -55,8 +91,8 @@ impl MultilineAnnotation {
pub fn as_line(&self) -> Annotation { pub fn as_line(&self) -> Annotation {
Annotation { Annotation {
start_col: 0, start_col: Default::default(),
end_col: 0, end_col: Default::default(),
is_primary: self.is_primary, is_primary: self.is_primary,
label: None, label: None,
annotation_type: AnnotationType::MultilineLine(self.depth), annotation_type: AnnotationType::MultilineLine(self.depth),
@ -92,14 +128,14 @@ pub enum AnnotationType {
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct Annotation { pub struct Annotation {
/// Start column, 0-based indexing -- counting *characters*, not /// Start column.
/// utf-8 bytes. Note that it is important that this field goes /// Note that it is important that this field goes
/// first, so that when we sort, we sort orderings by start /// first, so that when we sort, we sort orderings by start
/// column. /// column.
pub start_col: usize, pub start_col: AnnotationColumn,
/// End column within the line (exclusive) /// End column within the line (exclusive)
pub end_col: usize, pub end_col: AnnotationColumn,
/// Is this annotation derived from primary span /// Is this annotation derived from primary span
pub is_primary: bool, pub is_primary: bool,
@ -118,12 +154,13 @@ impl Annotation {
matches!(self.annotation_type, AnnotationType::MultilineLine(_)) matches!(self.annotation_type, AnnotationType::MultilineLine(_))
} }
/// Length of this annotation as displayed in the stderr output
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
// Account for usize underflows // Account for usize underflows
if self.end_col > self.start_col { if self.end_col.display > self.start_col.display {
self.end_col - self.start_col self.end_col.display - self.start_col.display
} else { } else {
self.start_col - self.end_col self.start_col.display - self.end_col.display
} }
} }

View file

@ -0,0 +1,6 @@
// ignore-tidy-tab
pub struct S;
impl S {
fn method(&self) {}
}

View file

@ -0,0 +1,12 @@
// Test for #109537: ensure that column numbers are correctly generated when using hard tabs.
// aux-build:tab_column_numbers.rs
// ignore-tidy-tab
extern crate tab_column_numbers;
fn main() {
let s = tab_column_numbers::S;
s.method();
//~^ ERROR method `method` is private
}

View file

@ -0,0 +1,14 @@
error[E0624]: method `method` is private
--> $DIR/tab-column-numbers.rs:10:4
|
LL | s.method();
| ^^^^^^ private method
|
::: $DIR/auxiliary/tab_column_numbers.rs:5:3
|
LL | fn method(&self) {}
| ---------------- private method defined here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0624`.