From 631cd5845e8de329d0e227aaa707d7ea228b8f8f Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:20:29 +0200 Subject: Merging upstream version 1.70.0+dfsg1. Signed-off-by: Daniel Baumann --- .../src/annotate_snippet_emitter_writer.rs | 5 +- compiler/rustc_errors/src/diagnostic.rs | 3 +- compiler/rustc_errors/src/diagnostic_impls.rs | 26 ++++- compiler/rustc_errors/src/emitter.rs | 123 +++++++++++++++------ compiler/rustc_errors/src/lib.rs | 9 +- compiler/rustc_errors/src/lock.rs | 81 ++++++-------- compiler/rustc_errors/src/snippet.rs | 65 ++++++++--- 7 files changed, 213 insertions(+), 99 deletions(-) (limited to 'compiler/rustc_errors/src') diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index d8879bf70..9872b3bda 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -202,7 +202,10 @@ impl AnnotateSnippetEmitterWriter { annotations: annotations .iter() .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(), annotation_type: annotation_type_for_level(*level), }) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 9ed8ab674..29c692128 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -880,6 +880,7 @@ impl Diagnostic { /// /// This is intended to be used for suggestions that are *very* obvious in what the changes /// need to be from the message, but we still want other tools to be able to apply them. + #[rustc_lint_diagnostics] pub fn tool_only_span_suggestion( &mut self, sp: Span, @@ -956,7 +957,7 @@ impl Diagnostic { // Exact iteration order of diagnostic arguments shouldn't make a difference to output because // they're only used in interpolation. #[allow(rustc::potential_query_instability)] - pub fn args<'a>(&'a self) -> impl Iterator> { + pub fn args(&self) -> impl Iterator> { self.args.iter() } diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index e82bad67b..65f8a61a3 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -1,4 +1,4 @@ -use crate::fluent_generated as fluent; +use crate::{fluent_generated as fluent, AddToDiagnostic}; use crate::{DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg}; use rustc_ast as ast; use rustc_ast_pretty::pprust; @@ -6,6 +6,7 @@ use rustc_hir as hir; use rustc_lint_defs::Level; use rustc_span::edition::Edition; use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol}; +use rustc_span::Span; use rustc_target::abi::TargetDataLayoutErrors; use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple}; use rustc_type_ir as type_ir; @@ -276,3 +277,26 @@ impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> { } } } + +/// Utility struct used to apply a single label while highlighting multiple spans +pub struct SingleLabelManySpans { + pub spans: Vec, + pub label: &'static str, + pub kind: LabelKind, +} +impl AddToDiagnostic for SingleLabelManySpans { + fn add_to_diagnostic_with(self, diag: &mut crate::Diagnostic, _: F) { + match self.kind { + LabelKind::Note => diag.span_note(self.spans, self.label), + LabelKind::Label => diag.span_labels(self.spans, self.label), + LabelKind::Help => diag.span_help(self.spans, self.label), + }; + } +} + +/// The kind of label to attach when using [`SingleLabelManySpans`] +pub enum LabelKind { + Note, + Label, + Help, +} diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 211bbf4f5..fe44799ef 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -12,7 +12,9 @@ use Destination::*; use rustc_span::source_map::SourceMap; 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::translation::{to_fluent_args, Translate}; use crate::{ @@ -858,7 +860,7 @@ impl EmitterWriter { let mut short_start = true; for ann in &line.annotations { 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 { Style::UnderlinePrimary } else { @@ -1093,15 +1095,15 @@ impl EmitterWriter { '_', line_offset + pos, width_offset + depth, - (code_offset + annotation.start_col).saturating_sub(left), + (code_offset + annotation.start_col.display).saturating_sub(left), style, ); } _ if self.teach => { buffer.set_style_range( line_offset, - (code_offset + annotation.start_col).saturating_sub(left), - (code_offset + annotation.end_col).saturating_sub(left), + (code_offset + annotation.start_col.display).saturating_sub(left), + (code_offset + annotation.end_col.display).saturating_sub(left), style, annotation.is_primary, ); @@ -1133,7 +1135,7 @@ impl EmitterWriter { for p in line_offset + 1..=line_offset + pos { buffer.putc( p, - (code_offset + annotation.start_col).saturating_sub(left), + (code_offset + annotation.start_col.display).saturating_sub(left), '|', style, ); @@ -1169,9 +1171,9 @@ impl EmitterWriter { let style = if annotation.is_primary { Style::LabelPrimary } else { Style::LabelSecondary }; 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 { - (pos + 2, annotation.start_col.saturating_sub(left)) + (pos + 2, annotation.start_col.display.saturating_sub(left)) }; if let Some(ref label) = annotation.label { buffer.puts(line_offset + pos, code_offset + col, label, style); @@ -1208,7 +1210,7 @@ impl EmitterWriter { } else { ('-', Style::UnderlineSecondary) }; - for p in annotation.start_col..annotation.end_col { + for p in annotation.start_col.display..annotation.end_col.display { buffer.putc( line_offset + 1, (code_offset + p).saturating_sub(left), @@ -1405,7 +1407,7 @@ impl EmitterWriter { // Account for newlines to align output to its label. for (line, text) in normalize_whitespace(&text).lines().enumerate() { buffer.append( - 0 + line, + line, &format!( "{}{}", if line == 0 { String::new() } else { " ".repeat(label_width) }, @@ -1459,7 +1461,7 @@ impl EmitterWriter { &annotated_file.file.name, line.line_index ), - annotations[0].start_col + 1, + annotations[0].start_col.file + 1, ), Style::LineAndColumn, ); @@ -1546,7 +1548,7 @@ impl EmitterWriter { buffer.prepend(buffer_msg_line_offset + 1, "::: ", Style::LineNumber); let loc = if let Some(first_line) = annotated_file.lines.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 { String::new() }; @@ -1607,8 +1609,8 @@ impl EmitterWriter { let mut span_left_margin = usize::MAX; for line in &annotated_file.lines { for ann in &line.annotations { - span_left_margin = min(span_left_margin, ann.start_col); - span_left_margin = min(span_left_margin, ann.end_col); + span_left_margin = min(span_left_margin, ann.start_col.display); + span_left_margin = min(span_left_margin, ann.end_col.display); } } 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()), ); for ann in &line.annotations { - span_right_margin = max(span_right_margin, ann.start_col); - span_right_margin = max(span_right_margin, ann.end_col); + span_right_margin = max(span_right_margin, ann.start_col.display); + span_right_margin = max(span_right_margin, ann.end_col.display); // FIXME: account for labels not in the same line 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); } } @@ -1829,6 +1832,12 @@ impl EmitterWriter { } let show_code_change = if has_deletion && !is_multiline { DisplaySuggestion::Diff + } else if let [part] = &parts[..] + && part.snippet.ends_with('\n') + && part.snippet.trim() == complete.trim() + { + // We are adding a line(s) of code before code that was already there. + DisplaySuggestion::Add } else if (parts.len() != 1 || parts[0].snippet.trim() != complete.trim()) && !is_multiline { @@ -1876,7 +1885,10 @@ impl EmitterWriter { row_num += line_end - line_start; } let mut unhighlighted_lines = Vec::new(); + let mut last_pos = 0; + let mut is_item_attribute = false; for (line_pos, (line, highlight_parts)) in lines.by_ref().zip(highlights).enumerate() { + last_pos = line_pos; debug!(%line_pos, %line, ?highlight_parts); // Remember lines that are not highlighted to hide them if needed @@ -1884,6 +1896,12 @@ impl EmitterWriter { unhighlighted_lines.push((line_pos, line)); continue; } + if highlight_parts.len() == 1 + && line.trim().starts_with("#[") + && line.trim().ends_with(']') + { + is_item_attribute = true; + } match unhighlighted_lines.len() { 0 => (), @@ -1895,7 +1913,7 @@ impl EmitterWriter { self.draw_code_line( &mut buffer, &mut row_num, - &Vec::new(), + &[], p + line_start, l, show_code_change, @@ -1915,11 +1933,11 @@ impl EmitterWriter { let last_line = unhighlighted_lines.pop(); let first_line = unhighlighted_lines.drain(..).next(); - first_line.map(|(p, l)| { + if let Some((p, l)) = first_line { self.draw_code_line( &mut buffer, &mut row_num, - &Vec::new(), + &[], p + line_start, l, show_code_change, @@ -1927,16 +1945,16 @@ impl EmitterWriter { &file_lines, is_multiline, ) - }); + } buffer.puts(row_num, max_line_num_len - 1, "...", Style::LineNumber); row_num += 1; - last_line.map(|(p, l)| { + if let Some((p, l)) = last_line { self.draw_code_line( &mut buffer, &mut row_num, - &Vec::new(), + &[], p + line_start, l, show_code_change, @@ -1944,14 +1962,14 @@ impl EmitterWriter { &file_lines, is_multiline, ) - }); + } } } self.draw_code_line( &mut buffer, &mut row_num, - highlight_parts, + &highlight_parts, line_pos + line_start, line, show_code_change, @@ -1960,13 +1978,41 @@ impl EmitterWriter { is_multiline, ) } + if let DisplaySuggestion::Add = show_code_change && is_item_attribute { + // The suggestion adds an entire line of code, ending on a newline, so we'll also + // print the *following* line, to provide context of what we're advicing people to + // do. Otherwise you would only see contextless code that can be confused for + // already existing code, despite the colors and UI elements. + // We special case `#[derive(_)]\n` and other attribute suggestions, because those + // are the ones where context is most useful. + let file_lines = sm + .span_to_lines(span.primary_span().unwrap().shrink_to_hi()) + .expect("span_to_lines failed when emitting suggestion"); + let line_num = sm.lookup_char_pos(parts[0].span.lo()).line; + if let Some(line) = file_lines.file.get_line(line_num - 1) { + let line = normalize_whitespace(&line); + self.draw_code_line( + &mut buffer, + &mut row_num, + &[], + line_num + last_pos + 1, + &line, + DisplaySuggestion::None, + max_line_num_len, + &file_lines, + is_multiline, + ) + } + } // This offset and the ones below need to be signed to account for replacement code // that is shorter than the original code. let mut offsets: Vec<(usize, isize)> = Vec::new(); // Only show an underline in the suggestions if the suggestion is not the // entirety of the code being shown and the displayed code is not multiline. - if let DisplaySuggestion::Diff | DisplaySuggestion::Underline = show_code_change { + if let DisplaySuggestion::Diff | DisplaySuggestion::Underline | DisplaySuggestion::Add = + show_code_change + { draw_col_separator_no_space(&mut buffer, row_num, max_line_num_len + 1); for part in parts { let span_start_pos = sm.lookup_char_pos(part.span.lo()).col_display; @@ -2176,7 +2222,7 @@ impl EmitterWriter { &self, buffer: &mut StyledBuffer, row_num: &mut usize, - highlight_parts: &Vec, + highlight_parts: &[SubstitutionHighlight], line_num: usize, line_to_add: &str, show_code_change: DisplaySuggestion, @@ -2232,7 +2278,7 @@ impl EmitterWriter { } } else if is_multiline { buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); - match &highlight_parts[..] { + match &highlight_parts { [SubstitutionHighlight { start: 0, end }] if *end == line_to_add.len() => { buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); } @@ -2244,6 +2290,10 @@ impl EmitterWriter { } } buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); + } else if let DisplaySuggestion::Add = show_code_change { + buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); + buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } else { buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); draw_col_separator(buffer, *row_num, max_line_num_len + 1); @@ -2278,6 +2328,7 @@ enum DisplaySuggestion { Underline, Diff, None, + Add, } impl FileWithAnnotatedLines { @@ -2352,8 +2403,8 @@ impl FileWithAnnotatedLines { depth: 1, line_start: lo.line, line_end: hi.line, - start_col: lo.col_display, - end_col: hi.col_display, + start_col: AnnotationColumn::from_loc(&lo), + end_col: AnnotationColumn::from_loc(&hi), is_primary, label, overlaps_exactly: false, @@ -2361,8 +2412,8 @@ impl FileWithAnnotatedLines { multiline_annotations.push((lo.file, ml)); } else { let ann = Annotation { - start_col: lo.col_display, - end_col: hi.col_display, + start_col: AnnotationColumn::from_loc(&lo), + end_col: AnnotationColumn::from_loc(&hi), is_primary, label, annotation_type: AnnotationType::Singleline, @@ -2551,7 +2602,13 @@ fn num_overlap( (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 { - 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( diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 09bf28ed4..d20b16890 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -6,7 +6,6 @@ #![feature(array_windows)] #![feature(drain_filter)] #![feature(if_let_guard)] -#![feature(is_terminal)] #![feature(adt_const_params)] #![feature(let_chains)] #![feature(never_type)] @@ -76,7 +75,7 @@ pub use snippet::Style; pub type PErr<'a> = DiagnosticBuilder<'a, ErrorGuaranteed>; pub type PResult<'a, T> = Result>; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. // (See also the comment on `DiagnosticBuilderInner`'s `diagnostic` field.) @@ -331,7 +330,7 @@ impl CodeSuggestion { }); buf.push_str(&part.snippet); let cur_hi = sm.lookup_char_pos(part.span.hi()); - if prev_hi.line == cur_lo.line && cur_hi.line == cur_lo.line { + if cur_hi.line == cur_lo.line && !part.snippet.is_empty() { // Account for the difference between the width of the current code and the // snippet being suggested, so that the *later* suggestions are correctly // aligned on the screen. @@ -383,7 +382,9 @@ pub use diagnostic::{ DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, }; pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted}; -pub use diagnostic_impls::{DiagnosticArgFromDisplay, DiagnosticSymbolList}; +pub use diagnostic_impls::{ + DiagnosticArgFromDisplay, DiagnosticSymbolList, LabelKind, SingleLabelManySpans, +}; use std::backtrace::Backtrace; /// A handler deals with errors and other compiler output. diff --git a/compiler/rustc_errors/src/lock.rs b/compiler/rustc_errors/src/lock.rs index a73472021..7db262abf 100644 --- a/compiler/rustc_errors/src/lock.rs +++ b/compiler/rustc_errors/src/lock.rs @@ -16,10 +16,12 @@ pub fn acquire_global_lock(name: &str) -> Box { use std::ffi::CString; use std::io; - use winapi::shared::ntdef::HANDLE; - use winapi::um::handleapi::CloseHandle; - use winapi::um::synchapi::{CreateMutexA, ReleaseMutex, WaitForSingleObject}; - use winapi::um::winbase::{INFINITE, WAIT_ABANDONED, WAIT_OBJECT_0}; + use windows::{ + core::PCSTR, + Win32::Foundation::{CloseHandle, HANDLE, WAIT_ABANDONED, WAIT_OBJECT_0}, + Win32::System::Threading::{CreateMutexA, ReleaseMutex, WaitForSingleObject}, + Win32::System::WindowsProgramming::INFINITE, + }; struct Handle(HANDLE); @@ -42,49 +44,38 @@ pub fn acquire_global_lock(name: &str) -> Box { } let cname = CString::new(name).unwrap(); - unsafe { - // Create a named mutex, with no security attributes and also not - // acquired when we create it. - // - // This will silently create one if it doesn't already exist, or it'll - // open up a handle to one if it already exists. - let mutex = CreateMutexA(std::ptr::null_mut(), 0, cname.as_ptr()); - if mutex.is_null() { - panic!( - "failed to create global mutex named `{}`: {}", - name, - io::Error::last_os_error() - ); - } - let mutex = Handle(mutex); + // Create a named mutex, with no security attributes and also not + // acquired when we create it. + // + // This will silently create one if it doesn't already exist, or it'll + // open up a handle to one if it already exists. + let mutex = unsafe { CreateMutexA(None, false, PCSTR::from_raw(cname.as_ptr().cast())) } + .unwrap_or_else(|_| panic!("failed to create global mutex named `{}`", name)); + let mutex = Handle(mutex); - // Acquire the lock through `WaitForSingleObject`. - // - // A return value of `WAIT_OBJECT_0` means we successfully acquired it. - // - // A return value of `WAIT_ABANDONED` means that the previous holder of - // the thread exited without calling `ReleaseMutex`. This can happen, - // for example, when the compiler crashes or is interrupted via ctrl-c - // or the like. In this case, however, we are still transferred - // ownership of the lock so we continue. - // - // If an error happens.. well... that's surprising! - match WaitForSingleObject(mutex.0, INFINITE) { - WAIT_OBJECT_0 | WAIT_ABANDONED => {} - code => { - panic!( - "WaitForSingleObject failed on global mutex named \ - `{}`: {} (ret={:x})", - name, - io::Error::last_os_error(), - code - ); - } - } - - // Return a guard which will call `ReleaseMutex` when dropped. - Box::new(Guard(mutex)) + // Acquire the lock through `WaitForSingleObject`. + // + // A return value of `WAIT_OBJECT_0` means we successfully acquired it. + // + // A return value of `WAIT_ABANDONED` means that the previous holder of + // the thread exited without calling `ReleaseMutex`. This can happen, + // for example, when the compiler crashes or is interrupted via ctrl-c + // or the like. In this case, however, we are still transferred + // ownership of the lock so we continue. + // + // If an error happens.. well... that's surprising! + match unsafe { WaitForSingleObject(mutex.0, INFINITE) } { + WAIT_OBJECT_0 | WAIT_ABANDONED => (), + err => panic!( + "WaitForSingleObject failed on global mutex named `{}`: {} (ret={:x})", + name, + io::Error::last_os_error(), + err.0 + ), } + + // Return a guard which will call `ReleaseMutex` when dropped. + Box::new(Guard(mutex)) } #[cfg(not(windows))] diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs index e4cc44c41..98eb70b5f 100644 --- a/compiler/rustc_errors/src/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -1,6 +1,6 @@ // Code for annotating snippets. -use crate::Level; +use crate::{Level, Loc}; #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] pub struct Line { @@ -8,13 +8,39 @@ pub struct Line { pub annotations: Vec, } +#[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 ) + 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)] pub struct MultilineAnnotation { pub depth: usize, pub line_start: usize, pub line_end: usize, - pub start_col: usize, - pub end_col: usize, + pub start_col: AnnotationColumn, + pub end_col: AnnotationColumn, pub is_primary: bool, pub label: Option, pub overlaps_exactly: bool, @@ -36,7 +62,12 @@ impl MultilineAnnotation { pub fn as_start(&self) -> Annotation { Annotation { 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, label: None, annotation_type: AnnotationType::MultilineStart(self.depth), @@ -45,7 +76,12 @@ impl MultilineAnnotation { pub fn as_end(&self) -> 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, is_primary: self.is_primary, label: self.label.clone(), @@ -55,8 +91,8 @@ impl MultilineAnnotation { pub fn as_line(&self) -> Annotation { Annotation { - start_col: 0, - end_col: 0, + start_col: Default::default(), + end_col: Default::default(), is_primary: self.is_primary, label: None, annotation_type: AnnotationType::MultilineLine(self.depth), @@ -92,14 +128,14 @@ pub enum AnnotationType { #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] pub struct Annotation { - /// Start column, 0-based indexing -- counting *characters*, not - /// utf-8 bytes. Note that it is important that this field goes + /// Start column. + /// Note that it is important that this field goes /// first, so that when we sort, we sort orderings by start /// column. - pub start_col: usize, + pub start_col: AnnotationColumn, /// End column within the line (exclusive) - pub end_col: usize, + pub end_col: AnnotationColumn, /// Is this annotation derived from primary span pub is_primary: bool, @@ -118,12 +154,13 @@ impl Annotation { matches!(self.annotation_type, AnnotationType::MultilineLine(_)) } + /// Length of this annotation as displayed in the stderr output pub fn len(&self) -> usize { // Account for usize underflows - if self.end_col > self.start_col { - self.end_col - self.start_col + if self.end_col.display > self.start_col.display { + self.end_col.display - self.start_col.display } else { - self.start_col - self.end_col + self.start_col.display - self.end_col.display } } -- cgit v1.2.3