diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 6c079db3e236d..1b70cbd2a3b68 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -10,8 +10,6 @@ use CodeSuggestion; use Level; -use RenderSpan; -use RenderSpan::{Suggestion, Guesses}; use std::{fmt, iter}; use syntax_pos::{MultiSpan, Span}; use snippet::Style; @@ -24,6 +22,19 @@ pub struct Diagnostic { pub code: Option, pub span: MultiSpan, pub children: Vec, + pub code_hints: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum DiagnosticCodeHint { + Suggestion { + msg: String, + sugg: CodeSuggestion, + }, + Guesses { + msg: String, + guesses: Vec, + }, } /// For example a note attached to an error. @@ -32,7 +43,7 @@ pub struct SubDiagnostic { pub level: Level, pub message: Vec<(String, Style)>, pub span: MultiSpan, - pub render_span: Option, + pub render_span: Option, } impl Diagnostic { @@ -47,6 +58,7 @@ impl Diagnostic { code: code, span: MultiSpan::new(), children: vec![], + code_hints: None, } } @@ -109,12 +121,12 @@ impl Diagnostic { } pub fn note(&mut self, msg: &str) -> &mut Self { - self.sub(Level::Note, msg, MultiSpan::new(), None); + self.sub(Level::Note, msg, MultiSpan::new()); self } pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self { - self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None); + self.sub_with_highlights(Level::Note, msg, MultiSpan::new()); self } @@ -122,12 +134,12 @@ impl Diagnostic { sp: S, msg: &str) -> &mut Self { - self.sub(Level::Note, msg, sp.into(), None); + self.sub(Level::Note, msg, sp.into()); self } pub fn warn(&mut self, msg: &str) -> &mut Self { - self.sub(Level::Warning, msg, MultiSpan::new(), None); + self.sub(Level::Warning, msg, MultiSpan::new()); self } @@ -135,12 +147,12 @@ impl Diagnostic { sp: S, msg: &str) -> &mut Self { - self.sub(Level::Warning, msg, sp.into(), None); + self.sub(Level::Warning, msg, sp.into()); self } pub fn help(&mut self , msg: &str) -> &mut Self { - self.sub(Level::Help, msg, MultiSpan::new(), None); + self.sub(Level::Help, msg, MultiSpan::new()); self } @@ -148,7 +160,7 @@ impl Diagnostic { sp: S, msg: &str) -> &mut Self { - self.sub(Level::Help, msg, sp.into(), None); + self.sub(Level::Help, msg, sp.into()); self } @@ -156,13 +168,15 @@ impl Diagnostic { /// /// See `diagnostic::RenderSpan::Suggestion` for more information. pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self { - self.sub(Level::Help, - msg, - MultiSpan::new(), - Some(Suggestion(CodeSuggestion { - msp: sp.into(), - substitutes: vec![suggestion], - }))); + assert!(self.code_hints.is_none(), + "use guesses to assign multiple suggestions to an error"); + self.code_hints = Some(DiagnosticCodeHint::Suggestion { + sugg: CodeSuggestion { + msp: sp.into(), + substitutes: vec![suggestion], + }, + msg: msg.to_owned(), + }); self } @@ -174,13 +188,16 @@ impl Diagnostic { pub fn guesses(&mut self, sp: Span, msg: &str, guesses: I) -> &mut Self where I: IntoIterator { - self.sub(Level::Help, - msg, - MultiSpan::new(), - Some(Guesses(guesses.into_iter().map(|guess| CodeSuggestion { - msp: sp.into(), - substitutes: vec![guess], - }).collect()))); + assert!(self.code_hints.is_none(), + "cannot attach multiple guesses to the same error"); + let guesses = guesses.into_iter().map(|guess| CodeSuggestion { + msp: sp.into(), + substitutes: vec![guess], + }).collect(); + self.code_hints = Some(DiagnosticCodeHint::Guesses { + guesses: guesses, + msg: msg.to_owned(), + }); self } @@ -223,15 +240,12 @@ impl Diagnostic { fn sub(&mut self, level: Level, message: &str, - span: MultiSpan, - render_span: Option) { - let sub = SubDiagnostic { - level: level, - message: vec![(message.to_owned(), Style::NoStyle)], - span: span, - render_span: render_span, - }; - self.children.push(sub); + span: MultiSpan) { + self.sub_with_highlights( + level, + vec![(message.to_owned(), Style::NoStyle)], + span, + ); } /// Convenience function for internal use, clients should use one of the @@ -239,13 +253,12 @@ impl Diagnostic { fn sub_with_highlights(&mut self, level: Level, message: Vec<(String, Style)>, - span: MultiSpan, - render_span: Option) { + span: MultiSpan) { let sub = SubDiagnostic { level: level, message: message, span: span, - render_span: render_span, + render_span: None, }; self.children.push(sub); } diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index d1c718d157e1f..d5784d32c6bb3 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -10,10 +10,9 @@ use self::Destination::*; -use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, CharPos}; +use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, CharPos, SpanLabel}; -use {Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, CodeMapper}; -use RenderSpan::*; +use {Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, CodeMapper, DiagnosticCodeHint}; use snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledString, Style}; use styled_buffer::StyledBuffer; @@ -37,7 +36,8 @@ impl Emitter for EmitterWriter { &db.styled_message(), &db.code, &primary_span, - &children); + &children, + db.code_hints.as_ref()); } } @@ -110,7 +110,10 @@ impl EmitterWriter { } } - fn preprocess_annotations(&self, msp: &MultiSpan) -> Vec { + fn preprocess_annotations(&self, + msp: &MultiSpan, + code_hint: &mut Option<&DiagnosticCodeHint>) + -> Vec { fn add_annotation_to_file(file_vec: &mut Vec, file: Rc, line_index: usize, @@ -149,11 +152,49 @@ impl EmitterWriter { let mut output = vec![]; let mut multiline_annotations = vec![]; + let hint = code_hint.and_then(|hint| match *hint { + DiagnosticCodeHint::Suggestion { ref msg, ref sugg } => Some((msg, sugg)), + DiagnosticCodeHint::Guesses { ref msg, ref guesses} => { + if guesses.len() == 1 { + Some((msg, &guesses[0])) + } else { + None + } + }, + }); + let mut hint = hint.and_then(|(msg, sugg)| { + if sugg.substitutes.len() == 1 && !sugg.substitutes[0].is_empty() { + let prim = sugg.msp.primary_spans(); + assert_eq!(prim.len(), 1); + Some(SpanLabel { + span: prim[0], + is_primary: true, + label: Some(format!("{} `{}`", msg, sugg.substitutes[0])), + }) + } else { + None + } + }); + if let Some(ref cm) = self.cm { - for span_label in msp.span_labels() { + let mut span_labels = msp.span_labels(); + // insert the hint into the correct label + for mut span_label in &mut span_labels { + if let Some(hint_label) = hint.take() { + if span_label.span == hint_label.span { + span_label.label = hint_label.label; + *code_hint = None; + } else { + hint = Some(hint_label); + } + } + } + // if no matching label found, print hint as an extra message + for span_label in span_labels.into_iter().chain(hint) { if span_label.span == DUMMY_SP || span_label.span == COMMAND_LINE_SP { continue; } + let lo = cm.lookup_char_pos(span_label.span.lo); let mut hi = cm.lookup_char_pos(span_label.span.hi); let mut is_minimized = false; @@ -728,7 +769,7 @@ impl EmitterWriter { /// displayed, keeping the provided highlighting. fn msg_to_buffer(&self, buffer: &mut StyledBuffer, - msg: &Vec<(String, Style)>, + msg: &[(String, Style)], padding: usize, label: &str, override_style: Option