Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Teach Diagnostics to highlight text #38955

Merged
merged 1 commit into from
Jan 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc/lint/context.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading