mirror of
https://github.com/openharmony/third_party_rust_codespan.git
synced 2026-07-20 19:53:16 -04:00
Make diagnostics look closer to what the LSP expects
This commit is contained in:
+11
-28
@@ -180,8 +180,6 @@ pub fn make_lsp_diagnostic<F>(
|
||||
where
|
||||
F: FnMut(&str) -> Result<Url, ()>,
|
||||
{
|
||||
use codespan_reporting::LabelStyle;
|
||||
|
||||
let find_file = |index| {
|
||||
code_map
|
||||
.find_file(index)
|
||||
@@ -189,37 +187,22 @@ where
|
||||
};
|
||||
|
||||
// We need a position for the primary error so take the span from the first primary label
|
||||
let (primary_file_map, primary_label_range) = {
|
||||
let first_primary_label = diagnostic
|
||||
.labels
|
||||
.iter()
|
||||
.find(|label| label.style == LabelStyle::Primary);
|
||||
|
||||
match first_primary_label {
|
||||
Some(label) => {
|
||||
let file_map = find_file(label.span.start())?;
|
||||
(Some(file_map), byte_span_to_range(&file_map, label.span)?)
|
||||
},
|
||||
None => (None, UNKNOWN_RANGE),
|
||||
}
|
||||
};
|
||||
let primary_file_map = find_file(diagnostic.primary_label.span.start())?;
|
||||
let primary_label_range = byte_span_to_range(&primary_file_map, diagnostic.primary_label.span)?;
|
||||
|
||||
let related_information = diagnostic
|
||||
.labels
|
||||
.secondary_labels
|
||||
.into_iter()
|
||||
.map(|label| {
|
||||
let (file_map, range) = match primary_file_map {
|
||||
// If the label's span does not point anywhere, assume it comes from the same file
|
||||
// as the primary label
|
||||
Some(file_map) if label.span.start() == ByteIndex::none() => {
|
||||
(file_map, UNKNOWN_RANGE)
|
||||
},
|
||||
Some(_) | None => {
|
||||
let file_map = find_file(label.span.start())?;
|
||||
let range = byte_span_to_range(file_map, label.span)?;
|
||||
// If the label's span does not point anywhere, assume it comes from the same file
|
||||
// as the primary label
|
||||
let (file_map, range) = if label.span.start() == ByteIndex::none() {
|
||||
(primary_file_map, UNKNOWN_RANGE)
|
||||
} else {
|
||||
let file_map = find_file(label.span.start())?;
|
||||
let range = byte_span_to_range(file_map, label.span)?;
|
||||
|
||||
(file_map, range)
|
||||
},
|
||||
(file_map, range)
|
||||
};
|
||||
|
||||
let uri = codespan_name_to_file(file_map.name())
|
||||
|
||||
@@ -2,7 +2,7 @@ use structopt::StructOpt;
|
||||
|
||||
use codespan::{CodeMap, Span};
|
||||
use codespan_reporting::termcolor::StandardStream;
|
||||
use codespan_reporting::{emit, ColorArg, Diagnostic, Label, Severity};
|
||||
use codespan_reporting::{emit, ColorArg, Diagnostic, Label};
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(name = "emit")]
|
||||
@@ -31,25 +31,23 @@ fn main() {
|
||||
let file_map = code_map.add_filemap("test".into(), source.to_string());
|
||||
|
||||
let str_start = file_map.byte_index(3.into(), 6.into()).unwrap();
|
||||
let error = Diagnostic::new(Severity::Error, "Unexpected type in `+` application")
|
||||
.with_label(
|
||||
Label::new_primary(Span::from_offset(str_start, 2.into()))
|
||||
.with_message("Expected integer but got string"),
|
||||
)
|
||||
.with_label(
|
||||
Label::new_secondary(Span::from_offset(str_start, 2.into()))
|
||||
.with_message("Expected integer but got string"),
|
||||
)
|
||||
.with_code("E0001");
|
||||
let error = Diagnostic::new_error(
|
||||
"Unexpected type in `+` application",
|
||||
Label::new(
|
||||
Span::from_offset(str_start, 2.into()),
|
||||
"Expected integer but got string",
|
||||
),
|
||||
)
|
||||
.with_code("E0001")
|
||||
.with_secondary_labels(vec![Label::new(
|
||||
Span::from_offset(str_start, 2.into()),
|
||||
"Expected integer but got string",
|
||||
)]);
|
||||
|
||||
let line_start = file_map.byte_index(2.into(), 3.into()).unwrap();
|
||||
let warning = Diagnostic::new(
|
||||
Severity::Warning,
|
||||
let warning = Diagnostic::new_warning(
|
||||
"`+` function has no effect unless its result is used",
|
||||
)
|
||||
.with_label(
|
||||
Label::new_primary(Span::from_offset(line_start, 27.into()))
|
||||
.with_message("Value discarded"),
|
||||
Label::new(Span::from_offset(line_start, 27.into()), "Value discarded"),
|
||||
);
|
||||
|
||||
let diagnostics = [error, warning];
|
||||
|
||||
@@ -6,18 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Severity;
|
||||
|
||||
/// A style for the label
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "memory_usage", derive(heapsize_derive::HeapSizeOf))]
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
pub enum LabelStyle {
|
||||
/// The main focus of the diagnostic
|
||||
Primary,
|
||||
/// Supporting labels that may help to isolate the cause of the diagnostic
|
||||
Secondary,
|
||||
}
|
||||
|
||||
/// A label describing an underlined region of code associated with a diagnostic
|
||||
/// A label describing an underlined region of code associated with a diagnostic.
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "memory_usage", derive(heapsize_derive::HeapSizeOf))]
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
@@ -26,31 +15,15 @@ pub struct Label {
|
||||
pub span: ByteSpan,
|
||||
/// A message to provide some additional information for the underlined code.
|
||||
pub message: String,
|
||||
/// The style to use for the label.
|
||||
pub style: LabelStyle,
|
||||
}
|
||||
|
||||
impl Label {
|
||||
pub fn new(span: ByteSpan, style: LabelStyle) -> Label {
|
||||
pub fn new(span: ByteSpan, mesaage: impl Into<String>) -> Label {
|
||||
Label {
|
||||
span,
|
||||
message: String::new(),
|
||||
style,
|
||||
message: mesaage.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_primary(span: ByteSpan) -> Label {
|
||||
Label::new(span, LabelStyle::Primary)
|
||||
}
|
||||
|
||||
pub fn new_secondary(span: ByteSpan) -> Label {
|
||||
Label::new(span, LabelStyle::Secondary)
|
||||
}
|
||||
|
||||
pub fn with_message(mut self, message: impl Into<String>) -> Label {
|
||||
self.message = message.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a diagnostic message and associated child messages.
|
||||
@@ -62,41 +35,43 @@ pub struct Diagnostic {
|
||||
pub severity: Severity,
|
||||
/// An optional code that identifies this diagnostic.
|
||||
pub code: Option<String>,
|
||||
/// The main message associated with this diagnostic
|
||||
/// The main message associated with this diagnostic.
|
||||
pub message: String,
|
||||
/// The labelled spans marking the regions of code that cause this
|
||||
/// diagnostic to be raised
|
||||
pub labels: Vec<Label>,
|
||||
/// A label that marks the primary cause of this diagnostic.
|
||||
pub primary_label: Label,
|
||||
/// Secondary labels that provide additional context for the diagnostic.
|
||||
pub secondary_labels: Vec<Label>,
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
pub fn new(severity: Severity, message: impl Into<String>) -> Diagnostic {
|
||||
pub fn new(severity: Severity, message: impl Into<String>, primary_label: Label) -> Diagnostic {
|
||||
Diagnostic {
|
||||
severity,
|
||||
code: None,
|
||||
message: message.into(),
|
||||
labels: Vec::new(),
|
||||
primary_label,
|
||||
secondary_labels: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_bug(message: impl Into<String>) -> Diagnostic {
|
||||
Diagnostic::new(Severity::Bug, message)
|
||||
pub fn new_bug(message: impl Into<String>, primary_label: Label) -> Diagnostic {
|
||||
Diagnostic::new(Severity::Bug, message, primary_label)
|
||||
}
|
||||
|
||||
pub fn new_error(message: impl Into<String>) -> Diagnostic {
|
||||
Diagnostic::new(Severity::Error, message)
|
||||
pub fn new_error(message: impl Into<String>, primary_label: Label) -> Diagnostic {
|
||||
Diagnostic::new(Severity::Error, message, primary_label)
|
||||
}
|
||||
|
||||
pub fn new_warning(message: impl Into<String>) -> Diagnostic {
|
||||
Diagnostic::new(Severity::Warning, message)
|
||||
pub fn new_warning(message: impl Into<String>, primary_label: Label) -> Diagnostic {
|
||||
Diagnostic::new(Severity::Warning, message, primary_label)
|
||||
}
|
||||
|
||||
pub fn new_note(message: impl Into<String>) -> Diagnostic {
|
||||
Diagnostic::new(Severity::Note, message)
|
||||
pub fn new_note(message: impl Into<String>, primary_label: Label) -> Diagnostic {
|
||||
Diagnostic::new(Severity::Note, message, primary_label)
|
||||
}
|
||||
|
||||
pub fn new_help(message: impl Into<String>) -> Diagnostic {
|
||||
Diagnostic::new(Severity::Help, message)
|
||||
pub fn new_help(message: impl Into<String>, primary_label: Label) -> Diagnostic {
|
||||
Diagnostic::new(Severity::Help, message, primary_label)
|
||||
}
|
||||
|
||||
pub fn with_code(mut self, code: impl Into<String>) -> Diagnostic {
|
||||
@@ -104,13 +79,8 @@ impl Diagnostic {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_label(mut self, label: Label) -> Diagnostic {
|
||||
self.labels.push(label);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_labels(mut self, labels: impl IntoIterator<Item = Label>) -> Diagnostic {
|
||||
self.labels.extend(labels);
|
||||
pub fn with_secondary_labels(mut self, labels: impl IntoIterator<Item = Label>) -> Diagnostic {
|
||||
self.secondary_labels.extend(labels);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use codespan::{ByteSpan, CodeMap, FileMap, LineIndex, RawIndex};
|
||||
use codespan::{ByteIndex, CodeMap, FileMap, LineIndex, RawIndex};
|
||||
use std::io;
|
||||
use termcolor::{Color, ColorSpec, WriteColor};
|
||||
|
||||
use crate::{Diagnostic, Label, LabelStyle, Severity};
|
||||
use crate::{Diagnostic, Label, Severity};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
@@ -60,12 +60,15 @@ pub fn emit(
|
||||
) -> io::Result<()> {
|
||||
Header::new(diagnostic).emit(&mut writer, config)?;
|
||||
|
||||
for label in &diagnostic.labels {
|
||||
match codemap.find_file(diagnostic.primary_label.span.start()) {
|
||||
None => SimpleMessage::new(&diagnostic.primary_label).emit(&mut writer, config)?,
|
||||
Some(file) => MarkedSource::new_primary(file, &diagnostic).emit(&mut writer, config)?,
|
||||
}
|
||||
|
||||
for label in &diagnostic.secondary_labels {
|
||||
match codemap.find_file(label.span.start()) {
|
||||
None => SimpleMessage::new(&label.message).emit(&mut writer, config)?,
|
||||
Some(file) => {
|
||||
MarkedSource::new(file, &diagnostic, &label).emit(&mut writer, config)?
|
||||
},
|
||||
None => SimpleMessage::new(&label).emit(&mut writer, config)?,
|
||||
Some(file) => MarkedSource::new_secondary(file, &label).emit(&mut writer, config)?,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +154,10 @@ struct SimpleMessage<'a> {
|
||||
}
|
||||
|
||||
impl<'a> SimpleMessage<'a> {
|
||||
fn new(message: &'a str) -> SimpleMessage<'a> {
|
||||
SimpleMessage { message }
|
||||
fn new(label: &'a Label) -> SimpleMessage<'a> {
|
||||
SimpleMessage {
|
||||
message: &label.message,
|
||||
}
|
||||
}
|
||||
|
||||
fn emit(&self, writer: &mut impl WriteColor, _config: &Config) -> io::Result<()> {
|
||||
@@ -165,6 +170,11 @@ impl<'a> SimpleMessage<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
enum MarkStyle {
|
||||
Primary(Severity),
|
||||
Secondary,
|
||||
}
|
||||
|
||||
/// A marked section of source code
|
||||
///
|
||||
/// ```text
|
||||
@@ -176,39 +186,46 @@ impl<'a> SimpleMessage<'a> {
|
||||
/// ```
|
||||
struct MarkedSource<'a, S: AsRef<str>> {
|
||||
file: &'a FileMap<S>,
|
||||
span: ByteSpan,
|
||||
message: &'a str,
|
||||
severity: Option<Severity>,
|
||||
label: &'a Label,
|
||||
mark_style: MarkStyle,
|
||||
}
|
||||
|
||||
impl<'a, S: AsRef<str>> MarkedSource<'a, S> {
|
||||
fn new(
|
||||
file: &'a FileMap<S>,
|
||||
diagnostic: &Diagnostic,
|
||||
label: &'a Label,
|
||||
) -> MarkedSource<'a, S> {
|
||||
fn new_primary(file: &'a FileMap<S>, diagnostic: &'a Diagnostic) -> MarkedSource<'a, S> {
|
||||
MarkedSource {
|
||||
file,
|
||||
span: label.span,
|
||||
message: &label.message,
|
||||
severity: match label.style {
|
||||
LabelStyle::Primary => Some(diagnostic.severity),
|
||||
LabelStyle::Secondary => None,
|
||||
},
|
||||
label: &diagnostic.primary_label,
|
||||
mark_style: MarkStyle::Primary(diagnostic.severity),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_secondary(file: &'a FileMap<S>, label: &'a Label) -> MarkedSource<'a, S> {
|
||||
MarkedSource {
|
||||
file,
|
||||
label,
|
||||
mark_style: MarkStyle::Secondary,
|
||||
}
|
||||
}
|
||||
|
||||
fn start(&self) -> ByteIndex {
|
||||
self.label.span.start()
|
||||
}
|
||||
|
||||
fn end(&self) -> ByteIndex {
|
||||
self.label.span.end()
|
||||
}
|
||||
|
||||
fn label_color(&self, config: &Config) -> Color {
|
||||
match self.severity {
|
||||
None => config.secondary_color,
|
||||
Some(severity) => config.severity_color(severity),
|
||||
match self.mark_style {
|
||||
MarkStyle::Primary(severity) => config.severity_color(severity),
|
||||
MarkStyle::Secondary => config.secondary_color,
|
||||
}
|
||||
}
|
||||
|
||||
fn underline_char(&self, config: &Config) -> char {
|
||||
match self.severity {
|
||||
Some(_) => config.primary_mark,
|
||||
None => config.secondary_mark,
|
||||
match self.mark_style {
|
||||
MarkStyle::Primary(_) => config.primary_mark,
|
||||
MarkStyle::Secondary => config.secondary_mark,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,11 +234,8 @@ impl<'a, S: AsRef<str>> MarkedSource<'a, S> {
|
||||
let label_color = self.label_color(config);
|
||||
let label_spec = ColorSpec::new().set_fg(Some(label_color)).clone();
|
||||
|
||||
let (start_line, start_column) = self
|
||||
.file
|
||||
.location(self.span.start())
|
||||
.expect("location_start");
|
||||
let (end_line, _) = self.file.location(self.span.end()).expect("location_end");
|
||||
let (start_line, start_column) = self.file.location(self.start()).expect("location_start");
|
||||
let (end_line, _) = self.file.location(self.end()).expect("location_end");
|
||||
let start_line_span = self.file.line_span(start_line).expect("line_span");
|
||||
let end_line_span = self.file.line_span(end_line).expect("line_span");
|
||||
|
||||
@@ -270,11 +284,11 @@ impl<'a, S: AsRef<str>> MarkedSource<'a, S> {
|
||||
)?;
|
||||
writer.reset()?;
|
||||
|
||||
let line_trimmer = |ch: char| ch == '\r' || ch == '\n';
|
||||
|
||||
// Write source prefix before marked section
|
||||
let source_prefix = self
|
||||
.file
|
||||
.src_slice(start_line_span.with_end(self.span.start()))
|
||||
.expect("prefix");
|
||||
let prefix_span = start_line_span.with_end(self.start());
|
||||
let source_prefix = self.file.src_slice(prefix_span).expect("prefix");
|
||||
write!(writer, "{}", source_prefix)?;
|
||||
|
||||
// Write marked section
|
||||
@@ -282,7 +296,7 @@ impl<'a, S: AsRef<str>> MarkedSource<'a, S> {
|
||||
// Single line
|
||||
|
||||
// Write marked source section
|
||||
let marked_source = self.file.src_slice(self.span).expect("marked_source");
|
||||
let marked_source = self.file.src_slice(self.label.span).expect("marked_source");
|
||||
writer.set_color(&label_spec)?;
|
||||
write!(writer, "{}", marked_source)?;
|
||||
writer.reset()?;
|
||||
@@ -291,10 +305,8 @@ impl<'a, S: AsRef<str>> MarkedSource<'a, S> {
|
||||
// Multiple lines
|
||||
|
||||
// Write marked source section
|
||||
let marked_source = self
|
||||
.file
|
||||
.src_slice(start_line_span.with_start(self.span.start()))
|
||||
.expect("start_of_marked");
|
||||
let marked_span = start_line_span.with_start(self.start());
|
||||
let marked_source = self.file.src_slice(marked_span).expect("start_of_marked");
|
||||
writer.set_color(&label_spec)?;
|
||||
write!(writer, "{}", marked_source)?;
|
||||
|
||||
@@ -311,14 +323,10 @@ impl<'a, S: AsRef<str>> MarkedSource<'a, S> {
|
||||
)?;
|
||||
|
||||
// Write marked source section
|
||||
let line_span = self.file.line_span(line_index).expect("marked_line_span");
|
||||
let marked_source = self
|
||||
.file
|
||||
.src_slice(line_span)
|
||||
.expect("marked_source")
|
||||
.trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
|
||||
let mark_span = self.file.line_span(line_index).expect("marked_line_span");
|
||||
let marked_source = self.file.src_slice(mark_span).expect("marked_source");
|
||||
writer.set_color(&label_spec)?;
|
||||
write!(writer, "{}", marked_source)?;
|
||||
write!(writer, "{}", marked_source.trim_end_matches(line_trimmer))?;
|
||||
write!(writer, "\n")?;
|
||||
}
|
||||
|
||||
@@ -332,10 +340,8 @@ impl<'a, S: AsRef<str>> MarkedSource<'a, S> {
|
||||
)?;
|
||||
|
||||
// Write marked source section
|
||||
let marked_source = self
|
||||
.file
|
||||
.src_slice(end_line_span.with_end(self.span.end()))
|
||||
.expect("marked_source");
|
||||
let mark_span = end_line_span.with_end(self.end());
|
||||
let marked_source = self.file.src_slice(mark_span).expect("marked_source");
|
||||
writer.set_color(&label_spec)?;
|
||||
write!(writer, "{}", marked_source)?;
|
||||
writer.reset()?;
|
||||
@@ -343,12 +349,9 @@ impl<'a, S: AsRef<str>> MarkedSource<'a, S> {
|
||||
};
|
||||
|
||||
// Write source suffix after marked section
|
||||
let source = self
|
||||
.file
|
||||
.src_slice(end_line_span.with_start(self.span.end()))
|
||||
.expect("suffix")
|
||||
.trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
|
||||
write!(writer, "{}", source)?;
|
||||
let suffix_span = end_line_span.with_start(self.end());
|
||||
let source_suffix = self.file.src_slice(suffix_span).expect("suffix");
|
||||
write!(writer, "{}", source_suffix.trim_end_matches(line_trimmer))?;
|
||||
write!(writer, "\n")?;
|
||||
|
||||
// Write underline gutter
|
||||
@@ -362,8 +365,8 @@ impl<'a, S: AsRef<str>> MarkedSource<'a, S> {
|
||||
for _ in 0..mark_len {
|
||||
write!(writer, "{}", self.underline_char(config))?;
|
||||
}
|
||||
if !self.message.is_empty() {
|
||||
write!(writer, " {}", self.message)?;
|
||||
if !self.label.message.is_empty() {
|
||||
write!(writer, " {}", self.label.message)?;
|
||||
write!(writer, "\n")?;
|
||||
}
|
||||
writer.reset()?;
|
||||
|
||||
@@ -9,7 +9,7 @@ mod emitter;
|
||||
|
||||
pub use termcolor;
|
||||
|
||||
pub use self::diagnostic::{Diagnostic, Label, LabelStyle};
|
||||
pub use self::diagnostic::{Diagnostic, Label};
|
||||
pub use self::emitter::{emit, Config};
|
||||
|
||||
/// A severity level for diagnostic messages
|
||||
|
||||
Reference in New Issue
Block a user