Teach Diagnostics to highlight text

This commit is contained in:
Esteban Küber 2017-01-11 13:55:41 -08:00
parent bd8e9b0c82
commit fc774e629f
29 changed files with 242 additions and 67 deletions

View file

@ -562,7 +562,7 @@ fn early_lint(&self, early_lint: EarlyLint) {
let span = early_lint.diagnostic.span.primary_span().expect("early lint w/o primary span");
let mut err = self.struct_span_lint(early_lint.id.lint,
span,
&early_lint.diagnostic.message);
&early_lint.diagnostic.message());
err.copy_details_not_message(&early_lint.diagnostic);
err.emit();
}

View file

@ -79,9 +79,9 @@ fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
impl Emitter for ExpectErrorEmitter {
fn emit(&mut self, db: &DiagnosticBuilder) {
remove_message(self, &db.message, db.level);
remove_message(self, &db.message(), db.level);
for child in &db.children {
remove_message(self, &child.message, child.level);
remove_message(self, &child.message(), child.level);
}
}
}

View file

@ -14,12 +14,13 @@
use RenderSpan::Suggestion;
use std::fmt;
use syntax_pos::{MultiSpan, Span};
use snippet::Style;
#[must_use]
#[derive(Clone, Debug, PartialEq)]
pub struct Diagnostic {
pub level: Level,
pub message: String,
pub message: Vec<(String, Style)>,
pub code: Option<String>,
pub span: MultiSpan,
pub children: Vec<SubDiagnostic>,
@ -29,7 +30,7 @@ pub struct Diagnostic {
#[derive(Clone, Debug, PartialEq)]
pub struct SubDiagnostic {
pub level: Level,
pub message: String,
pub message: Vec<(String, Style)>,
pub span: MultiSpan,
pub render_span: Option<RenderSpan>,
}
@ -42,7 +43,7 @@ pub fn new(level: Level, message: &str) -> Self {
pub fn new_with_code(level: Level, code: Option<String>, message: &str) -> Self {
Diagnostic {
level: level,
message: message.to_owned(),
message: vec![(message.to_owned(), Style::NoStyle)],
code: code,
span: MultiSpan::new(),
children: vec![],
@ -96,8 +97,14 @@ pub fn note_expected_found_extra(&mut self,
-> &mut Self
{
// For now, just attach these as notes
self.note(&format!("expected {} `{}`{}", label, expected, expected_extra));
self.note(&format!(" found {} `{}`{}", label, found, found_extra));
self.highlighted_note(vec![
(format!("expected {} `", label), Style::NoStyle),
(format!("{}", expected), Style::Highlight),
(format!("`{}\n", expected_extra), Style::NoStyle),
(format!(" found {} `", label), Style::NoStyle),
(format!("{}", found), Style::Highlight),
(format!("`{}", found_extra), Style::NoStyle),
]);
self
}
@ -106,6 +113,11 @@ pub fn note(&mut self, msg: &str) -> &mut Self {
self
}
pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
self
}
pub fn span_note<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str)
@ -168,7 +180,11 @@ pub fn code(&mut self, s: String) -> &mut Self {
self
}
pub fn message(&self) -> &str {
pub fn message(&self) -> String {
self.message.iter().map(|i| i.0.to_owned()).collect::<String>()
}
pub fn styled_message(&self) -> &Vec<(String, Style)> {
&self.message
}
@ -193,10 +209,36 @@ fn sub(&mut self,
render_span: Option<RenderSpan>) {
let sub = SubDiagnostic {
level: level,
message: message.to_owned(),
message: vec![(message.to_owned(), Style::NoStyle)],
span: span,
render_span: render_span,
};
self.children.push(sub);
}
/// Convenience function for internal use, clients should use one of the
/// public methods above.
fn sub_with_highlights(&mut self,
level: Level,
message: Vec<(String, Style)>,
span: MultiSpan,
render_span: Option<RenderSpan>) {
let sub = SubDiagnostic {
level: level,
message: message,
span: span,
render_span: render_span,
};
self.children.push(sub);
}
}
impl SubDiagnostic {
pub fn message(&self) -> String {
self.message.iter().map(|i| i.0.to_owned()).collect::<String>()
}
pub fn styled_message(&self) -> &Vec<(String, Style)> {
&self.message
}
}

View file

@ -33,7 +33,11 @@ fn emit(&mut self, db: &DiagnosticBuilder) {
let mut primary_span = db.span.clone();
let mut children = db.children.clone();
self.fix_multispans_in_std_macros(&mut primary_span, &mut children);
self.emit_messages_default(&db.level, &db.message, &db.code, &primary_span, &children);
self.emit_messages_default(&db.level,
&db.styled_message(),
&db.code,
&primary_span,
&children);
}
}
@ -695,8 +699,8 @@ fn fix_multispans_in_std_macros(&mut self,
if spans_updated {
children.push(SubDiagnostic {
level: Level::Note,
message: "this error originates in a macro outside of the current crate"
.to_string(),
message: vec![("this error originates in a macro outside of the current crate"
.to_string(), Style::NoStyle)],
span: MultiSpan::new(),
render_span: None,
});
@ -704,8 +708,14 @@ fn fix_multispans_in_std_macros(&mut self,
}
/// Add a left margin to every line but the first, given a padding length and the label being
/// displayed.
fn msg_with_padding(&self, msg: &str, padding: usize, label: &str) -> String {
/// displayed, keeping the provided highlighting.
fn msg_to_buffer(&self,
buffer: &mut StyledBuffer,
msg: &Vec<(String, Style)>,
padding: usize,
label: &str,
override_style: Option<Style>) {
// The extra 5 ` ` is padding that's always needed to align to the `note: `:
//
// error: message
@ -726,20 +736,56 @@ fn msg_with_padding(&self, msg: &str, padding: usize, label: &str) -> String {
.map(|_| " ")
.collect::<String>();
msg.split('\n').enumerate().fold("".to_owned(), |mut acc, x| {
if x.0 != 0 {
acc.push_str("\n");
// Align every line with first one.
acc.push_str(&padding);
/// Return wether `style`, or the override if present and the style is `NoStyle`.
fn style_or_override(style: Style, override_style: Option<Style>) -> Style {
if let Some(o) = override_style {
if style == Style::NoStyle {
return o;
}
}
acc.push_str(&x.1);
acc
})
style
}
let mut line_number = 0;
// Provided the following diagnostic message:
//
// let msg = vec![
// ("
// ("highlighted multiline\nstring to\nsee how it ", Style::NoStyle),
// ("looks", Style::Highlight),
// ("with\nvery ", Style::NoStyle),
// ("weird", Style::Highlight),
// (" formats\n", Style::NoStyle),
// ("see?", Style::Highlight),
// ];
//
// the expected output on a note is (* surround the highlighted text)
//
// = note: highlighted multiline
// string to
// see how it *looks* with
// very *weird* formats
// see?
for &(ref text, ref style) in msg.iter() {
let lines = text.split('\n').collect::<Vec<_>>();
if lines.len() > 1 {
for (i, line) in lines.iter().enumerate() {
if i != 0 {
line_number += 1;
buffer.append(line_number, &padding, Style::NoStyle);
}
buffer.append(line_number, line, style_or_override(*style, override_style));
}
} else {
buffer.append(line_number, text, style_or_override(*style, override_style));
}
}
}
fn emit_message_default(&mut self,
msp: &MultiSpan,
msg: &str,
msg: &Vec<(String, Style)>,
code: &Option<String>,
level: &Level,
max_line_num_len: usize,
@ -755,9 +801,7 @@ fn emit_message_default(&mut self,
draw_note_separator(&mut buffer, 0, max_line_num_len + 1);
buffer.append(0, &level.to_string(), Style::HeaderMsg);
buffer.append(0, ": ", Style::NoStyle);
let message = self.msg_with_padding(msg, max_line_num_len, "note");
buffer.append(0, &message, Style::NoStyle);
self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note", None);
} else {
buffer.append(0, &level.to_string(), Style::Level(level.clone()));
match code {
@ -769,7 +813,9 @@ fn emit_message_default(&mut self,
_ => {}
}
buffer.append(0, ": ", Style::HeaderMsg);
buffer.append(0, msg, Style::HeaderMsg);
for &(ref text, _) in msg.iter() {
buffer.append(0, text, Style::HeaderMsg);
}
}
// Preprocess all the annotations so that they are grouped by file and by line number
@ -879,7 +925,7 @@ fn emit_message_default(&mut self,
fn emit_suggestion_default(&mut self,
suggestion: &CodeSuggestion,
level: &Level,
msg: &str,
msg: &Vec<(String, Style)>,
max_line_num_len: usize)
-> io::Result<()> {
use std::borrow::Borrow;
@ -890,9 +936,11 @@ fn emit_suggestion_default(&mut self,
buffer.append(0, &level.to_string(), Style::Level(level.clone()));
buffer.append(0, ": ", Style::HeaderMsg);
let message = self.msg_with_padding(msg, max_line_num_len, "suggestion");
buffer.append(0, &message, Style::HeaderMsg);
self.msg_to_buffer(&mut buffer,
msg,
max_line_num_len,
"suggestion",
Some(Style::HeaderMsg));
let lines = cm.span_to_lines(primary_span).unwrap();
@ -921,7 +969,7 @@ fn emit_suggestion_default(&mut self,
}
fn emit_messages_default(&mut self,
level: &Level,
message: &String,
message: &Vec<(String, Style)>,
code: &Option<String>,
span: &MultiSpan,
children: &Vec<SubDiagnostic>) {
@ -942,7 +990,7 @@ fn emit_messages_default(&mut self,
match child.render_span {
Some(FullSpan(ref msp)) => {
match self.emit_message_default(msp,
&child.message,
&child.styled_message(),
&None,
&child.level,
max_line_num_len,
@ -954,7 +1002,7 @@ fn emit_messages_default(&mut self,
Some(Suggestion(ref cs)) => {
match self.emit_suggestion_default(cs,
&child.level,
&child.message,
&child.styled_message(),
max_line_num_len) {
Err(e) => panic!("failed to emit error: {}", e),
_ => ()
@ -962,7 +1010,7 @@ fn emit_messages_default(&mut self,
},
None => {
match self.emit_message_default(&child.span,
&child.message,
&child.styled_message(),
&None,
&child.level,
max_line_num_len,
@ -1197,6 +1245,7 @@ fn apply_style(&mut self, lvl: Level, style: Style) -> io::Result<()> {
self.start_attr(term::Attr::Bold)?;
self.start_attr(term::Attr::ForegroundColor(l.color()))?;
}
Style::Highlight => self.start_attr(term::Attr::Bold)?,
}
Ok(())
}

View file

@ -185,4 +185,5 @@ pub enum Style {
NoStyle,
ErrorCode,
Level(Level),
Highlight,
}

View file

@ -121,13 +121,13 @@ fn dump(&mut self, handler: &Handler) {
impl Emitter for SharedEmitter {
fn emit(&mut self, db: &DiagnosticBuilder) {
self.buffer.lock().unwrap().push(Diagnostic {
msg: db.message.to_string(),
msg: db.message(),
code: db.code.clone(),
lvl: db.level,
});
for child in &db.children {
self.buffer.lock().unwrap().push(Diagnostic {
msg: child.message.to_string(),
msg: child.message(),
code: None,
lvl: child.level,
});

View file

@ -74,15 +74,15 @@ fn emit(&mut self, db: &DiagnosticBuilder) {
// The following data types are provided just for serialisation.
#[derive(RustcEncodable)]
struct Diagnostic<'a> {
struct Diagnostic {
/// The primary error message.
message: &'a str,
message: String,
code: Option<DiagnosticCode>,
/// "error: internal compiler error", "error", "warning", "note", "help".
level: &'static str,
spans: Vec<DiagnosticSpan>,
/// Associated diagnostic messages.
children: Vec<Diagnostic<'a>>,
children: Vec<Diagnostic>,
/// The message as rustc would render it. Currently this is only
/// `Some` for "suggestions", but eventually it will include all
/// snippets.
@ -148,12 +148,12 @@ struct DiagnosticCode {
explanation: Option<&'static str>,
}
impl<'a> Diagnostic<'a> {
fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder,
je: &JsonEmitter)
-> Diagnostic<'c> {
impl Diagnostic {
fn from_diagnostic_builder(db: &DiagnosticBuilder,
je: &JsonEmitter)
-> Diagnostic {
Diagnostic {
message: &db.message,
message: db.message(),
code: DiagnosticCode::map_opt_string(db.code.clone(), je),
level: db.level.to_str(),
spans: DiagnosticSpan::from_multispan(&db.span, je),
@ -164,9 +164,9 @@ fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder,
}
}
fn from_sub_diagnostic<'c>(db: &'c SubDiagnostic, je: &JsonEmitter) -> Diagnostic<'c> {
fn from_sub_diagnostic(db: &SubDiagnostic, je: &JsonEmitter) -> Diagnostic {
Diagnostic {
message: &db.message,
message: db.message(),
code: None,
level: db.level.to_str(),
spans: db.render_span.as_ref()

View file

@ -18,6 +18,5 @@
//~^ ERROR: mismatched types
//~| NOTE: expected normal fn, found unsafe fn
//~| NOTE: expected type `fn(proc_macro::TokenStream) -> proc_macro::TokenStream`
//~| NOTE: found type `unsafe extern "C" fn(i32, u32) -> u32 {foo}`
loop {}
}

View file

@ -15,13 +15,11 @@ pub trait Buffer<'a, R: Resources<'a>> {
//~^ ERROR mismatched types
//~| lifetime mismatch
//~| NOTE expected type `Resources<'_>`
//~| NOTE found type `Resources<'a>`
//~| NOTE the lifetime 'a as defined on the method body at 14:4...
//~| NOTE ...does not necessarily outlive the anonymous lifetime #1 defined on the method body
//~| ERROR mismatched types
//~| lifetime mismatch
//~| NOTE expected type `Resources<'_>`
//~| NOTE found type `Resources<'a>`
//~| NOTE the anonymous lifetime #1 defined on the method body at 14:4...
//~| NOTE ...does not necessarily outlive the lifetime 'a as defined on the method body
}

View file

@ -16,7 +16,6 @@ fn next(&'a mut self) -> Option<Self::Item>
//~^ ERROR method not compatible with trait
//~| lifetime mismatch
//~| NOTE expected type `fn(&mut RepeatMut<'a, T>) -> std::option::Option<&mut T>`
//~| NOTE found type `fn(&'a mut RepeatMut<'a, T>) -> std::option::Option<&mut T>`
{
//~^ NOTE the anonymous lifetime #1 defined on the body
//~| NOTE ...does not necessarily outlive the lifetime 'a as defined on the body

View file

@ -8,7 +8,7 @@ error[E0053]: method `b` has an incompatible type for trait
| ^ expected type parameter, found a different type parameter
|
= note: expected type `fn(&E, F) -> F`
= note: found type `fn(&E, G) -> G`
found type `fn(&E, G) -> G`
error: aborting due to previous error

View file

@ -0,0 +1,23 @@
error[E0053]: method `foo` has an incompatible type for trait
--> $DIR/E0053.rs:19:15
|
12 | fn foo(x: u16); //~ NOTE type in trait
| --- type in trait
...
19 | fn foo(x: i16) { }
| ^^^ expected u16, found i16
error[E0053]: method `bar` has an incompatible type for trait
--> $DIR/E0053.rs:22:12
|
13 | fn bar(&self); //~ NOTE type in trait
| ----- type in trait
...
22 | fn bar(&mut self) { }
| ^^^^^^^^^ types differ in mutability
|
= note: expected type `fn(&Bar)`
found type `fn(&mut Bar)`
error: aborting due to 2 previous errors

View file

@ -0,0 +1,19 @@
error[E0409]: variable `y` is bound with different mode in pattern #2 than in pattern #1
--> $DIR/E0409.rs:15:23
|
15 | (0, ref y) | (y, 0) => {} //~ ERROR E0409
| - ^ bound in different ways
| |
| first binding
error[E0308]: mismatched types
--> $DIR/E0409.rs:15:23
|
15 | (0, ref y) | (y, 0) => {} //~ ERROR E0409
| ^ expected &{integer}, found integral variable
|
= note: expected type `&{integer}`
found type `{integer}`
error: aborting due to previous error

View file

@ -0,0 +1,11 @@
error[E0308]: mismatched types
--> $DIR/issue-19109.rs:14:5
|
14 | t as *mut Trait
| ^^^^^^^^^^^^^^^ expected (), found *-ptr
|
= note: expected type `()`
found type `*mut Trait`
error: aborting due to previous error

View file

@ -5,7 +5,7 @@ error[E0308]: mismatched types
| ^^^^ expected type parameter, found bool
|
= note: expected type `bool` (type parameter)
= note: found type `bool` (bool)
found type `bool` (bool)
error: aborting due to previous error

View file

@ -5,7 +5,7 @@ error[E0308]: mismatched types
| ^^^^ expected struct `Foo`, found reference
|
= note: expected type `Foo`
= note: found type `&_`
found type `&_`
= help: did you mean `foo: &Foo`?
error[E0308]: mismatched types
@ -15,7 +15,7 @@ error[E0308]: mismatched types
| ^^^^ expected u32, found reference
|
= note: expected type `u32`
= note: found type `&_`
found type `&_`
error[E0308]: mismatched types
--> $DIR/issue-38371.rs:31:8
@ -24,7 +24,7 @@ error[E0308]: mismatched types
| ^^^^^ expected u32, found reference
|
= note: expected type `u32`
= note: found type `&_`
found type `&_`
error[E0529]: expected an array or slice, found `u32`
--> $DIR/issue-38371.rs:34:9

View file

@ -7,7 +7,7 @@ error[E0308]: mismatched types
| |_____^ ...ending here: expected u32, found ()
|
= note: expected type `u32`
= note: found type `()`
found type `()`
error: aborting due to previous error

View file

@ -0,0 +1,23 @@
error[E0308]: mismatched types
--> $DIR/overloaded-calls-bad.rs:38:17
|
38 | let ans = s("what"); //~ ERROR mismatched types
| ^^^^^^ expected isize, found reference
|
= note: expected type `isize`
found type `&'static str`
error[E0057]: this function takes 1 parameter but 0 parameters were supplied
--> $DIR/overloaded-calls-bad.rs:42:15
|
42 | let ans = s();
| ^^^ expected 1 parameter
error[E0057]: this function takes 1 parameter but 2 parameters were supplied
--> $DIR/overloaded-calls-bad.rs:45:17
|
45 | let ans = s("burma", "shave");
| ^^^^^^^^^^^^^^^^ expected 1 parameter
error: aborting due to 3 previous errors

View file

@ -0,0 +1,11 @@
error[E0308]: mismatched types
--> $DIR/trait-bounds-cant-coerce.rs:24:7
|
24 | a(x); //~ ERROR mismatched types [E0308]
| ^ expected trait `Foo + std::marker::Send`, found trait `Foo`
|
= note: expected type `Box<Foo + std::marker::Send + 'static>`
found type `Box<Foo + 'static>`
error: aborting due to previous error

View file

@ -17,7 +17,7 @@ error[E0053]: method `bar` has an incompatible type for trait
| ^^^^ types differ in mutability
|
= note: expected type `fn(&mut Bar, &mut Bar)`
= note: found type `fn(&mut Bar, &Bar)`
found type `fn(&mut Bar, &Bar)`
error: aborting due to 2 previous errors

View file

@ -35,7 +35,7 @@ error[E0308]: mismatched types
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
|
= note: expected type `()`
= note: found type `std::result::Result<bool, std::io::Error>`
found type `std::result::Result<bool, std::io::Error>`
= help: here are some functions which might fulfill your needs:
- .unwrap()
- .unwrap_err()

View file

@ -5,7 +5,7 @@ error[E0308]: mismatched types
| ^^^^^^^^^^^^^ expected usize, found struct `std::string::String`
|
= note: expected type `usize`
= note: found type `std::string::String`
found type `std::string::String`
= help: here are some functions which might fulfill your needs:
- .capacity()
- .len()
@ -17,7 +17,7 @@ error[E0308]: mismatched types
| ^^^^^^^^^^^^^ expected &str, found struct `std::string::String`
|
= note: expected type `&str`
= note: found type `std::string::String`
found type `std::string::String`
= help: here are some functions which might fulfill your needs:
- .as_str()
- .trim()
@ -31,7 +31,7 @@ error[E0308]: mismatched types
| ^^ types differ in mutability
|
= note: expected type `&mut std::string::String`
= note: found type `&std::string::String`
found type `&std::string::String`
error[E0308]: mismatched types
--> $DIR/coerce-suggestions.rs:36:11
@ -40,7 +40,7 @@ error[E0308]: mismatched types
| ^^ types differ in mutability
|
= note: expected type `&mut i32`
= note: found type `&std::string::String`
found type `&std::string::String`
error[E0308]: mismatched types
--> $DIR/coerce-suggestions.rs:42:9
@ -49,7 +49,7 @@ error[E0308]: mismatched types
| ^^^^^ cyclic type of infinite size
|
= note: expected type `_`
= note: found type `Box<_>`
found type `Box<_>`
error: aborting due to 5 previous errors

View file

@ -5,7 +5,7 @@ error[E0308]: mismatched method receiver
| ^^^^^^^^^ expected Self, found struct `SomeType`
|
= note: expected type `&Self`
= note: found type `&SomeType`
found type `&SomeType`
error: aborting due to previous error

View file

@ -5,7 +5,7 @@ error[E0308]: mismatched types
| ^^^^^^^^^^ expected (), found closure
|
= note: expected type `()`
= note: found type `[closure@$DIR/move-closure.rs:15:17: 15:27]`
found type `[closure@$DIR/move-closure.rs:15:17: 15:27]`
error: aborting due to previous error