diff --git a/Cargo.lock b/Cargo.lock index 375e5672229..7e0448a152f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,6 +48,12 @@ dependencies = [ "ansi_term", ] +[[package]] +name = "annotate-snippets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5" + [[package]] name = "ansi_term" version = "0.11.0" @@ -3277,7 +3283,7 @@ version = "654.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "908e1ea187c6bb368af4ba6db980001e920515e67371ddc4086e749baabe6080" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.6.1", "atty", "log", "rustc-ap-rustc_data_structures", @@ -3761,7 +3767,7 @@ version = "0.0.0" name = "rustc_errors" version = "0.0.0" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.8.0", "atty", "log", "rustc_data_structures", @@ -4408,7 +4414,7 @@ dependencies = [ name = "rustfmt-nightly" version = "1.4.14" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.6.1", "bytecount", "cargo_metadata 0.8.0", "derive-new", diff --git a/src/librustc_errors/Cargo.toml b/src/librustc_errors/Cargo.toml index b8340b1a1df..5882a6855b6 100644 --- a/src/librustc_errors/Cargo.toml +++ b/src/librustc_errors/Cargo.toml @@ -17,7 +17,7 @@ rustc_data_structures = { path = "../librustc_data_structures" } unicode-width = "0.1.4" atty = "0.2" termcolor = "1.0" -annotate-snippets = "0.6.1" +annotate-snippets = "0.8.0" termize = "0.1.1" [target.'cfg(windows)'.dependencies] diff --git a/src/librustc_errors/annotate_snippet_emitter_writer.rs b/src/librustc_errors/annotate_snippet_emitter_writer.rs index d83175694f4..df5e43626aa 100644 --- a/src/librustc_errors/annotate_snippet_emitter_writer.rs +++ b/src/librustc_errors/annotate_snippet_emitter_writer.rs @@ -8,12 +8,11 @@ use crate::emitter::FileWithAnnotatedLines; use crate::snippet::Line; use crate::{CodeSuggestion, Diagnostic, DiagnosticId, Emitter, Level, SubDiagnostic}; -use annotate_snippets::display_list::DisplayList; -use annotate_snippets::formatter::DisplayListFormatter; +use annotate_snippets::display_list::{DisplayList, FormatOptions}; use annotate_snippets::snippet::*; use rustc_data_structures::sync::Lrc; use rustc_span::source_map::SourceMap; -use rustc_span::{Loc, MultiSpan, SourceFile}; +use rustc_span::{MultiSpan, SourceFile}; /// Generates diagnostics using annotate-snippet pub struct AnnotateSnippetEmitterWriter { @@ -59,112 +58,20 @@ impl Emitter for AnnotateSnippetEmitterWriter { } } -/// Collects all the data needed to generate the data structures needed for the -/// `annotate-snippets` library. -struct DiagnosticConverter<'a> { - source_map: Option>, - level: Level, - message: String, - code: Option, - msp: MultiSpan, - #[allow(dead_code)] - children: &'a [SubDiagnostic], - #[allow(dead_code)] - suggestions: &'a [CodeSuggestion], +/// Provides the source string for the given `line` of `file` +fn source_string(file: Lrc, line: &Line) -> String { + file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or_default() } -impl<'a> DiagnosticConverter<'a> { - /// Turns rustc Diagnostic information into a `annotate_snippets::snippet::Snippet`. - fn to_annotation_snippet(&self) -> Option { - if let Some(source_map) = &self.source_map { - // Make sure our primary file comes first - let primary_lo = if let Some(ref primary_span) = self.msp.primary_span().as_ref() { - source_map.lookup_char_pos(primary_span.lo()) - } else { - // FIXME(#59346): Not sure when this is the case and what - // should be done if it happens - return None; - }; - let annotated_files = - FileWithAnnotatedLines::collect_annotations(&self.msp, &self.source_map); - let slices = self.slices_for_files(annotated_files, primary_lo); - - Some(Snippet { - title: Some(Annotation { - label: Some(self.message.to_string()), - id: self.code.clone().map(|c| match c { - DiagnosticId::Error(val) | DiagnosticId::Lint(val) => val, - }), - annotation_type: Self::annotation_type_for_level(self.level), - }), - footer: vec![], - slices, - }) - } else { - // FIXME(#59346): Is it ok to return None if there's no source_map? - None - } - } - - fn slices_for_files( - &self, - annotated_files: Vec, - primary_lo: Loc, - ) -> Vec { - // FIXME(#64205): Provide a test case where `annotated_files` is > 1 - annotated_files - .iter() - .flat_map(|annotated_file| { - annotated_file - .lines - .iter() - .map(|line| { - let line_source = Self::source_string(annotated_file.file.clone(), &line); - Slice { - source: line_source, - line_start: line.line_index, - origin: Some(primary_lo.file.name.to_string()), - // FIXME(#59346): Not really sure when `fold` should be true or false - fold: false, - annotations: line - .annotations - .iter() - .map(|a| self.annotation_to_source_annotation(a.clone())) - .collect(), - } - }) - .collect::>() - }) - .collect::>() - } - - /// Turns a `crate::snippet::Annotation` into a `SourceAnnotation` - fn annotation_to_source_annotation( - &self, - annotation: crate::snippet::Annotation, - ) -> SourceAnnotation { - SourceAnnotation { - range: (annotation.start_col, annotation.end_col), - label: annotation.label.unwrap_or("".to_string()), - annotation_type: Self::annotation_type_for_level(self.level), - } - } - - /// Provides the source string for the given `line` of `file` - fn source_string(file: Lrc, line: &Line) -> String { - file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or(String::new()) - } - - /// Maps `Diagnostic::Level` to `snippet::AnnotationType` - fn annotation_type_for_level(level: Level) -> AnnotationType { - match level { - Level::Bug | Level::Fatal | Level::Error => AnnotationType::Error, - Level::Warning => AnnotationType::Warning, - Level::Note => AnnotationType::Note, - Level::Help => AnnotationType::Help, - // FIXME(#59346): Not sure how to map these two levels - Level::Cancelled | Level::FailureNote => AnnotationType::Error, - } +/// Maps `Diagnostic::Level` to `snippet::AnnotationType` +fn annotation_type_for_level(level: Level) -> AnnotationType { + match level { + Level::Bug | Level::Fatal | Level::Error => AnnotationType::Error, + Level::Warning => AnnotationType::Warning, + Level::Note => AnnotationType::Note, + Level::Help => AnnotationType::Help, + // FIXME(#59346): Not sure how to map these two levels + Level::Cancelled | Level::FailureNote => AnnotationType::Error, } } @@ -191,25 +98,76 @@ impl AnnotateSnippetEmitterWriter { message: String, code: &Option, msp: &MultiSpan, - children: &[SubDiagnostic], - suggestions: &[CodeSuggestion], + _children: &[SubDiagnostic], + _suggestions: &[CodeSuggestion], ) { - let converter = DiagnosticConverter { - source_map: self.source_map.clone(), - level: *level, - message, - code: code.clone(), - msp: msp.clone(), - children, - suggestions, - }; - if let Some(snippet) = converter.to_annotation_snippet() { - let dl = DisplayList::from(snippet); - let dlf = DisplayListFormatter::new(true, self.ui_testing); + if let Some(source_map) = &self.source_map { + // Make sure our primary file comes first + let primary_lo = if let Some(ref primary_span) = msp.primary_span().as_ref() { + source_map.lookup_char_pos(primary_span.lo()) + } else { + // FIXME(#59346): Not sure when this is the case and what + // should be done if it happens + return; + }; + let annotated_files = + FileWithAnnotatedLines::collect_annotations(msp, &self.source_map); + // owned: line source, line index, annotations + type Owned = (String, usize, Vec); + let origin = primary_lo.file.name.to_string(); + let annotated_files: Vec = annotated_files + .into_iter() + .flat_map(|annotated_file| { + let file = annotated_file.file; + annotated_file + .lines + .into_iter() + .map(|line| { + (source_string(file.clone(), &line), line.line_index, line.annotations) + }) + .collect::>() + }) + .collect(); + let snippet = Snippet { + title: Some(Annotation { + label: Some(&message), + id: code.as_ref().map(|c| match c { + DiagnosticId::Error(val) | DiagnosticId::Lint(val) => val.as_str(), + }), + annotation_type: annotation_type_for_level(*level), + }), + footer: vec![], + opt: FormatOptions { color: true, anonymized_line_numbers: self.ui_testing }, + slices: annotated_files + .iter() + .map(|(source, line_index, annotations)| { + Slice { + source, + line_start: *line_index, + origin: Some(&origin), + // FIXME(#59346): Not really sure when `fold` should be true or false + fold: false, + annotations: annotations + .into_iter() + .map(|annotation| SourceAnnotation { + range: (annotation.start_col, annotation.end_col), + label: annotation + .label + .as_ref() + .map(|s| s.as_str()) + .unwrap_or_default(), + annotation_type: annotation_type_for_level(*level), + }) + .collect(), + } + }) + .collect(), + }; // FIXME(#59346): Figure out if we can _always_ print to stderr or not. // `emitter.rs` has the `Destination` enum that lists various possible output // destinations. - eprintln!("{}", dlf.format(&dl)); - }; + eprintln!("{}", DisplayList::from(snippet)) + } + // FIXME(#59346): Is it ok to return None if there's no source_map? } } diff --git a/src/test/ui/annotate-snippet/missing-type.stderr b/src/test/ui/annotate-snippet/missing-type.stderr index 806acf0bed5..c16f022a77f 100644 --- a/src/test/ui/annotate-snippet/missing-type.stderr +++ b/src/test/ui/annotate-snippet/missing-type.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `Iter` in this scope - --> $DIR/missing-type.rs:4:11 + --> $DIR/missing-type.rs:4:12 | LL | let x: Iter; | ^^^^ not found in this scope