Skip to content

Commit

Permalink
Auto merge of #38955 - estebank:highlighted-diags, r=nikomatsakis
Browse files Browse the repository at this point in the history
Teach Diagnostics to highlight text

Support styled `Diagnostic` output:

<img width="469" alt="mismatched types error with colorized types in the note" src="https://cloud.githubusercontent.com/assets/1606434/21871227/93a84198-d815-11e6-88b1-0ede3c7e28ef.png">

Closes #37532 and #38901.

r? @nikomatsakis CC @jonathandturner @nagisa @nrc
  • Loading branch information
bors committed Jan 20, 2017
2 parents b53d374 + fc774e6 commit b7ca2b9
Show file tree
Hide file tree
Showing 29 changed files with 242 additions and 67 deletions.
2 changes: 1 addition & 1 deletion src/librustc/lint/context.rs
Expand Up @@ -562,7 +562,7 @@ pub trait LintContext<'tcx>: Sized {
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();
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_driver/test.rs
Expand Up @@ -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);
}
}
}
Expand Down
56 changes: 49 additions & 7 deletions src/librustc_errors/diagnostic.rs
Expand Up @@ -14,12 +14,13 @@ use RenderSpan;
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>,
Expand All @@ -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>,
}
Expand All @@ -42,7 +43,7 @@ impl Diagnostic {
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![],
Expand Down Expand Up @@ -96,8 +97,14 @@ impl Diagnostic {
-> &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
}

Expand All @@ -106,6 +113,11 @@ impl Diagnostic {
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)
Expand Down Expand Up @@ -168,7 +180,11 @@ impl Diagnostic {
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
}

Expand All @@ -193,10 +209,36 @@ impl Diagnostic {
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
}
}
101 changes: 75 additions & 26 deletions src/librustc_errors/emitter.rs
Expand Up @@ -33,7 +33,11 @@ impl Emitter for EmitterWriter {
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);
}
}

Expand Down Expand Up @@ -695,17 +699,23 @@ impl EmitterWriter {
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,
});
}
}

/// 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
Expand All @@ -726,20 +736,56 @@ impl EmitterWriter {
.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;
}
}
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));
}
acc.push_str(&x.1);
acc
})
}
}

fn emit_message_default(&mut self,
msp: &MultiSpan,
msg: &str,
msg: &Vec<(String, Style)>,
code: &Option<String>,
level: &Level,
max_line_num_len: usize,
Expand All @@ -755,9 +801,7 @@ impl EmitterWriter {
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 {
Expand All @@ -769,7 +813,9 @@ impl EmitterWriter {
_ => {}
}
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
Expand Down Expand Up @@ -879,7 +925,7 @@ impl EmitterWriter {
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;
Expand All @@ -890,9 +936,11 @@ impl EmitterWriter {

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();

Expand Down Expand Up @@ -921,7 +969,7 @@ impl EmitterWriter {
}
fn emit_messages_default(&mut self,
level: &Level,
message: &String,
message: &Vec<(String, Style)>,
code: &Option<String>,
span: &MultiSpan,
children: &Vec<SubDiagnostic>) {
Expand All @@ -942,7 +990,7 @@ impl EmitterWriter {
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,
Expand All @@ -954,15 +1002,15 @@ impl EmitterWriter {
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),
_ => ()
}
},
None => {
match self.emit_message_default(&child.span,
&child.message,
&child.styled_message(),
&None,
&child.level,
max_line_num_len,
Expand Down Expand Up @@ -1197,6 +1245,7 @@ impl Destination {
self.start_attr(term::Attr::Bold)?;
self.start_attr(term::Attr::ForegroundColor(l.color()))?;
}
Style::Highlight => self.start_attr(term::Attr::Bold)?,
}
Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_errors/snippet.rs
Expand Up @@ -185,4 +185,5 @@ pub enum Style {
NoStyle,
ErrorCode,
Level(Level),
Highlight,
}
4 changes: 2 additions & 2 deletions src/librustc_trans/back/write.rs
Expand Up @@ -121,13 +121,13 @@ impl SharedEmitter {
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,
});
Expand Down

0 comments on commit b7ca2b9

Please sign in to comment.