Skip to content

Commit b7ca2b9

Browse files
committed
Auto merge of #38955 - estebank:highlighted-diags, r=nikomatsakis
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
2 parents b53d374 + fc774e6 commit b7ca2b9

29 files changed

+242
-67
lines changed

src/librustc/lint/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ pub trait LintContext<'tcx>: Sized {
562562
let span = early_lint.diagnostic.span.primary_span().expect("early lint w/o primary span");
563563
let mut err = self.struct_span_lint(early_lint.id.lint,
564564
span,
565-
&early_lint.diagnostic.message);
565+
&early_lint.diagnostic.message());
566566
err.copy_details_not_message(&early_lint.diagnostic);
567567
err.emit();
568568
}

src/librustc_driver/test.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
7979

8080
impl Emitter for ExpectErrorEmitter {
8181
fn emit(&mut self, db: &DiagnosticBuilder) {
82-
remove_message(self, &db.message, db.level);
82+
remove_message(self, &db.message(), db.level);
8383
for child in &db.children {
84-
remove_message(self, &child.message, child.level);
84+
remove_message(self, &child.message(), child.level);
8585
}
8686
}
8787
}

src/librustc_errors/diagnostic.rs

+49-7
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ use RenderSpan;
1414
use RenderSpan::Suggestion;
1515
use std::fmt;
1616
use syntax_pos::{MultiSpan, Span};
17+
use snippet::Style;
1718

1819
#[must_use]
1920
#[derive(Clone, Debug, PartialEq)]
2021
pub struct Diagnostic {
2122
pub level: Level,
22-
pub message: String,
23+
pub message: Vec<(String, Style)>,
2324
pub code: Option<String>,
2425
pub span: MultiSpan,
2526
pub children: Vec<SubDiagnostic>,
@@ -29,7 +30,7 @@ pub struct Diagnostic {
2930
#[derive(Clone, Debug, PartialEq)]
3031
pub struct SubDiagnostic {
3132
pub level: Level,
32-
pub message: String,
33+
pub message: Vec<(String, Style)>,
3334
pub span: MultiSpan,
3435
pub render_span: Option<RenderSpan>,
3536
}
@@ -42,7 +43,7 @@ impl Diagnostic {
4243
pub fn new_with_code(level: Level, code: Option<String>, message: &str) -> Self {
4344
Diagnostic {
4445
level: level,
45-
message: message.to_owned(),
46+
message: vec![(message.to_owned(), Style::NoStyle)],
4647
code: code,
4748
span: MultiSpan::new(),
4849
children: vec![],
@@ -96,8 +97,14 @@ impl Diagnostic {
9697
-> &mut Self
9798
{
9899
// For now, just attach these as notes
99-
self.note(&format!("expected {} `{}`{}", label, expected, expected_extra));
100-
self.note(&format!(" found {} `{}`{}", label, found, found_extra));
100+
self.highlighted_note(vec![
101+
(format!("expected {} `", label), Style::NoStyle),
102+
(format!("{}", expected), Style::Highlight),
103+
(format!("`{}\n", expected_extra), Style::NoStyle),
104+
(format!(" found {} `", label), Style::NoStyle),
105+
(format!("{}", found), Style::Highlight),
106+
(format!("`{}", found_extra), Style::NoStyle),
107+
]);
101108
self
102109
}
103110

@@ -106,6 +113,11 @@ impl Diagnostic {
106113
self
107114
}
108115

116+
pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
117+
self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
118+
self
119+
}
120+
109121
pub fn span_note<S: Into<MultiSpan>>(&mut self,
110122
sp: S,
111123
msg: &str)
@@ -168,7 +180,11 @@ impl Diagnostic {
168180
self
169181
}
170182

171-
pub fn message(&self) -> &str {
183+
pub fn message(&self) -> String {
184+
self.message.iter().map(|i| i.0.to_owned()).collect::<String>()
185+
}
186+
187+
pub fn styled_message(&self) -> &Vec<(String, Style)> {
172188
&self.message
173189
}
174190

@@ -193,10 +209,36 @@ impl Diagnostic {
193209
render_span: Option<RenderSpan>) {
194210
let sub = SubDiagnostic {
195211
level: level,
196-
message: message.to_owned(),
212+
message: vec![(message.to_owned(), Style::NoStyle)],
197213
span: span,
198214
render_span: render_span,
199215
};
200216
self.children.push(sub);
201217
}
218+
219+
/// Convenience function for internal use, clients should use one of the
220+
/// public methods above.
221+
fn sub_with_highlights(&mut self,
222+
level: Level,
223+
message: Vec<(String, Style)>,
224+
span: MultiSpan,
225+
render_span: Option<RenderSpan>) {
226+
let sub = SubDiagnostic {
227+
level: level,
228+
message: message,
229+
span: span,
230+
render_span: render_span,
231+
};
232+
self.children.push(sub);
233+
}
234+
}
235+
236+
impl SubDiagnostic {
237+
pub fn message(&self) -> String {
238+
self.message.iter().map(|i| i.0.to_owned()).collect::<String>()
239+
}
240+
241+
pub fn styled_message(&self) -> &Vec<(String, Style)> {
242+
&self.message
243+
}
202244
}

src/librustc_errors/emitter.rs

+75-26
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ impl Emitter for EmitterWriter {
3333
let mut primary_span = db.span.clone();
3434
let mut children = db.children.clone();
3535
self.fix_multispans_in_std_macros(&mut primary_span, &mut children);
36-
self.emit_messages_default(&db.level, &db.message, &db.code, &primary_span, &children);
36+
self.emit_messages_default(&db.level,
37+
&db.styled_message(),
38+
&db.code,
39+
&primary_span,
40+
&children);
3741
}
3842
}
3943

@@ -695,17 +699,23 @@ impl EmitterWriter {
695699
if spans_updated {
696700
children.push(SubDiagnostic {
697701
level: Level::Note,
698-
message: "this error originates in a macro outside of the current crate"
699-
.to_string(),
702+
message: vec![("this error originates in a macro outside of the current crate"
703+
.to_string(), Style::NoStyle)],
700704
span: MultiSpan::new(),
701705
render_span: None,
702706
});
703707
}
704708
}
705709

706710
/// Add a left margin to every line but the first, given a padding length and the label being
707-
/// displayed.
708-
fn msg_with_padding(&self, msg: &str, padding: usize, label: &str) -> String {
711+
/// displayed, keeping the provided highlighting.
712+
fn msg_to_buffer(&self,
713+
buffer: &mut StyledBuffer,
714+
msg: &Vec<(String, Style)>,
715+
padding: usize,
716+
label: &str,
717+
override_style: Option<Style>) {
718+
709719
// The extra 5 ` ` is padding that's always needed to align to the `note: `:
710720
//
711721
// error: message
@@ -726,20 +736,56 @@ impl EmitterWriter {
726736
.map(|_| " ")
727737
.collect::<String>();
728738

729-
msg.split('\n').enumerate().fold("".to_owned(), |mut acc, x| {
730-
if x.0 != 0 {
731-
acc.push_str("\n");
732-
// Align every line with first one.
733-
acc.push_str(&padding);
739+
/// Return wether `style`, or the override if present and the style is `NoStyle`.
740+
fn style_or_override(style: Style, override_style: Option<Style>) -> Style {
741+
if let Some(o) = override_style {
742+
if style == Style::NoStyle {
743+
return o;
744+
}
745+
}
746+
style
747+
}
748+
749+
let mut line_number = 0;
750+
751+
// Provided the following diagnostic message:
752+
//
753+
// let msg = vec![
754+
// ("
755+
// ("highlighted multiline\nstring to\nsee how it ", Style::NoStyle),
756+
// ("looks", Style::Highlight),
757+
// ("with\nvery ", Style::NoStyle),
758+
// ("weird", Style::Highlight),
759+
// (" formats\n", Style::NoStyle),
760+
// ("see?", Style::Highlight),
761+
// ];
762+
//
763+
// the expected output on a note is (* surround the highlighted text)
764+
//
765+
// = note: highlighted multiline
766+
// string to
767+
// see how it *looks* with
768+
// very *weird* formats
769+
// see?
770+
for &(ref text, ref style) in msg.iter() {
771+
let lines = text.split('\n').collect::<Vec<_>>();
772+
if lines.len() > 1 {
773+
for (i, line) in lines.iter().enumerate() {
774+
if i != 0 {
775+
line_number += 1;
776+
buffer.append(line_number, &padding, Style::NoStyle);
777+
}
778+
buffer.append(line_number, line, style_or_override(*style, override_style));
779+
}
780+
} else {
781+
buffer.append(line_number, text, style_or_override(*style, override_style));
734782
}
735-
acc.push_str(&x.1);
736-
acc
737-
})
783+
}
738784
}
739785

740786
fn emit_message_default(&mut self,
741787
msp: &MultiSpan,
742-
msg: &str,
788+
msg: &Vec<(String, Style)>,
743789
code: &Option<String>,
744790
level: &Level,
745791
max_line_num_len: usize,
@@ -755,9 +801,7 @@ impl EmitterWriter {
755801
draw_note_separator(&mut buffer, 0, max_line_num_len + 1);
756802
buffer.append(0, &level.to_string(), Style::HeaderMsg);
757803
buffer.append(0, ": ", Style::NoStyle);
758-
759-
let message = self.msg_with_padding(msg, max_line_num_len, "note");
760-
buffer.append(0, &message, Style::NoStyle);
804+
self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note", None);
761805
} else {
762806
buffer.append(0, &level.to_string(), Style::Level(level.clone()));
763807
match code {
@@ -769,7 +813,9 @@ impl EmitterWriter {
769813
_ => {}
770814
}
771815
buffer.append(0, ": ", Style::HeaderMsg);
772-
buffer.append(0, msg, Style::HeaderMsg);
816+
for &(ref text, _) in msg.iter() {
817+
buffer.append(0, text, Style::HeaderMsg);
818+
}
773819
}
774820

775821
// Preprocess all the annotations so that they are grouped by file and by line number
@@ -879,7 +925,7 @@ impl EmitterWriter {
879925
fn emit_suggestion_default(&mut self,
880926
suggestion: &CodeSuggestion,
881927
level: &Level,
882-
msg: &str,
928+
msg: &Vec<(String, Style)>,
883929
max_line_num_len: usize)
884930
-> io::Result<()> {
885931
use std::borrow::Borrow;
@@ -890,9 +936,11 @@ impl EmitterWriter {
890936

891937
buffer.append(0, &level.to_string(), Style::Level(level.clone()));
892938
buffer.append(0, ": ", Style::HeaderMsg);
893-
894-
let message = self.msg_with_padding(msg, max_line_num_len, "suggestion");
895-
buffer.append(0, &message, Style::HeaderMsg);
939+
self.msg_to_buffer(&mut buffer,
940+
msg,
941+
max_line_num_len,
942+
"suggestion",
943+
Some(Style::HeaderMsg));
896944

897945
let lines = cm.span_to_lines(primary_span).unwrap();
898946

@@ -921,7 +969,7 @@ impl EmitterWriter {
921969
}
922970
fn emit_messages_default(&mut self,
923971
level: &Level,
924-
message: &String,
972+
message: &Vec<(String, Style)>,
925973
code: &Option<String>,
926974
span: &MultiSpan,
927975
children: &Vec<SubDiagnostic>) {
@@ -942,7 +990,7 @@ impl EmitterWriter {
942990
match child.render_span {
943991
Some(FullSpan(ref msp)) => {
944992
match self.emit_message_default(msp,
945-
&child.message,
993+
&child.styled_message(),
946994
&None,
947995
&child.level,
948996
max_line_num_len,
@@ -954,15 +1002,15 @@ impl EmitterWriter {
9541002
Some(Suggestion(ref cs)) => {
9551003
match self.emit_suggestion_default(cs,
9561004
&child.level,
957-
&child.message,
1005+
&child.styled_message(),
9581006
max_line_num_len) {
9591007
Err(e) => panic!("failed to emit error: {}", e),
9601008
_ => ()
9611009
}
9621010
},
9631011
None => {
9641012
match self.emit_message_default(&child.span,
965-
&child.message,
1013+
&child.styled_message(),
9661014
&None,
9671015
&child.level,
9681016
max_line_num_len,
@@ -1197,6 +1245,7 @@ impl Destination {
11971245
self.start_attr(term::Attr::Bold)?;
11981246
self.start_attr(term::Attr::ForegroundColor(l.color()))?;
11991247
}
1248+
Style::Highlight => self.start_attr(term::Attr::Bold)?,
12001249
}
12011250
Ok(())
12021251
}

src/librustc_errors/snippet.rs

+1
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,5 @@ pub enum Style {
185185
NoStyle,
186186
ErrorCode,
187187
Level(Level),
188+
Highlight,
188189
}

src/librustc_trans/back/write.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,13 @@ impl SharedEmitter {
121121
impl Emitter for SharedEmitter {
122122
fn emit(&mut self, db: &DiagnosticBuilder) {
123123
self.buffer.lock().unwrap().push(Diagnostic {
124-
msg: db.message.to_string(),
124+
msg: db.message(),
125125
code: db.code.clone(),
126126
lvl: db.level,
127127
});
128128
for child in &db.children {
129129
self.buffer.lock().unwrap().push(Diagnostic {
130-
msg: child.message.to_string(),
130+
msg: child.message(),
131131
code: None,
132132
lvl: child.level,
133133
});

0 commit comments

Comments
 (0)