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::translation
::{to_fluent_args, Translate}
;
12 CodeSuggestion
, Diagnostic
, DiagnosticId
, DiagnosticMessage
, Emitter
, FluentBundle
,
13 LazyFallbackBundle
, Level
, MultiSpan
, Style
, SubDiagnostic
,
15 use annotate_snippets
::display_list
::{DisplayList, FormatOptions}
;
16 use annotate_snippets
::snippet
::*;
17 use rustc_data_structures
::sync
::Lrc
;
18 use rustc_error_messages
::FluentArgs
;
19 use rustc_span
::source_map
::SourceMap
;
20 use rustc_span
::SourceFile
;
22 /// Generates diagnostics using annotate-snippet
23 pub struct AnnotateSnippetEmitterWriter
{
24 source_map
: Option
<Lrc
<SourceMap
>>,
25 fluent_bundle
: Option
<Lrc
<FluentBundle
>>,
26 fallback_bundle
: LazyFallbackBundle
,
28 /// If true, hides the longer explanation text
30 /// If true, will normalize line numbers with `LL` to prevent noise in UI test diffs.
33 macro_backtrace
: bool
,
36 impl Translate
for AnnotateSnippetEmitterWriter
{
37 fn fluent_bundle(&self) -> Option
<&Lrc
<FluentBundle
>> {
38 self.fluent_bundle
.as_ref()
41 fn fallback_fluent_bundle(&self) -> &FluentBundle
{
46 impl Emitter
for AnnotateSnippetEmitterWriter
{
47 /// The entry point for the diagnostics generation
48 fn emit_diagnostic(&mut self, diag
: &Diagnostic
) {
49 let fluent_args
= to_fluent_args(diag
.args());
51 let mut children
= diag
.children
.clone();
52 let (mut primary_span
, suggestions
) = self.primary_span_formatted(diag
, &fluent_args
);
54 self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
61 self.emit_messages_default(
72 fn source_map(&self) -> Option
<&Lrc
<SourceMap
>> {
73 self.source_map
.as_ref()
76 fn should_show_explain(&self) -> bool
{
81 /// Provides the source string for the given `line` of `file`
82 fn source_string(file
: Lrc
<SourceFile
>, line
: &Line
) -> String
{
83 file
.get_line(line
.line_index
- 1).map(|a
| a
.to_string()).unwrap_or_default()
86 /// Maps `Diagnostic::Level` to `snippet::AnnotationType`
87 fn annotation_type_for_level(level
: Level
) -> AnnotationType
{
89 Level
::Bug
| Level
::DelayedBug
| Level
::Fatal
| Level
::Error { .. }
=> {
92 Level
::Warning(_
) => AnnotationType
::Warning
,
93 Level
::Note
| Level
::OnceNote
=> AnnotationType
::Note
,
94 Level
::Help
=> AnnotationType
::Help
,
95 // FIXME(#59346): Not sure how to map this level
96 Level
::FailureNote
=> AnnotationType
::Error
,
97 Level
::Allow
=> panic
!("Should not call with Allow"),
98 Level
::Expect(_
) => panic
!("Should not call with Expect"),
102 impl AnnotateSnippetEmitterWriter
{
104 source_map
: Option
<Lrc
<SourceMap
>>,
105 fluent_bundle
: Option
<Lrc
<FluentBundle
>>,
106 fallback_bundle
: LazyFallbackBundle
,
108 macro_backtrace
: bool
,
120 /// Allows to modify `Self` to enable or disable the `ui_testing` flag.
122 /// If this is set to true, line numbers will be normalized as `LL` in the output.
123 pub fn ui_testing(mut self, ui_testing
: bool
) -> Self {
124 self.ui_testing
= ui_testing
;
128 fn emit_messages_default(
131 messages
: &[(DiagnosticMessage
, Style
)],
132 args
: &FluentArgs
<'_
>,
133 code
: &Option
<DiagnosticId
>,
135 _children
: &[SubDiagnostic
],
136 _suggestions
: &[CodeSuggestion
],
138 let message
= self.translate_messages(messages
, args
);
139 if let Some(source_map
) = &self.source_map
{
140 // Make sure our primary file comes first
141 let primary_lo
= if let Some(ref primary_span
) = msp
.primary_span().as_ref() {
142 if primary_span
.is_dummy() {
143 // FIXME(#59346): Not sure when this is the case and what
144 // should be done if it happens
147 source_map
.lookup_char_pos(primary_span
.lo())
150 // FIXME(#59346): Not sure when this is the case and what
151 // should be done if it happens
154 let mut annotated_files
= FileWithAnnotatedLines
::collect_annotations(self, args
, msp
);
156 annotated_files
.binary_search_by(|x
| x
.file
.name
.cmp(&primary_lo
.file
.name
))
158 annotated_files
.swap(0, pos
);
160 // owned: line source, line index, annotations
161 type Owned
= (String
, usize, Vec
<crate::snippet
::Annotation
>);
162 let filename
= source_map
.filename_for_diagnostics(&primary_lo
.file
.name
);
163 let origin
= filename
.to_string_lossy();
164 let annotated_files
: Vec
<Owned
> = annotated_files
166 .flat_map(|annotated_file
| {
167 let file
= annotated_file
.file
;
172 (source_string(file
.clone(), &line
), line
.line_index
, line
.annotations
)
174 .collect
::<Vec
<Owned
>>()
177 let snippet
= Snippet
{
178 title
: Some(Annotation
{
179 label
: Some(&message
),
180 id
: code
.as_ref().map(|c
| match c
{
181 DiagnosticId
::Error(val
) | DiagnosticId
::Lint { name: val, .. }
=> {
185 annotation_type
: annotation_type_for_level(*level
),
190 anonymized_line_numbers
: self.ui_testing
,
193 slices
: annotated_files
195 .map(|(source
, line_index
, annotations
)| {
198 line_start
: *line_index
,
199 origin
: Some(&origin
),
200 // FIXME(#59346): Not really sure when `fold` should be true or false
202 annotations
: annotations
204 .map(|annotation
| SourceAnnotation
{
206 annotation
.start_col
.display
,
207 annotation
.end_col
.display
,
209 label
: annotation
.label
.as_deref().unwrap_or_default(),
210 annotation_type
: annotation_type_for_level(*level
),
217 // FIXME(#59346): Figure out if we can _always_ print to stderr or not.
218 // `emitter.rs` has the `Destination` enum that lists various possible output
220 eprintln
!("{}", DisplayList
::from(snippet
))
222 // FIXME(#59346): Is it ok to return None if there's no source_map?