2019-06-05 21:13:56 +02:00
|
|
|
//! Emit diagnostics using the `annotate-snippets` library
|
|
|
|
//!
|
|
|
|
//! This is the equivalent of `./emitter.rs` but making use of the
|
|
|
|
//! [`annotate-snippets`][annotate_snippets] library instead of building the output ourselves.
|
|
|
|
//!
|
|
|
|
//! [annotate_snippets]: https://docs.rs/crate/annotate-snippets/
|
2019-05-31 22:01:27 +02:00
|
|
|
|
2023-12-30 18:11:41 +03:00
|
|
|
use annotate_snippets::{Annotation, AnnotationType, Renderer, Slice, Snippet, SourceAnnotation};
|
2019-05-31 22:01:27 +02:00
|
|
|
use rustc_data_structures::sync::Lrc;
|
2022-03-26 07:27:43 +00:00
|
|
|
use rustc_error_messages::FluentArgs;
|
2019-12-31 20:15:40 +03:00
|
|
|
use rustc_span::source_map::SourceMap;
|
2022-03-24 02:03:04 +00:00
|
|
|
use rustc_span::SourceFile;
|
2024-07-29 08:13:50 +10:00
|
|
|
|
2019-05-31 22:01:27 +02:00
|
|
|
use crate::emitter::FileWithAnnotatedLines;
|
|
|
|
use crate::snippet::Line;
|
2022-10-03 14:02:49 +01:00
|
|
|
use crate::translation::{to_fluent_args, Translate};
|
2022-03-26 07:27:43 +00:00
|
|
|
use crate::{
|
2024-02-29 11:58:51 +11:00
|
|
|
CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, FluentBundle, LazyFallbackBundle,
|
|
|
|
Level, MultiSpan, Style, Subdiag,
|
2022-03-26 07:27:43 +00:00
|
|
|
};
|
2019-05-31 22:01:27 +02:00
|
|
|
|
2019-06-05 21:13:56 +02:00
|
|
|
/// Generates diagnostics using annotate-snippet
|
2024-01-05 10:37:44 +11:00
|
|
|
pub struct AnnotateSnippetEmitter {
|
2019-11-15 08:32:31 -05:00
|
|
|
source_map: Option<Lrc<SourceMap>>,
|
2022-03-28 09:36:20 +01:00
|
|
|
fluent_bundle: Option<Lrc<FluentBundle>>,
|
2022-04-12 09:34:40 +01:00
|
|
|
fallback_bundle: LazyFallbackBundle,
|
2022-03-26 07:27:43 +00:00
|
|
|
|
2019-05-31 22:01:27 +02:00
|
|
|
/// If true, hides the longer explanation text
|
|
|
|
short_message: bool,
|
2019-07-25 21:03:53 +02:00
|
|
|
/// If true, will normalize line numbers with `LL` to prevent noise in UI test diffs.
|
2019-05-31 22:01:27 +02:00
|
|
|
ui_testing: bool,
|
2019-09-07 09:57:11 -04:00
|
|
|
|
2019-12-15 17:12:30 +02:00
|
|
|
macro_backtrace: bool,
|
2019-05-31 22:01:27 +02:00
|
|
|
}
|
|
|
|
|
2024-01-05 10:37:44 +11:00
|
|
|
impl Translate for AnnotateSnippetEmitter {
|
2022-08-10 17:30:47 +01:00
|
|
|
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
|
|
|
|
self.fluent_bundle.as_ref()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fallback_fluent_bundle(&self) -> &FluentBundle {
|
2022-11-29 11:01:17 +00:00
|
|
|
&self.fallback_bundle
|
2022-08-10 17:30:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-05 10:37:44 +11:00
|
|
|
impl Emitter for AnnotateSnippetEmitter {
|
2019-05-31 22:01:27 +02:00
|
|
|
/// The entry point for the diagnostics generation
|
2024-02-22 18:32:06 +11:00
|
|
|
fn emit_diagnostic(&mut self, mut diag: DiagInner) {
|
2024-02-16 06:07:49 +11:00
|
|
|
let fluent_args = to_fluent_args(diag.args.iter());
|
2022-03-26 07:27:43 +00:00
|
|
|
|
2024-02-02 15:44:22 +11:00
|
|
|
let mut suggestions = diag.suggestions.unwrap_or(vec![]);
|
|
|
|
self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
|
2019-05-31 22:01:27 +02:00
|
|
|
|
2019-12-15 17:47:51 +02:00
|
|
|
self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
|
2024-02-02 15:44:22 +11:00
|
|
|
&mut diag.span,
|
|
|
|
&mut diag.children,
|
2019-10-15 08:12:55 +02:00
|
|
|
&diag.level,
|
2019-12-15 17:12:30 +02:00
|
|
|
self.macro_backtrace,
|
2019-09-07 09:57:11 -04:00
|
|
|
);
|
2019-12-22 17:42:04 -05:00
|
|
|
|
2019-10-15 08:12:55 +02:00
|
|
|
self.emit_messages_default(
|
|
|
|
&diag.level,
|
2023-12-20 17:12:17 +11:00
|
|
|
&diag.messages,
|
2022-03-26 07:27:43 +00:00
|
|
|
&fluent_args,
|
2019-10-15 08:12:55 +02:00
|
|
|
&diag.code,
|
2024-02-02 15:44:22 +11:00
|
|
|
&diag.span,
|
|
|
|
&diag.children,
|
|
|
|
&suggestions,
|
2019-05-31 22:01:27 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-11-15 08:32:31 -05:00
|
|
|
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
|
2019-10-13 21:48:39 -07:00
|
|
|
self.source_map.as_ref()
|
|
|
|
}
|
|
|
|
|
2019-05-31 22:01:27 +02:00
|
|
|
fn should_show_explain(&self) -> bool {
|
|
|
|
!self.short_message
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-08 22:48:26 +02:00
|
|
|
/// Provides the source string for the given `line` of `file`
|
|
|
|
fn source_string(file: Lrc<SourceFile>, line: &Line) -> String {
|
|
|
|
file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or_default()
|
2019-05-31 22:01:27 +02:00
|
|
|
}
|
|
|
|
|
2024-02-22 18:32:06 +11:00
|
|
|
/// Maps `diagnostic::Level` to `snippet::AnnotationType`
|
2020-05-08 22:48:26 +02:00
|
|
|
fn annotation_type_for_level(level: Level) -> AnnotationType {
|
|
|
|
match level {
|
2024-02-12 16:48:45 +11:00
|
|
|
Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => AnnotationType::Error,
|
2024-01-09 12:28:45 +11:00
|
|
|
Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning,
|
2022-03-20 20:02:18 +01:00
|
|
|
Level::Note | Level::OnceNote => AnnotationType::Note,
|
2023-07-25 19:37:45 +02:00
|
|
|
Level::Help | Level::OnceHelp => AnnotationType::Help,
|
2022-01-26 03:39:14 +00:00
|
|
|
// FIXME(#59346): Not sure how to map this level
|
|
|
|
Level::FailureNote => AnnotationType::Error,
|
2020-08-13 15:41:52 -04:00
|
|
|
Level::Allow => panic!("Should not call with Allow"),
|
2021-08-06 23:18:16 +02:00
|
|
|
Level::Expect(_) => panic!("Should not call with Expect"),
|
2019-05-31 22:01:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-05 10:37:44 +11:00
|
|
|
impl AnnotateSnippetEmitter {
|
2019-05-31 22:01:27 +02:00
|
|
|
pub fn new(
|
2019-11-15 08:32:31 -05:00
|
|
|
source_map: Option<Lrc<SourceMap>>,
|
2022-03-28 09:36:20 +01:00
|
|
|
fluent_bundle: Option<Lrc<FluentBundle>>,
|
2022-04-12 09:34:40 +01:00
|
|
|
fallback_bundle: LazyFallbackBundle,
|
2019-09-07 09:57:11 -04:00
|
|
|
short_message: bool,
|
2019-12-15 17:12:30 +02:00
|
|
|
macro_backtrace: bool,
|
2019-05-31 22:01:27 +02:00
|
|
|
) -> Self {
|
2022-03-28 09:36:20 +01:00
|
|
|
Self {
|
|
|
|
source_map,
|
|
|
|
fluent_bundle,
|
|
|
|
fallback_bundle,
|
|
|
|
short_message,
|
|
|
|
ui_testing: false,
|
|
|
|
macro_backtrace,
|
|
|
|
}
|
2019-05-31 22:01:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Allows to modify `Self` to enable or disable the `ui_testing` flag.
|
|
|
|
///
|
|
|
|
/// If this is set to true, line numbers will be normalized as `LL` in the output.
|
|
|
|
pub fn ui_testing(mut self, ui_testing: bool) -> Self {
|
|
|
|
self.ui_testing = ui_testing;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2019-06-03 07:38:19 +02:00
|
|
|
fn emit_messages_default(
|
|
|
|
&mut self,
|
|
|
|
level: &Level,
|
2024-02-29 11:58:51 +11:00
|
|
|
messages: &[(DiagMessage, Style)],
|
2022-03-26 07:27:43 +00:00
|
|
|
args: &FluentArgs<'_>,
|
Stop using `String` for error codes.
Error codes are integers, but `String` is used everywhere to represent
them. Gross!
This commit introduces `ErrCode`, an integral newtype for error codes,
replacing `String`. It also introduces a constant for every error code,
e.g. `E0123`, and removes the `error_code!` macro. The constants are
imported wherever used with `use rustc_errors::codes::*`.
With the old code, we have three different ways to specify an error code
at a use point:
```
error_code!(E0123) // macro call
struct_span_code_err!(dcx, span, E0123, "msg"); // bare ident arg to macro call
\#[diag(name, code = "E0123")] // string
struct Diag;
```
With the new code, they all use the `E0123` constant.
```
E0123 // constant
struct_span_code_err!(dcx, span, E0123, "msg"); // constant
\#[diag(name, code = E0123)] // constant
struct Diag;
```
The commit also changes the structure of the error code definitions:
- `rustc_error_codes` now just defines a higher-order macro listing the
used error codes and nothing else.
- Because that's now the only thing in the `rustc_error_codes` crate, I
moved it into the `lib.rs` file and removed the `error_codes.rs` file.
- `rustc_errors` uses that macro to define everything, e.g. the error
code constants and the `DIAGNOSTIC_TABLES`. This is in its new
`codes.rs` file.
2024-01-14 10:57:07 +11:00
|
|
|
code: &Option<ErrCode>,
|
2019-06-03 07:38:19 +02:00
|
|
|
msp: &MultiSpan,
|
2024-02-23 06:42:05 +11:00
|
|
|
_children: &[Subdiag],
|
2020-05-08 22:48:26 +02:00
|
|
|
_suggestions: &[CodeSuggestion],
|
2019-05-31 22:01:27 +02:00
|
|
|
) {
|
2022-03-26 07:27:43 +00:00
|
|
|
let message = self.translate_messages(messages, args);
|
2020-05-08 22:48:26 +02:00
|
|
|
if let Some(source_map) = &self.source_map {
|
|
|
|
// Make sure our primary file comes first
|
2023-11-21 20:07:32 +01:00
|
|
|
let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() {
|
2020-05-16 04:57:40 +02:00
|
|
|
if primary_span.is_dummy() {
|
|
|
|
// FIXME(#59346): Not sure when this is the case and what
|
|
|
|
// should be done if it happens
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
source_map.lookup_char_pos(primary_span.lo())
|
|
|
|
}
|
2020-05-08 22:48:26 +02:00
|
|
|
} else {
|
|
|
|
// FIXME(#59346): Not sure when this is the case and what
|
|
|
|
// should be done if it happens
|
|
|
|
return;
|
|
|
|
};
|
2022-03-26 07:27:43 +00:00
|
|
|
let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp);
|
2020-05-16 04:57:40 +02:00
|
|
|
if let Ok(pos) =
|
|
|
|
annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name))
|
|
|
|
{
|
|
|
|
annotated_files.swap(0, pos);
|
|
|
|
}
|
2023-07-24 14:40:44 +02:00
|
|
|
// owned: file name, line source, line index, annotations
|
|
|
|
type Owned = (String, String, usize, Vec<crate::snippet::Annotation>);
|
2020-05-08 22:48:26 +02:00
|
|
|
let annotated_files: Vec<Owned> = annotated_files
|
|
|
|
.into_iter()
|
|
|
|
.flat_map(|annotated_file| {
|
|
|
|
let file = annotated_file.file;
|
|
|
|
annotated_file
|
|
|
|
.lines
|
|
|
|
.into_iter()
|
|
|
|
.map(|line| {
|
2023-07-24 14:40:44 +02:00
|
|
|
// Ensure the source file is present before we try
|
|
|
|
// to load a string from it.
|
2023-09-15 15:32:34 +02:00
|
|
|
// FIXME(#115869): support -Z ignore-directory-in-diagnostics-source-blocks
|
2023-08-31 12:50:44 +02:00
|
|
|
source_map.ensure_source_file_source_present(&file);
|
2023-07-24 14:40:44 +02:00
|
|
|
(
|
|
|
|
format!("{}", source_map.filename_for_diagnostics(&file.name)),
|
|
|
|
source_string(file.clone(), &line),
|
|
|
|
line.line_index,
|
|
|
|
line.annotations,
|
|
|
|
)
|
2020-05-08 22:48:26 +02:00
|
|
|
})
|
|
|
|
.collect::<Vec<Owned>>()
|
|
|
|
})
|
|
|
|
.collect();
|
Stop using `String` for error codes.
Error codes are integers, but `String` is used everywhere to represent
them. Gross!
This commit introduces `ErrCode`, an integral newtype for error codes,
replacing `String`. It also introduces a constant for every error code,
e.g. `E0123`, and removes the `error_code!` macro. The constants are
imported wherever used with `use rustc_errors::codes::*`.
With the old code, we have three different ways to specify an error code
at a use point:
```
error_code!(E0123) // macro call
struct_span_code_err!(dcx, span, E0123, "msg"); // bare ident arg to macro call
\#[diag(name, code = "E0123")] // string
struct Diag;
```
With the new code, they all use the `E0123` constant.
```
E0123 // constant
struct_span_code_err!(dcx, span, E0123, "msg"); // constant
\#[diag(name, code = E0123)] // constant
struct Diag;
```
The commit also changes the structure of the error code definitions:
- `rustc_error_codes` now just defines a higher-order macro listing the
used error codes and nothing else.
- Because that's now the only thing in the `rustc_error_codes` crate, I
moved it into the `lib.rs` file and removed the `error_codes.rs` file.
- `rustc_errors` uses that macro to define everything, e.g. the error
code constants and the `DIAGNOSTIC_TABLES`. This is in its new
`codes.rs` file.
2024-01-14 10:57:07 +11:00
|
|
|
let code = code.map(|code| code.to_string());
|
2020-05-08 22:48:26 +02:00
|
|
|
let snippet = Snippet {
|
|
|
|
title: Some(Annotation {
|
|
|
|
label: Some(&message),
|
2024-01-13 13:11:56 +11:00
|
|
|
id: code.as_deref(),
|
2020-05-08 22:48:26 +02:00
|
|
|
annotation_type: annotation_type_for_level(*level),
|
|
|
|
}),
|
|
|
|
footer: vec![],
|
|
|
|
slices: annotated_files
|
|
|
|
.iter()
|
2023-07-24 14:40:44 +02:00
|
|
|
.map(|(file_name, source, line_index, annotations)| {
|
2020-05-08 22:48:26 +02:00
|
|
|
Slice {
|
|
|
|
source,
|
|
|
|
line_start: *line_index,
|
2023-11-21 20:07:32 +01:00
|
|
|
origin: Some(file_name),
|
2020-05-08 22:48:26 +02:00
|
|
|
// FIXME(#59346): Not really sure when `fold` should be true or false
|
|
|
|
fold: false,
|
|
|
|
annotations: annotations
|
2020-06-09 15:57:08 +02:00
|
|
|
.iter()
|
2020-05-08 22:48:26 +02:00
|
|
|
.map(|annotation| SourceAnnotation {
|
2023-03-28 08:12:36 -04:00
|
|
|
range: (
|
|
|
|
annotation.start_col.display,
|
|
|
|
annotation.end_col.display,
|
|
|
|
),
|
2020-06-09 15:57:08 +02:00
|
|
|
label: annotation.label.as_deref().unwrap_or_default(),
|
2020-05-08 22:48:26 +02:00
|
|
|
annotation_type: annotation_type_for_level(*level),
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
};
|
2019-06-01 08:29:12 +02:00
|
|
|
// 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.
|
2023-12-30 18:11:41 +03:00
|
|
|
let renderer = Renderer::plain().anonymized_line_numbers(self.ui_testing);
|
|
|
|
eprintln!("{}", renderer.render(snippet))
|
2020-05-08 22:48:26 +02:00
|
|
|
}
|
|
|
|
// FIXME(#59346): Is it ok to return None if there's no source_map?
|
2019-05-31 22:01:27 +02:00
|
|
|
}
|
|
|
|
}
|