diff options
| author | Djzin <djzin@users.noreply.github.com> | 2017-05-27 14:31:47 +0100 |
|---|---|---|
| committer | Djzin <djzin@users.noreply.github.com> | 2017-05-27 14:31:47 +0100 |
| commit | 74751358e625878306aa193fed788e79aa53d4fa (patch) | |
| tree | 1ba9b336d1ddb45d9f688d69f5bd4ede028db622 /src/librustc_errors | |
| parent | c6307a2fa55c3d62c06b85b349257a8194093442 (diff) | |
| parent | 3e7908f616745573a11ad7dfad245f12be0069da (diff) | |
| download | rust-74751358e625878306aa193fed788e79aa53d4fa.tar.gz rust-74751358e625878306aa193fed788e79aa53d4fa.zip | |
Merge remote-tracking branch 'upstream/master' into fast-swap
Diffstat (limited to 'src/librustc_errors')
| -rw-r--r-- | src/librustc_errors/diagnostic.rs | 114 | ||||
| -rw-r--r-- | src/librustc_errors/diagnostic_builder.rs | 40 | ||||
| -rw-r--r-- | src/librustc_errors/emitter.rs | 401 | ||||
| -rw-r--r-- | src/librustc_errors/lib.rs | 140 | ||||
| -rw-r--r-- | src/librustc_errors/snippet.rs | 35 |
5 files changed, 480 insertions, 250 deletions
diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 1b77ead92de..861880aa265 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -9,9 +9,9 @@ // except according to those terms. use CodeSuggestion; +use Substitution; use Level; use RenderSpan; -use RenderSpan::Suggestion; use std::fmt; use syntax_pos::{MultiSpan, Span}; use snippet::Style; @@ -24,6 +24,7 @@ pub struct Diagnostic { pub code: Option<String>, pub span: MultiSpan, pub children: Vec<SubDiagnostic>, + pub suggestions: Vec<CodeSuggestion>, } /// For example a note attached to an error. @@ -35,6 +36,46 @@ pub struct SubDiagnostic { pub render_span: Option<RenderSpan>, } +#[derive(PartialEq, Eq)] +pub struct DiagnosticStyledString(pub Vec<StringPart>); + +impl DiagnosticStyledString { + pub fn new() -> DiagnosticStyledString { + DiagnosticStyledString(vec![]) + } + pub fn push_normal<S: Into<String>>(&mut self, t: S) { + self.0.push(StringPart::Normal(t.into())); + } + pub fn push_highlighted<S: Into<String>>(&mut self, t: S) { + self.0.push(StringPart::Highlighted(t.into())); + } + pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString { + DiagnosticStyledString(vec![StringPart::Normal(t.into())]) + } + + pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString { + DiagnosticStyledString(vec![StringPart::Highlighted(t.into())]) + } + + pub fn content(&self) -> String { + self.0.iter().map(|x| x.content()).collect::<String>() + } +} + +#[derive(PartialEq, Eq)] +pub enum StringPart { + Normal(String), + Highlighted(String), +} + +impl StringPart { + pub fn content(&self) -> String { + match self { + &StringPart::Normal(ref s) | & StringPart::Highlighted(ref s) => s.to_owned() + } + } +} + impl Diagnostic { pub fn new(level: Level, message: &str) -> Self { Diagnostic::new_with_code(level, None, message) @@ -47,6 +88,7 @@ impl Diagnostic { code: code, span: MultiSpan::new(), children: vec![], + suggestions: vec![], } } @@ -73,16 +115,15 @@ impl Diagnostic { /// all, and you just supplied a `Span` to create the diagnostic, /// then the snippet will just include that `Span`, which is /// called the primary span. - pub fn span_label(&mut self, span: Span, label: &fmt::Display) - -> &mut Self { - self.span.push_span_label(span, format!("{}", label)); + pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self { + self.span.push_span_label(span, label.into()); self } pub fn note_expected_found(&mut self, label: &fmt::Display, - expected: &fmt::Display, - found: &fmt::Display) + expected: DiagnosticStyledString, + found: DiagnosticStyledString) -> &mut Self { self.note_expected_found_extra(label, expected, found, &"", &"") @@ -90,21 +131,29 @@ impl Diagnostic { pub fn note_expected_found_extra(&mut self, label: &fmt::Display, - expected: &fmt::Display, - found: &fmt::Display, + expected: DiagnosticStyledString, + found: DiagnosticStyledString, expected_extra: &fmt::Display, found_extra: &fmt::Display) -> &mut Self { + let mut msg: Vec<_> = vec![(format!("expected {} `", label), Style::NoStyle)]; + msg.extend(expected.0.iter() + .map(|x| match *x { + StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), + StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), + })); + msg.push((format!("`{}\n", expected_extra), Style::NoStyle)); + msg.push((format!(" found {} `", label), Style::NoStyle)); + msg.extend(found.0.iter() + .map(|x| match *x { + StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), + StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), + })); + msg.push((format!("`{}", found_extra), Style::NoStyle)); + // For now, just attach these as notes - 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.highlighted_note(msg); self } @@ -154,19 +203,26 @@ impl Diagnostic { /// Prints out a message with a suggested edit of the code. /// - /// See `diagnostic::RenderSpan::Suggestion` for more information. - pub fn span_suggestion<S: Into<MultiSpan>>(&mut self, - sp: S, - msg: &str, - suggestion: String) - -> &mut Self { - self.sub(Level::Help, - msg, - MultiSpan::new(), - Some(Suggestion(CodeSuggestion { - msp: sp.into(), - substitutes: vec![suggestion], - }))); + /// See `diagnostic::CodeSuggestion` for more information. + pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitution_parts: vec![Substitution { + span: sp, + substitutions: vec![suggestion], + }], + msg: msg.to_owned(), + }); + self + } + + pub fn span_suggestions(&mut self, sp: Span, msg: &str, suggestions: Vec<String>) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitution_parts: vec![Substitution { + span: sp, + substitutions: suggestions, + }], + msg: msg.to_owned(), + }); self } diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index 7dfea6b8951..0081339a363 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -9,6 +9,8 @@ // except according to those terms. use Diagnostic; +use DiagnosticStyledString; + use Level; use Handler; use std::fmt::{self, Debug}; @@ -97,7 +99,10 @@ impl<'a> DiagnosticBuilder<'a> { self.handler.emitter.borrow_mut().emit(&self); self.cancel(); - self.handler.panic_if_treat_err_as_bug(); + + if self.level == Level::Error { + self.handler.panic_if_treat_err_as_bug(); + } // if self.is_fatal() { // panic!(FatalError); @@ -110,19 +115,21 @@ impl<'a> DiagnosticBuilder<'a> { /// all, and you just supplied a `Span` to create the diagnostic, /// then the snippet will just include that `Span`, which is /// called the primary span. - forward!(pub fn span_label(&mut self, span: Span, label: &fmt::Display) - -> &mut Self); + pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self { + self.diagnostic.span_label(span, label); + self + } forward!(pub fn note_expected_found(&mut self, label: &fmt::Display, - expected: &fmt::Display, - found: &fmt::Display) + expected: DiagnosticStyledString, + found: DiagnosticStyledString) -> &mut Self); forward!(pub fn note_expected_found_extra(&mut self, label: &fmt::Display, - expected: &fmt::Display, - found: &fmt::Display, + expected: DiagnosticStyledString, + found: DiagnosticStyledString, expected_extra: &fmt::Display, found_extra: &fmt::Display) -> &mut Self); @@ -139,11 +146,16 @@ impl<'a> DiagnosticBuilder<'a> { sp: S, msg: &str) -> &mut Self); - forward!(pub fn span_suggestion<S: Into<MultiSpan>>(&mut self, - sp: S, - msg: &str, - suggestion: String) - -> &mut Self); + forward!(pub fn span_suggestion(&mut self, + sp: Span, + msg: &str, + suggestion: String) + -> &mut Self); + forward!(pub fn span_suggestions(&mut self, + sp: Span, + msg: &str, + suggestions: Vec<String>) + -> &mut Self); forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self); forward!(pub fn code(&mut self, s: String) -> &mut Self); @@ -180,8 +192,8 @@ impl<'a> Debug for DiagnosticBuilder<'a> { } } -/// Destructor bomb - a DiagnosticBuilder must be either emitted or cancelled or -/// we emit a bug. +/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled +/// or we emit a bug. impl<'a> Drop for DiagnosticBuilder<'a> { fn drop(&mut self) { if !panicking() && !self.cancelled() { diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 431edb3c9bc..a9645f9ab7b 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -10,7 +10,7 @@ use self::Destination::*; -use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, CharPos}; +use syntax_pos::{DUMMY_SP, FileMap, Span, MultiSpan, CharPos}; use {Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, CodeMapper}; use RenderSpan::*; @@ -21,6 +21,8 @@ use std::io::prelude::*; use std::io; use std::rc::Rc; use term; +use std::collections::HashMap; +use std::cmp::min; /// Emitter trait for emitting errors. pub trait Emitter { @@ -32,6 +34,36 @@ impl Emitter for EmitterWriter { fn emit(&mut self, db: &DiagnosticBuilder) { let mut primary_span = db.span.clone(); let mut children = db.children.clone(); + + if let Some((sugg, rest)) = db.suggestions.split_first() { + if rest.is_empty() && + // don't display multipart suggestions as labels + sugg.substitution_parts.len() == 1 && + // don't display multi-suggestions as labels + sugg.substitutions() == 1 && + // don't display long messages as labels + sugg.msg.split_whitespace().count() < 10 && + // don't display multiline suggestions as labels + sugg.substitution_parts[0].substitutions[0].find('\n').is_none() { + let substitution = &sugg.substitution_parts[0].substitutions[0]; + let msg = format!("help: {} `{}`", sugg.msg, substitution); + primary_span.push_span_label(sugg.substitution_spans().next().unwrap(), msg); + } else { + // if there are multiple suggestions, print them all in full + // to be consistent. We could try to figure out if we can + // make one (or the first one) inline, but that would give + // undue importance to a semi-random suggestion + for sugg in &db.suggestions { + children.push(SubDiagnostic { + level: Level::Help, + message: Vec::new(), + span: MultiSpan::new(), + render_span: Some(Suggestion(sugg.clone())), + }); + } + } + } + self.fix_multispans_in_std_macros(&mut primary_span, &mut children); self.emit_messages_default(&db.level, &db.styled_message(), @@ -43,6 +75,10 @@ impl Emitter for EmitterWriter { /// maximum number of lines we will print for each error; arbitrary. pub const MAX_HIGHLIGHT_LINES: usize = 6; +/// maximum number of suggestions to be shown +/// +/// Arbitrary, but taken from trait import suggestion limit +pub const MAX_SUGGESTIONS: usize = 4; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ColorConfig { @@ -72,21 +108,6 @@ struct FileWithAnnotatedLines { multiline_depth: usize, } - -/// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See -/// `EmitterWriter::print_maybe_styled` for details. -macro_rules! print_maybe_styled { - ($dst: expr, $style: expr, $($arg: tt)*) => { - $dst.print_maybe_styled(format_args!($($arg)*), $style, false) - } -} - -macro_rules! println_maybe_styled { - ($dst: expr, $style: expr, $($arg: tt)*) => { - $dst.print_maybe_styled(format_args!($($arg)*), $style, true) - } -} - impl EmitterWriter { pub fn stderr(color_config: ColorConfig, code_map: Option<Rc<CodeMapper>>) -> EmitterWriter { if color_config.use_color() { @@ -151,20 +172,11 @@ impl EmitterWriter { if let Some(ref cm) = self.cm { for span_label in msp.span_labels() { - if span_label.span == DUMMY_SP || span_label.span == COMMAND_LINE_SP { + if span_label.span == DUMMY_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; - - // If the span is long multi-line, simplify down to the span of one character - let max_multiline_span_length = 8; - if lo.line != hi.line && (hi.line - lo.line) > max_multiline_span_length { - hi.line = lo.line; - hi.col = CharPos(lo.col.0 + 1); - is_minimized = true; - } // Watch out for "empty spans". If we get a span like 6..6, we // want to just display a `^` at 6, so convert that to @@ -175,16 +187,7 @@ impl EmitterWriter { hi.col = CharPos(lo.col.0 + 1); } - let mut ann = Annotation { - start_col: lo.col.0, - end_col: hi.col.0, - is_primary: span_label.is_primary, - label: span_label.label.clone(), - annotation_type: AnnotationType::Singleline, - }; - if is_minimized { - ann.annotation_type = AnnotationType::Minimized; - } else if lo.line != hi.line { + let ann_type = if lo.line != hi.line { let ml = MultilineAnnotation { depth: 1, line_start: lo.line, @@ -194,8 +197,17 @@ impl EmitterWriter { is_primary: span_label.is_primary, label: span_label.label.clone(), }; - ann.annotation_type = AnnotationType::Multiline(ml.clone()); - multiline_annotations.push((lo.file.clone(), ml)); + multiline_annotations.push((lo.file.clone(), ml.clone())); + AnnotationType::Multiline(ml) + } else { + AnnotationType::Singleline + }; + let ann = Annotation { + start_col: lo.col.0, + end_col: hi.col.0, + is_primary: span_label.is_primary, + label: span_label.label.clone(), + annotation_type: ann_type, }; if !ann.is_multiline() { @@ -233,9 +245,15 @@ impl EmitterWriter { max_depth = ann.depth; } add_annotation_to_file(&mut output, file.clone(), ann.line_start, ann.as_start()); - for line in ann.line_start + 1..ann.line_end { + let middle = min(ann.line_start + 4, ann.line_end); + for line in ann.line_start + 1..middle { add_annotation_to_file(&mut output, file.clone(), line, ann.as_line()); } + if middle < ann.line_end - 1 { + for line in ann.line_end - 1..ann.line_end { + add_annotation_to_file(&mut output, file.clone(), line, ann.as_line()); + } + } add_annotation_to_file(&mut output, file, ann.line_end, ann.as_end()); } for file_vec in output.iter_mut() { @@ -249,16 +267,13 @@ impl EmitterWriter { file: Rc<FileMap>, line: &Line, width_offset: usize, - multiline_depth: usize) { - let source_string = file.get_line(line.line_index - 1) - .unwrap_or(""); + code_offset: usize) -> Vec<(usize, Style)> { + let source_string = match file.get_line(line.line_index - 1) { + Some(s) => s, + None => return Vec::new(), + }; let line_offset = buffer.num_lines(); - let code_offset = if multiline_depth == 0 { - width_offset - } else { - width_offset + multiline_depth + 1 - }; // First create the source line we will highlight. buffer.puts(line_offset, code_offset, &source_string, Style::Quotation); @@ -269,6 +284,41 @@ impl EmitterWriter { draw_col_separator(buffer, line_offset, width_offset - 2); + // Special case when there's only one annotation involved, it is the start of a multiline + // span and there's no text at the beginning of the code line. Instead of doing the whole + // graph: + // + // 2 | fn foo() { + // | _^ + // 3 | | + // 4 | | } + // | |_^ test + // + // we simplify the output to: + // + // 2 | / fn foo() { + // 3 | | + // 4 | | } + // | |_^ test + if line.annotations.len() == 1 { + if let Some(ref ann) = line.annotations.get(0) { + if let AnnotationType::MultilineStart(depth) = ann.annotation_type { + if source_string[0..ann.start_col].trim() == "" { + let style = if ann.is_primary { + Style::UnderlinePrimary + } else { + Style::UnderlineSecondary + }; + buffer.putc(line_offset, + width_offset + depth - 1, + '/', + style); + return vec![(depth, style)]; + } + } + } + } + // We want to display like this: // // vec.push(vec.pop().unwrap()); @@ -286,7 +336,7 @@ impl EmitterWriter { // previous borrow of `vec` occurs here // // For this reason, we group the lines into "highlight lines" - // and "annotations lines", where the highlight lines have the `~`. + // and "annotations lines", where the highlight lines have the `^`. // Sort the annotations by (start, end col) let mut annotations = line.annotations.clone(); @@ -361,10 +411,8 @@ impl EmitterWriter { for (i, annotation) in annotations.iter().enumerate() { for (j, next) in annotations.iter().enumerate() { if overlaps(next, annotation, 0) // This label overlaps with another one and both - && !annotation.is_line() // take space (they have text and are not - && !next.is_line() // multiline lines). - && annotation.has_label() - && j > i + && annotation.has_label() // take space (they have text and are not + && j > i // multiline lines). && p == 0 // We're currently on the first line, move the label one line down { // This annotation needs a new line in the output. @@ -380,7 +428,7 @@ impl EmitterWriter { } else { 0 }; - if overlaps(next, annotation, l) // Do not allow two labels to be in the same + if (overlaps(next, annotation, l) // Do not allow two labels to be in the same // line if they overlap including padding, to // avoid situations like: // @@ -389,11 +437,18 @@ impl EmitterWriter { // | | // fn_spanx_span // - && !annotation.is_line() // Do not add a new line if this annotation - && !next.is_line() // or the next are vertical line placeholders. && annotation.has_label() // Both labels must have some text, otherwise - && next.has_label() // they are not overlapping. + && next.has_label()) // they are not overlapping. + // Do not add a new line if this annotation + // or the next are vertical line placeholders. + || (annotation.takes_space() // If either this or the next annotation is + && next.has_label()) // multiline start/end, move it to a new line + || (annotation.has_label() // so as not to overlap the orizontal lines. + && next.takes_space()) + || (annotation.takes_space() + && next.takes_space()) { + // This annotation needs a new line in the output. p += 1; break; } @@ -403,6 +458,7 @@ impl EmitterWriter { line_len = p; } } + if line_len != 0 { line_len += 1; } @@ -410,25 +466,9 @@ impl EmitterWriter { // If there are no annotations or the only annotations on this line are // MultilineLine, then there's only code being shown, stop processing. if line.annotations.is_empty() || line.annotations.iter() - .filter(|a| { - // Set the multiline annotation vertical lines to the left of - // the code in this line. - if let AnnotationType::MultilineLine(depth) = a.annotation_type { - buffer.putc(line_offset, - width_offset + depth - 1, - '|', - if a.is_primary { - Style::UnderlinePrimary - } else { - Style::UnderlineSecondary - }); - false - } else { - true - } - }).collect::<Vec<_>>().len() == 0 + .filter(|a| !a.is_line()).collect::<Vec<_>>().len() == 0 { - return; + return vec![]; } // Write the colunmn separator. @@ -483,8 +523,7 @@ impl EmitterWriter { } } - // Write the vertical lines for multiline spans and for labels that are - // on a different line as the underline. + // Write the vertical lines for labels that are on a different line as the underline. // // After this we will have: // @@ -492,7 +531,7 @@ impl EmitterWriter { // | __________ // | | | // | | - // 3 | | + // 3 | // 4 | | } // | |_ for &(pos, annotation) in &annotations_position { @@ -503,7 +542,7 @@ impl EmitterWriter { }; let pos = pos + 1; - if pos > 1 && annotation.has_label() { + if pos > 1 && (annotation.has_label() || annotation.takes_space()) { for p in line_offset + 1..line_offset + pos + 1 { buffer.putc(p, code_offset + annotation.start_col, @@ -528,16 +567,6 @@ impl EmitterWriter { style); } } - AnnotationType::MultilineLine(depth) => { - // the first line will have already be filled when we checked - // wether there were any annotations for this line. - for p in line_offset + 1..line_offset + line_len + 2 { - buffer.putc(p, - width_offset + depth - 1, - '|', - style); - } - } _ => (), } } @@ -547,12 +576,12 @@ impl EmitterWriter { // After this we will have: // // 2 | fn foo() { - // | __________ starting here... - // | | | - // | | something about `foo` - // 3 | | - // 4 | | } - // | |_ ...ending here: test + // | __________ + // | | + // | something about `foo` + // 3 | + // 4 | } + // | _ test for &(pos, annotation) in &annotations_position { let style = if annotation.is_primary { Style::LabelPrimary @@ -590,12 +619,12 @@ impl EmitterWriter { // After this we will have: // // 2 | fn foo() { - // | ____-_____^ starting here... - // | | | - // | | something about `foo` - // 3 | | - // 4 | | } - // | |_^ ...ending here: test + // | ____-_____^ + // | | + // | something about `foo` + // 3 | + // 4 | } + // | _^ test for &(_, annotation) in &annotations_position { let (underline, style) = if annotation.is_primary { ('^', Style::UnderlinePrimary) @@ -609,13 +638,27 @@ impl EmitterWriter { style); } } + annotations_position.iter().filter_map(|&(_, annotation)| { + match annotation.annotation_type { + AnnotationType::MultilineStart(p) | AnnotationType::MultilineEnd(p) => { + let style = if annotation.is_primary { + Style::LabelPrimary + } else { + Style::LabelSecondary + }; + Some((p, style)) + }, + _ => None + } + + }).collect::<Vec<_>>() } fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize { let mut max = 0; if let Some(ref cm) = self.cm { for primary_span in msp.primary_spans() { - if primary_span != &DUMMY_SP && primary_span != &COMMAND_LINE_SP { + if primary_span != &DUMMY_SP { let hi = cm.lookup_char_pos(primary_span.hi); if hi.line > max { max = hi.line; @@ -623,7 +666,7 @@ impl EmitterWriter { } } for span_label in msp.span_labels() { - if span_label.span != DUMMY_SP && span_label.span != COMMAND_LINE_SP { + if span_label.span != DUMMY_SP { let hi = cm.lookup_char_pos(span_label.span.hi); if hi.line > max { max = hi.line; @@ -659,20 +702,20 @@ impl EmitterWriter { // First, find all the spans in <*macros> and point instead at their use site for sp in span.primary_spans() { - if (*sp == COMMAND_LINE_SP) || (*sp == DUMMY_SP) { + if *sp == DUMMY_SP { continue; } if cm.span_to_filename(sp.clone()).contains("macros>") { - let v = cm.macro_backtrace(sp.clone()); + let v = sp.macro_backtrace(); if let Some(use_site) = v.last() { before_after.push((sp.clone(), use_site.call_site.clone())); } } - for trace in cm.macro_backtrace(sp.clone()).iter().rev() { + for trace in sp.macro_backtrace().iter().rev() { // Only show macro locations that are local // and display them like a span_note if let Some(def_site) = trace.def_site_span { - if (def_site == COMMAND_LINE_SP) || (def_site == DUMMY_SP) { + if def_site == DUMMY_SP { continue; } // Check to make sure we're not in any <*macros> @@ -689,11 +732,11 @@ impl EmitterWriter { span.push_span_label(label_span, label_text); } for sp_label in span.span_labels() { - if (sp_label.span == COMMAND_LINE_SP) || (sp_label.span == DUMMY_SP) { + if sp_label.span == DUMMY_SP { continue; } if cm.span_to_filename(sp_label.span.clone()).contains("macros>") { - let v = cm.macro_backtrace(sp_label.span.clone()); + let v = sp_label.span.macro_backtrace(); if let Some(use_site) = v.last() { before_after.push((sp_label.span.clone(), use_site.call_site.clone())); } @@ -734,7 +777,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<Style>) { @@ -848,7 +891,7 @@ impl EmitterWriter { // Make sure our primary file comes first let primary_lo = if let (Some(ref cm), Some(ref primary_span)) = (self.cm.as_ref(), msp.primary_span().as_ref()) { - if primary_span != &&DUMMY_SP && primary_span != &&COMMAND_LINE_SP { + if primary_span != &&DUMMY_SP { cm.lookup_char_pos(primary_span.lo) } else { emit_to_destination(&buffer.render(), level, &mut self.dst)?; @@ -866,6 +909,11 @@ impl EmitterWriter { // Print out the annotate source lines that correspond with the error for annotated_file in annotated_files { + // we can't annotate anything if the source is unavailable. + if annotated_file.file.src.is_none() { + continue; + } + // print out the span location and spacer before we print the annotated source // to do this, we need to know if this span will be primary let is_primary = primary_lo.file.name == annotated_file.file.name; @@ -902,22 +950,64 @@ impl EmitterWriter { let buffer_msg_line_offset = buffer.num_lines(); draw_col_separator_no_space(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); + // Contains the vertical lines' positions for active multiline annotations + let mut multilines = HashMap::new(); + // Next, output the annotate source for this file for line_idx in 0..annotated_file.lines.len() { - self.render_source_line(&mut buffer, - annotated_file.file.clone(), - &annotated_file.lines[line_idx], - 3 + max_line_num_len, - annotated_file.multiline_depth); + let previous_buffer_line = buffer.num_lines(); + + let width_offset = 3 + max_line_num_len; + let code_offset = if annotated_file.multiline_depth == 0 { + width_offset + } else { + width_offset + annotated_file.multiline_depth + 1 + }; + let depths = self.render_source_line(&mut buffer, + annotated_file.file.clone(), + &annotated_file.lines[line_idx], + width_offset, + code_offset); + + let mut to_add = HashMap::new(); + + for (depth, style) in depths { + if multilines.get(&depth).is_some() { + multilines.remove(&depth); + } else { + to_add.insert(depth, style); + } + } + + // Set the multiline annotation vertical lines to the left of + // the code in this line. + for (depth, style) in &multilines { + for line in previous_buffer_line..buffer.num_lines() { + draw_multiline_line(&mut buffer, + line, + width_offset, + *depth, + *style); + } + } // check to see if we need to print out or elide lines that come between - // this annotated line and the next one + // this annotated line and the next one. if line_idx < (annotated_file.lines.len() - 1) { let line_idx_delta = annotated_file.lines[line_idx + 1].line_index - annotated_file.lines[line_idx].line_index; if line_idx_delta > 2 { let last_buffer_line_num = buffer.num_lines(); buffer.puts(last_buffer_line_num, 0, "...", Style::LineNumber); + + // Set the multiline annotation vertical lines on `...` bridging line. + for (depth, style) in &multilines { + draw_multiline_line(&mut buffer, + last_buffer_line_num, + width_offset, + *depth, + *style); + } } else if line_idx_delta == 2 { let unannotated_line = annotated_file.file .get_line(annotated_file.lines[line_idx].line_index) @@ -932,11 +1022,21 @@ impl EmitterWriter { Style::LineNumber); draw_col_separator(&mut buffer, last_buffer_line_num, 1 + max_line_num_len); buffer.puts(last_buffer_line_num, - 3 + max_line_num_len, + code_offset, &unannotated_line, Style::Quotation); + + for (depth, style) in &multilines { + draw_multiline_line(&mut buffer, + last_buffer_line_num, + width_offset, + *depth, + *style); + } } } + + multilines.extend(&to_add); } } @@ -948,43 +1048,48 @@ impl EmitterWriter { fn emit_suggestion_default(&mut self, suggestion: &CodeSuggestion, level: &Level, - msg: &Vec<(String, Style)>, max_line_num_len: usize) -> io::Result<()> { use std::borrow::Borrow; - let primary_span = suggestion.msp.primary_span().unwrap(); + let primary_span = suggestion.substitution_spans().next().unwrap(); if let Some(ref cm) = self.cm { let mut buffer = StyledBuffer::new(); - buffer.append(0, &level.to_string(), Style::Level(level.clone())); - buffer.append(0, ": ", 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(); assert!(!lines.lines.is_empty()); - let complete = suggestion.splice_lines(cm.borrow()); + buffer.append(0, &level.to_string(), Style::Level(level.clone())); + buffer.append(0, ": ", Style::HeaderMsg); + self.msg_to_buffer(&mut buffer, + &[(suggestion.msg.to_owned(), Style::NoStyle)], + max_line_num_len, + "suggestion", + Some(Style::HeaderMsg)); - // print the suggestion without any line numbers, but leave - // space for them. This helps with lining up with previous - // snippets from the actual error being reported. - let mut lines = complete.lines(); + let suggestions = suggestion.splice_lines(cm.borrow()); let mut row_num = 1; - for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { - draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); - buffer.append(row_num, line, Style::NoStyle); - row_num += 1; - } + for complete in suggestions.iter().take(MAX_SUGGESTIONS) { + + // print the suggestion without any line numbers, but leave + // space for them. This helps with lining up with previous + // snippets from the actual error being reported. + let mut lines = complete.lines(); + for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { + draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); + buffer.append(row_num, line, Style::NoStyle); + row_num += 1; + } - // if we elided some lines, add an ellipsis - if let Some(_) = lines.next() { - buffer.append(row_num, "...", Style::NoStyle); + // if we elided some lines, add an ellipsis + if let Some(_) = lines.next() { + buffer.append(row_num, "...", Style::NoStyle); + } + } + if suggestions.len() > MAX_SUGGESTIONS { + let msg = format!("and {} other candidates", suggestions.len() - MAX_SUGGESTIONS); + buffer.append(row_num, &msg, Style::NoStyle); } emit_to_destination(&buffer.render(), level, &mut self.dst)?; } @@ -1025,7 +1130,6 @@ impl EmitterWriter { Some(Suggestion(ref cs)) => { match self.emit_suggestion_default(cs, &child.level, - &child.styled_message(), max_line_num_len) { Err(e) => panic!("failed to emit error: {}", e), _ => () @@ -1085,6 +1189,15 @@ fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { buffer.puts(line, col, "= ", Style::LineNumber); } +fn draw_multiline_line(buffer: &mut StyledBuffer, + line: usize, + offset: usize, + depth: usize, + style: Style) +{ + buffer.putc(line, offset + depth - 1, '|', style); +} + fn num_overlap(a_start: usize, a_end: usize, b_start: usize, b_end:usize, inclusive: bool) -> bool { let extra = if inclusive { 1 @@ -1183,10 +1296,8 @@ impl Write for BufferedWriter { } fn flush(&mut self) -> io::Result<()> { let mut stderr = io::stderr(); - let result = (|| { - stderr.write_all(&self.buffer)?; - stderr.flush() - })(); + let result = stderr.write_all(&self.buffer) + .and_then(|_| stderr.flush()); self.buffer.clear(); result } diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 4c889dad8ca..f7191e49216 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -9,7 +9,6 @@ // except according to those terms. #![crate_name = "rustc_errors"] -#![unstable(feature = "rustc_private", issue = "27812")] #![crate_type = "dylib"] #![crate_type = "rlib"] #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", @@ -19,10 +18,13 @@ #![feature(custom_attribute)] #![allow(unused_attributes)] -#![feature(rustc_private)] -#![feature(staged_api)] #![feature(range_contains)] #![feature(libc)] +#![feature(conservative_impl_trait)] + +#![cfg_attr(stage0, unstable(feature = "rustc_private", issue = "27812"))] +#![cfg_attr(stage0, feature(rustc_private))] +#![cfg_attr(stage0, feature(staged_api))] extern crate term; extern crate libc; @@ -48,7 +50,6 @@ pub mod styled_buffer; mod lock; use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION}; -use syntax_pos::MacroBacktrace; #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] pub enum RenderSpan { @@ -66,8 +67,33 @@ pub enum RenderSpan { #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] pub struct CodeSuggestion { - pub msp: MultiSpan, - pub substitutes: Vec<String>, + /// Each substitute can have multiple variants due to multiple + /// applicable suggestions + /// + /// `foo.bar` might be replaced with `a.b` or `x.y` by replacing + /// `foo` and `bar` on their own: + /// + /// ``` + /// vec![ + /// (0..3, vec!["a", "x"]), + /// (4..7, vec!["b", "y"]), + /// ] + /// ``` + /// + /// or by replacing the entire span: + /// + /// ``` + /// vec![(0..7, vec!["a.b", "x.y"])] + /// ``` + pub substitution_parts: Vec<Substitution>, + pub msg: String, +} + +#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] +/// See the docs on `CodeSuggestion::substitutions` +pub struct Substitution { + pub span: Span, + pub substitutions: Vec<String>, } pub trait CodeMapper { @@ -75,13 +101,22 @@ pub trait CodeMapper { fn span_to_lines(&self, sp: Span) -> FileLinesResult; fn span_to_string(&self, sp: Span) -> String; fn span_to_filename(&self, sp: Span) -> FileName; - fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace>; fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>; } impl CodeSuggestion { - /// Returns the assembled code suggestion. - pub fn splice_lines(&self, cm: &CodeMapper) -> String { + /// Returns the number of substitutions + fn substitutions(&self) -> usize { + self.substitution_parts[0].substitutions.len() + } + + /// Returns the number of substitutions + pub fn substitution_spans<'a>(&'a self) -> impl Iterator<Item = Span> + 'a { + self.substitution_parts.iter().map(|sub| sub.span) + } + + /// Returns the assembled code suggestions. + pub fn splice_lines(&self, cm: &CodeMapper) -> Vec<String> { use syntax_pos::{CharPos, Loc, Pos}; fn push_trailing(buf: &mut String, @@ -103,24 +138,26 @@ impl CodeSuggestion { } } - let mut primary_spans = self.msp.primary_spans().to_owned(); - - assert_eq!(primary_spans.len(), self.substitutes.len()); - if primary_spans.is_empty() { - return format!(""); + if self.substitution_parts.is_empty() { + return vec![String::new()]; } + let mut primary_spans: Vec<_> = self.substitution_parts + .iter() + .map(|sub| (sub.span, &sub.substitutions)) + .collect(); + // Assumption: all spans are in the same file, and all spans // are disjoint. Sort in ascending order. - primary_spans.sort_by_key(|sp| sp.lo); + primary_spans.sort_by_key(|sp| sp.0.lo); // Find the bounding span. - let lo = primary_spans.iter().map(|sp| sp.lo).min().unwrap(); - let hi = primary_spans.iter().map(|sp| sp.hi).min().unwrap(); + let lo = primary_spans.iter().map(|sp| sp.0.lo).min().unwrap(); + let hi = primary_spans.iter().map(|sp| sp.0.hi).min().unwrap(); let bounding_span = Span { lo: lo, hi: hi, - expn_id: NO_EXPANSION, + ctxt: NO_EXPANSION, }; let lines = cm.span_to_lines(bounding_span).unwrap(); assert!(!lines.lines.is_empty()); @@ -139,33 +176,40 @@ impl CodeSuggestion { prev_hi.col = CharPos::from_usize(0); let mut prev_line = fm.get_line(lines.lines[0].line_index); - let mut buf = String::new(); + let mut bufs = vec![String::new(); self.substitutions()]; - for (sp, substitute) in primary_spans.iter().zip(self.substitutes.iter()) { + for (sp, substitutes) in primary_spans { let cur_lo = cm.lookup_char_pos(sp.lo); - if prev_hi.line == cur_lo.line { - push_trailing(&mut buf, prev_line, &prev_hi, Some(&cur_lo)); - } else { - push_trailing(&mut buf, prev_line, &prev_hi, None); - // push lines between the previous and current span (if any) - for idx in prev_hi.line..(cur_lo.line - 1) { - if let Some(line) = fm.get_line(idx) { - buf.push_str(line); - buf.push('\n'); + for (buf, substitute) in bufs.iter_mut().zip(substitutes) { + if prev_hi.line == cur_lo.line { + push_trailing(buf, prev_line, &prev_hi, Some(&cur_lo)); + } else { + push_trailing(buf, prev_line, &prev_hi, None); + // push lines between the previous and current span (if any) + for idx in prev_hi.line..(cur_lo.line - 1) { + if let Some(line) = fm.get_line(idx) { + buf.push_str(line); + buf.push('\n'); + } + } + if let Some(cur_line) = fm.get_line(cur_lo.line - 1) { + buf.push_str(&cur_line[..cur_lo.col.to_usize()]); } } - if let Some(cur_line) = fm.get_line(cur_lo.line - 1) { - buf.push_str(&cur_line[..cur_lo.col.to_usize()]); - } + buf.push_str(substitute); } - buf.push_str(substitute); prev_hi = cm.lookup_char_pos(sp.hi); prev_line = fm.get_line(prev_hi.line - 1); } - push_trailing(&mut buf, prev_line, &prev_hi, None); - // remove trailing newline - buf.pop(); - buf + for buf in &mut bufs { + // if the replacement already ends with a newline, don't print the next line + if !buf.ends_with('\n') { + push_trailing(buf, prev_line, &prev_hi, None); + } + // remove trailing newline + buf.pop(); + } + bufs } } @@ -205,7 +249,7 @@ impl error::Error for ExplicitBug { } } -pub use diagnostic::{Diagnostic, SubDiagnostic}; +pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString, StringPart}; pub use diagnostic_builder::DiagnosticBuilder; /// A handler deals with errors; certain errors @@ -339,7 +383,7 @@ impl Handler { pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> FatalError { self.emit(&sp.into(), msg, Fatal); self.panic_if_treat_err_as_bug(); - return FatalError; + FatalError } pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self, sp: S, @@ -348,7 +392,7 @@ impl Handler { -> FatalError { self.emit_with_code(&sp.into(), msg, code, Fatal); self.panic_if_treat_err_as_bug(); - return FatalError; + FatalError } pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { self.emit(&sp.into(), msg, Error); @@ -377,6 +421,9 @@ impl Handler { panic!(ExplicitBug); } pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { + if self.treat_err_as_bug { + self.span_bug(sp, msg); + } let mut delayed = self.delayed_span_bug.borrow_mut(); *delayed = Some((sp.into(), msg.to_string())); } @@ -386,6 +433,14 @@ impl Handler { pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { self.emit(&sp.into(), msg, Note); } + pub fn span_note_diag<'a>(&'a self, + sp: Span, + msg: &str) + -> DiagnosticBuilder<'a> { + let mut db = DiagnosticBuilder::new(self, Note, msg); + db.set_span(sp); + db + } pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! { self.span_bug(sp, &format!("unimplemented {}", msg)); } @@ -446,10 +501,7 @@ impl Handler { return; } - 1 => s = "aborting due to previous error".to_string(), - _ => { - s = format!("aborting due to {} previous errors", self.err_count.get()); - } + _ => s = "aborting due to previous error(s)".to_string(), } panic!(self.fatal(&s)); diff --git a/src/librustc_errors/snippet.rs b/src/librustc_errors/snippet.rs index 5debbf4d37c..7401ead2208 100644 --- a/src/librustc_errors/snippet.rs +++ b/src/librustc_errors/snippet.rs @@ -63,7 +63,7 @@ impl MultilineAnnotation { start_col: self.start_col, end_col: self.start_col + 1, is_primary: self.is_primary, - label: Some("starting here...".to_owned()), + label: None, annotation_type: AnnotationType::MultilineStart(self.depth) } } @@ -73,10 +73,7 @@ impl MultilineAnnotation { start_col: self.end_col - 1, end_col: self.end_col, is_primary: self.is_primary, - label: match self.label { - Some(ref label) => Some(format!("...ending here: {}", label)), - None => Some("...ending here".to_owned()), - }, + label: self.label.clone(), annotation_type: AnnotationType::MultilineEnd(self.depth) } } @@ -97,9 +94,6 @@ pub enum AnnotationType { /// Annotation under a single line of code Singleline, - /// Annotation under the first character of a multiline span - Minimized, - /// Annotation enclosing the first and last character of a multiline span Multiline(MultilineAnnotation), @@ -109,15 +103,18 @@ pub enum AnnotationType { // Each of these corresponds to one part of the following diagram: // // x | foo(1 + bar(x, - // | _________^ starting here... < MultilineStart - // x | | y), < MultilineLine - // | |______________^ ...ending here: label < MultilineEnd + // | _________^ < MultilineStart + // x | | y), < MultilineLine + // | |______________^ label < MultilineEnd // x | z); /// Annotation marking the first character of a fully shown multiline span MultilineStart(usize), /// Annotation marking the last character of a fully shown multiline span MultilineEnd(usize), /// Line at the left enclosing the lines of a fully shown multiline span + // Just a placeholder for the drawing algorithm, to know that it shouldn't skip the first 4 + // and last 2 lines of code. The actual line is drawn in `emit_message_default` and not in + // `draw_multiline_line`. MultilineLine(usize), } @@ -144,13 +141,6 @@ pub struct Annotation { } impl Annotation { - pub fn is_minimized(&self) -> bool { - match self.annotation_type { - AnnotationType::Minimized => true, - _ => false, - } - } - /// Wether this annotation is a vertical line placeholder. pub fn is_line(&self) -> bool { if let AnnotationType::MultilineLine(_) = self.annotation_type { @@ -196,6 +186,15 @@ impl Annotation { false } } + + pub fn takes_space(&self) -> bool { + // Multiline annotations always have to keep vertical space. + match self.annotation_type { + AnnotationType::MultilineStart(_) | + AnnotationType::MultilineEnd(_) => true, + _ => false, + } + } } #[derive(Debug)] |
