1 //! Emit diagnostics using the `annotate-snippets` library
3 //! This is the equivalent of `./emitter.rs` but making use of the
4 //! [`annotate-snippets`][annotate_snippets] library instead of building the output ourselves.
6 //! [annotate_snippets]: https://docs.rs/crate/annotate-snippets/
8 use crate::emitter
::FileWithAnnotatedLines
;
9 use crate::snippet
::Line
;
10 use crate::{CodeSuggestion, Diagnostic, DiagnosticId, Emitter, Level, SubDiagnostic}
;
11 use annotate_snippets
::display_list
::{DisplayList, FormatOptions}
;
12 use annotate_snippets
::snippet
::*;
13 use rustc_data_structures
::sync
::Lrc
;
14 use rustc_span
::source_map
::SourceMap
;
15 use rustc_span
::{MultiSpan, SourceFile}
;
17 /// Generates diagnostics using annotate-snippet
18 pub struct AnnotateSnippetEmitterWriter
{
19 source_map
: Option
<Lrc
<SourceMap
>>,
20 /// If true, hides the longer explanation text
22 /// If true, will normalize line numbers with `LL` to prevent noise in UI test diffs.
25 macro_backtrace
: bool
,
28 impl Emitter
for AnnotateSnippetEmitterWriter
{
29 /// The entry point for the diagnostics generation
30 fn emit_diagnostic(&mut self, diag
: &Diagnostic
) {
31 let mut children
= diag
.children
.clone();
32 let (mut primary_span
, suggestions
) = self.primary_span_formatted(&diag
);
34 self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
42 self.emit_messages_default(
52 fn source_map(&self) -> Option
<&Lrc
<SourceMap
>> {
53 self.source_map
.as_ref()
56 fn should_show_explain(&self) -> bool
{
61 /// Provides the source string for the given `line` of `file`
62 fn source_string(file
: Lrc
<SourceFile
>, line
: &Line
) -> String
{
63 file
.get_line(line
.line_index
- 1).map(|a
| a
.to_string()).unwrap_or_default()
66 /// Maps `Diagnostic::Level` to `snippet::AnnotationType`
67 fn annotation_type_for_level(level
: Level
) -> AnnotationType
{
69 Level
::Bug
| Level
::Fatal
| Level
::Error
=> AnnotationType
::Error
,
70 Level
::Warning
=> AnnotationType
::Warning
,
71 Level
::Note
=> AnnotationType
::Note
,
72 Level
::Help
=> AnnotationType
::Help
,
73 // FIXME(#59346): Not sure how to map these two levels
74 Level
::Cancelled
| Level
::FailureNote
=> AnnotationType
::Error
,
78 impl AnnotateSnippetEmitterWriter
{
80 source_map
: Option
<Lrc
<SourceMap
>>,
82 macro_backtrace
: bool
,
84 Self { source_map, short_message, ui_testing: false, macro_backtrace }
87 /// Allows to modify `Self` to enable or disable the `ui_testing` flag.
89 /// If this is set to true, line numbers will be normalized as `LL` in the output.
90 pub fn ui_testing(mut self, ui_testing
: bool
) -> Self {
91 self.ui_testing
= ui_testing
;
95 fn emit_messages_default(
99 code
: &Option
<DiagnosticId
>,
101 _children
: &[SubDiagnostic
],
102 _suggestions
: &[CodeSuggestion
],
104 if let Some(source_map
) = &self.source_map
{
105 // Make sure our primary file comes first
106 let primary_lo
= if let Some(ref primary_span
) = msp
.primary_span().as_ref() {
107 if primary_span
.is_dummy() {
108 // FIXME(#59346): Not sure when this is the case and what
109 // should be done if it happens
112 source_map
.lookup_char_pos(primary_span
.lo())
115 // FIXME(#59346): Not sure when this is the case and what
116 // should be done if it happens
119 let mut annotated_files
=
120 FileWithAnnotatedLines
::collect_annotations(msp
, &self.source_map
);
122 annotated_files
.binary_search_by(|x
| x
.file
.name
.cmp(&primary_lo
.file
.name
))
124 annotated_files
.swap(0, pos
);
126 // owned: line source, line index, annotations
127 type Owned
= (String
, usize, Vec
<crate::snippet
::Annotation
>);
128 let origin
= primary_lo
.file
.name
.to_string();
129 let annotated_files
: Vec
<Owned
> = annotated_files
131 .flat_map(|annotated_file
| {
132 let file
= annotated_file
.file
;
137 (source_string(file
.clone(), &line
), line
.line_index
, line
.annotations
)
139 .collect
::<Vec
<Owned
>>()
142 let snippet
= Snippet
{
143 title
: Some(Annotation
{
144 label
: Some(&message
),
145 id
: code
.as_ref().map(|c
| match c
{
146 DiagnosticId
::Error(val
) | DiagnosticId
::Lint(val
) => val
.as_str(),
148 annotation_type
: annotation_type_for_level(*level
),
151 opt
: FormatOptions { color: true, anonymized_line_numbers: self.ui_testing }
,
152 slices
: annotated_files
154 .map(|(source
, line_index
, annotations
)| {
157 line_start
: *line_index
,
158 origin
: Some(&origin
),
159 // FIXME(#59346): Not really sure when `fold` should be true or false
161 annotations
: annotations
163 .map(|annotation
| SourceAnnotation
{
164 range
: (annotation
.start_col
, annotation
.end_col
),
165 label
: annotation
.label
.as_deref().unwrap_or_default(),
166 annotation_type
: annotation_type_for_level(*level
),
173 // FIXME(#59346): Figure out if we can _always_ print to stderr or not.
174 // `emitter.rs` has the `Destination` enum that lists various possible output
176 eprintln
!("{}", DisplayList
::from(snippet
))
178 // FIXME(#59346): Is it ok to return None if there's no source_map?