use registry;
use check_old_skool;
-use {Level, RenderSpan, CodeSuggestion, DiagnosticBuilder, CodeMapper};
+use {Level, CodeSuggestion, DiagnosticBuilder, CodeMapper};
use RenderSpan::*;
-use Level::*;
-use snippet::{SnippetData, StyledString, Style, FormatMode, Annotation, Line};
+use snippet::{StyledString, Style, FormatMode, Annotation, Line};
use styled_buffer::StyledBuffer;
-use std::{cmp, fmt};
+use std::cmp;
use std::io::prelude::*;
use std::io;
use std::rc::Rc;
registry: Option<registry::Registry>,
cm: Option<Rc<CodeMapper>>,
- /// Is this the first error emitted thus far? If not, we emit a
- /// `\n` before the top-level errors.
- first: bool,
-
// For now, allow an old-school mode while we transition
format_mode: FormatMode
}
EmitterWriter { dst: dst,
registry: registry,
cm: code_map,
- first: true,
format_mode: format_mode.clone() }
} else {
EmitterWriter { dst: Raw(Box::new(io::stderr())),
registry: registry,
cm: code_map,
- first: true,
format_mode: format_mode.clone() }
}
}
EmitterWriter { dst: Raw(dst),
registry: registry,
cm: code_map,
- first: true,
format_mode: format_mode.clone() }
}
- fn emit_message(&mut self,
- rsp: &RenderSpan,
- msg: &str,
- code: Option<&str>,
- lvl: Level,
- is_header: bool,
- show_snippet: bool) {
- match self.emit_message_(rsp, msg, code, lvl, is_header, show_snippet) {
- Ok(()) => { }
- Err(e) => panic!("failed to emit error: {}", e)
- }
- }
-
fn preprocess_annotations(&self, msp: &MultiSpan) -> Vec<FileWithAnnotatedLines> {
fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
file: Rc<FileMap>,
if let Some(ref cm) = self.cm {
for span_label in msp.span_labels() {
- let mut lo = cm.lookup_char_pos(span_label.span.lo);
+ 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 msp.primary_spans().is_empty() && msp.span_labels().is_empty() && is_secondary {
// This is a secondary message with no span info
- for i in 0..max_line_num_len {
+ for _ in 0..max_line_num_len {
buffer.prepend(0, " ", Style::NoStyle);
}
draw_note_separator(&mut buffer, 0, max_line_num_len + 1);
cm.lookup_char_pos(primary_span.lo)
} else {
// If we don't have span information, emit and exit
- emit_to_destination(&buffer.render(), level, &mut self.dst);
+ emit_to_destination(&buffer.render(), level, &mut self.dst)?;
return Ok(());
};
if let Ok(pos) =
let is_primary = primary_lo.file.name == annotated_file.file.name;
if is_primary {
// remember where we are in the output buffer for easy reference
- let mut buffer_msg_line_offset = buffer.num_lines();
+ let buffer_msg_line_offset = buffer.num_lines();
buffer.prepend(buffer_msg_line_offset, "--> ", Style::LineNumber);
let loc = primary_lo.clone();
buffer.append(buffer_msg_line_offset,
&format!("{}:{}:{}", loc.file.name, loc.line, loc.col.0 + 1),
Style::LineAndColumn);
- for i in 0..max_line_num_len {
+ for _ in 0..max_line_num_len {
buffer.prepend(buffer_msg_line_offset, " ", Style::NoStyle);
}
} else {
// remember where we are in the output buffer for easy reference
- let mut buffer_msg_line_offset = buffer.num_lines();
+ let buffer_msg_line_offset = buffer.num_lines();
// Add spacing line
draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1);
buffer.append(buffer_msg_line_offset + 1,
&annotated_file.file.name,
Style::LineAndColumn);
- for i in 0..max_line_num_len {
+ for _ in 0..max_line_num_len {
buffer.prepend(buffer_msg_line_offset + 1, " ", Style::NoStyle);
}
}
// Put in the spacer between the location and annotated source
- let mut buffer_msg_line_offset = buffer.num_lines();
+ let buffer_msg_line_offset = buffer.num_lines();
draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1);
// Next, output the annotate source for this file
}
// final step: take our styled buffer, render it, then output it
- emit_to_destination(&buffer.render(), level, &mut self.dst);
+ emit_to_destination(&buffer.render(), level, &mut self.dst)?;
Ok(())
}
assert!(!lines.lines.is_empty());
let complete = suggestion.splice_lines(cm.borrow());
- let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES);
- let display_lines = &lines.lines[..line_count];
-
- let fm = &*lines.file;
- // Calculate the widest number to format evenly
- let max_digits = line_num_max_digits(display_lines.last().unwrap());
// print the suggestion without any line numbers, but leave
// space for them. This helps with lining up with previous
if let Some(_) = lines.next() {
buffer.append(row_num, "...", Style::NoStyle);
}
- emit_to_destination(&buffer.render(), level, &mut self.dst);
+ emit_to_destination(&buffer.render(), level, &mut self.dst)?;
}
Ok(())
}
if !db.children.is_empty() {
let mut buffer = StyledBuffer::new();
draw_col_separator(&mut buffer, 0, max_line_num_len + 1);
- emit_to_destination(&buffer.render(), &db.level, &mut self.dst);
+ match emit_to_destination(&buffer.render(), &db.level, &mut self.dst) {
+ Ok(()) => (),
+ Err(e) => panic!("failed to emit error: {}", e)
+ }
}
for child in &db.children {
match child.render_span {
}
Err(e) => panic!("failed to emit error: {}", e)
}
- write!(&mut self.dst, "\n");
+ match write!(&mut self.dst, "\n") {
+ Err(e) => panic!("failed to emit error: {}", e),
+ _ => ()
+ }
}
fn emit_message_old_school(&mut self,
msp: &MultiSpan,
}
if !show_snippet {
- emit_to_destination(&buffer.render(), level, &mut self.dst);
+ emit_to_destination(&buffer.render(), level, &mut self.dst)?;
return Ok(());
}
// print any filename or anything for those.
match msp.primary_span() {
Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => {
- emit_to_destination(&buffer.render(), level, &mut self.dst);
+ emit_to_destination(&buffer.render(), level, &mut self.dst)?;
return Ok(());
}
_ => { }
}
- let mut annotated_files = self.preprocess_annotations(msp);
+ let annotated_files = self.preprocess_annotations(msp);
if let (Some(ref cm), Some(ann_file), Some(ref primary_span)) =
(self.cm.as_ref(), annotated_files.first(), msp.primary_span().as_ref()) {
buffer.puts(line_offset, 0, &file_pos, Style::FileNameStyle);
buffer.puts(line_offset, file_pos_len, &source_string, Style::Quotation);
// Sort the annotations by (start, end col)
- let mut annotations = ann_file.lines[0].annotations.clone();
+ let annotations = ann_file.lines[0].annotations.clone();
// Next, create the highlight line.
for annotation in &annotations {
}
// final step: take our styled buffer, render it, then output it
- emit_to_destination(&buffer.render(), level, &mut self.dst);
+ emit_to_destination(&buffer.render(), level, &mut self.dst)?;
Ok(())
}
fn emit_suggestion_old_school(&mut self,
let mut row_num = 1;
for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) {
buffer.append(row_num, &fm.name, Style::FileNameStyle);
- for i in 0..max_digits+2 {
+ for _ in 0..max_digits+2 {
buffer.append(row_num, &" ", Style::NoStyle);
}
buffer.append(row_num, line, Style::NoStyle);
if let Some(_) = lines.next() {
buffer.append(row_num, "...", Style::NoStyle);
}
- emit_to_destination(&buffer.render(), level, &mut self.dst);
+ emit_to_destination(&buffer.render(), level, &mut self.dst)?;
}
Ok(())
}
};
match child.render_span {
- Some(FullSpan(ref msp)) => {
+ Some(FullSpan(_)) => {
match self.emit_message_old_school(&span,
&child.message,
&None,
}
}
- fn emit_message_(&mut self,
- rsp: &RenderSpan,
- msg: &str,
- code: Option<&str>,
- lvl: Level,
- is_header: bool,
- show_snippet: bool)
- -> io::Result<()> {
- let old_school = match self.format_mode {
- FormatMode::NewErrorFormat => false,
- FormatMode::OriginalErrorFormat => true,
- FormatMode::EnvironmentSelected => check_old_skool()
- };
-
- if is_header {
- if self.first {
- self.first = false;
- } else {
- if !old_school {
- write!(self.dst, "\n")?;
- }
- }
- }
-
- match code {
- Some(code) if self.registry.as_ref()
- .and_then(|registry| registry.find_description(code))
- .is_some() => {
- let code_with_explain = String::from("--explain ") + code;
- if old_school {
- let loc = match rsp.span().primary_span() {
- Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(),
- Some(ps) => if let Some(ref cm) = self.cm {
- cm.span_to_string(ps)
- } else {
- "".to_string()
- },
- None => "".to_string()
- };
- print_diagnostic(&mut self.dst, &loc, lvl, msg, Some(code))?
- }
- else {
- print_diagnostic(&mut self.dst, "", lvl, msg, Some(&code_with_explain))?
- }
- }
- _ => {
- if old_school {
- let loc = match rsp.span().primary_span() {
- Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(),
- Some(ps) => if let Some(ref cm) = self.cm {
- cm.span_to_string(ps)
- } else {
- "".to_string()
- },
- None => "".to_string()
- };
- print_diagnostic(&mut self.dst, &loc, lvl, msg, code)?
- }
- else {
- print_diagnostic(&mut self.dst, "", lvl, msg, code)?
- }
- }
- }
-
- if !show_snippet {
- return Ok(());
- }
-
- // Watch out for various nasty special spans; don't try to
- // print any filename or anything for those.
- match rsp.span().primary_span() {
- Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => {
- return Ok(());
- }
- _ => { }
- }
-
- // Otherwise, print out the snippet etc as needed.
- match *rsp {
- FullSpan(ref msp) => {
- self.highlight_lines(msp, lvl)?;
- if let Some(primary_span) = msp.primary_span() {
- self.print_macro_backtrace(primary_span)?;
- }
- }
- Suggestion(ref suggestion) => {
- self.highlight_suggestion(suggestion)?;
- if let Some(primary_span) = rsp.span().primary_span() {
- self.print_macro_backtrace(primary_span)?;
- }
- }
- }
- if old_school {
- match code {
- Some(code) if self.registry.as_ref()
- .and_then(|registry| registry.find_description(code))
- .is_some() => {
- let loc = match rsp.span().primary_span() {
- Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(),
- Some(ps) => if let Some(ref cm) = self.cm {
- cm.span_to_string(ps)
- } else {
- "".to_string()
- },
- None => "".to_string()
- };
- let msg = "run `rustc --explain ".to_string() + &code.to_string() +
- "` to see a detailed explanation";
- print_diagnostic(&mut self.dst, &loc, Level::Help, &msg,
- None)?
- }
- _ => ()
- }
- }
- Ok(())
- }
-
- fn highlight_suggestion(&mut self, suggestion: &CodeSuggestion) -> io::Result<()>
- {
- use std::borrow::Borrow;
-
- let primary_span = suggestion.msp.primary_span().unwrap();
- if let Some(ref cm) = self.cm {
- let lines = cm.span_to_lines(primary_span).unwrap();
-
- assert!(!lines.lines.is_empty());
-
- let complete = suggestion.splice_lines(cm.borrow());
- let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES);
- let display_lines = &lines.lines[..line_count];
-
- let fm = &*lines.file;
- // Calculate the widest number to format evenly
- let max_digits = line_num_max_digits(display_lines.last().unwrap());
-
- // 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) {
- write!(&mut self.dst, "{0}:{1:2$} {3}\n",
- fm.name, "", max_digits, line)?;
- }
-
- // if we elided some lines, add an ellipsis
- if let Some(_) = lines.next() {
- write!(&mut self.dst, "{0:1$} {0:2$} ...\n",
- "", fm.name.len(), max_digits)?;
- }
- }
- Ok(())
- }
-
- pub fn highlight_lines(&mut self,
- msp: &MultiSpan,
- lvl: Level)
- -> io::Result<()>
- {
- // Check to see if we have any lines to highlight, exit early if not
- match self.cm {
- None => return Ok(()),
- _ => ()
- }
-
- let old_school = match self.format_mode {
- FormatMode::NewErrorFormat => false,
- FormatMode::OriginalErrorFormat => true,
- FormatMode::EnvironmentSelected => check_old_skool()
- };
-
- let mut snippet_data = SnippetData::new(self.cm.as_ref().unwrap().clone(),
- msp.primary_span(),
- self.format_mode.clone());
- if old_school {
- let mut output_vec = vec![];
-
- for span_label in msp.span_labels() {
- let mut snippet_data = SnippetData::new(self.cm.as_ref().unwrap().clone(),
- Some(span_label.span),
- self.format_mode.clone());
-
- snippet_data.push(span_label.span,
- span_label.is_primary,
- span_label.label);
- if span_label.is_primary {
- output_vec.insert(0, snippet_data);
- }
- else {
- output_vec.push(snippet_data);
- }
- }
-
- for snippet_data in output_vec.iter() {
- /*
- let rendered_lines = snippet_data.render_lines();
- for rendered_line in &rendered_lines {
- for styled_string in &rendered_line.text {
- self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?;
- write!(&mut self.dst, "{}", styled_string.text)?;
- self.dst.reset_attrs()?;
- }
- write!(&mut self.dst, "\n")?;
- }
- */
- emit_to_destination(&snippet_data.render_lines(), &lvl, &mut self.dst);
- }
- }
- else {
- for span_label in msp.span_labels() {
- snippet_data.push(span_label.span,
- span_label.is_primary,
- span_label.label);
- }
- emit_to_destination(&snippet_data.render_lines(), &lvl, &mut self.dst);
- /*
- let rendered_lines = snippet_data.render_lines();
- for rendered_line in &rendered_lines {
- for styled_string in &rendered_line.text {
- self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?;
- write!(&mut self.dst, "{}", styled_string.text)?;
- self.dst.reset_attrs()?;
- }
- write!(&mut self.dst, "\n")?;
- }
- */
- }
- Ok(())
- }
-
fn render_macro_backtrace_old_school(&mut self,
sp: &Span,
buffer: &mut StyledBuffer) -> io::Result<()> {
}
Ok(())
}
- fn print_macro_backtrace(&mut self,
- sp: Span)
- -> io::Result<()> {
- if let Some(ref cm) = self.cm {
- for trace in cm.macro_backtrace(sp) {
- let mut diag_string =
- format!("in this expansion of {}", trace.macro_decl_name);
- if let Some(def_site_span) = trace.def_site_span {
- diag_string.push_str(
- &format!(" (defined in {})",
- cm.span_to_filename(def_site_span)));
- }
- let snippet = cm.span_to_string(trace.call_site);
- print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?;
- }
- }
- Ok(())
- }
}
fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
dst: &mut Destination) -> io::Result<()> {
for line in rendered_buffer {
for part in line {
- dst.apply_style(lvl.clone(), part.style);
- write!(dst, "{}", part.text);
+ dst.apply_style(lvl.clone(), part.style)?;
+ write!(dst, "{}", part.text)?;
dst.reset_attrs()?;
}
- write!(dst, "\n");
+ write!(dst, "\n")?;
}
Ok(())
}
digits
}
-fn print_diagnostic(dst: &mut Destination,
- topic: &str,
- lvl: Level,
- msg: &str,
- code: Option<&str>)
- -> io::Result<()> {
- if !topic.is_empty() {
- let old_school = check_old_skool();
- if !old_school {
- write!(dst, "{}: ", topic)?;
- }
- else {
- write!(dst, "{} ", topic)?;
- }
- dst.reset_attrs()?;
- }
- dst.start_attr(term::Attr::Bold)?;
- dst.start_attr(term::Attr::ForegroundColor(lvl.color()))?;
- write!(dst, "{}", lvl.to_string())?;
- dst.reset_attrs()?;
- write!(dst, ": ")?;
- dst.start_attr(term::Attr::Bold)?;
- write!(dst, "{}", msg)?;
-
- if let Some(code) = code {
- let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
- print_maybe_styled!(dst, style, " [{}]", code.clone())?;
- }
-
- dst.reset_attrs()?;
- write!(dst, "\n")?;
- Ok(())
-}
-
#[cfg(unix)]
fn stderr_isatty() -> bool {
use libc;
}
Ok(())
}
-
- fn print_maybe_styled(&mut self,
- args: fmt::Arguments,
- color: term::Attr,
- print_newline_at_end: bool)
- -> io::Result<()> {
- match *self {
- Terminal(ref mut t) => {
- t.attr(color)?;
- // If `msg` ends in a newline, we need to reset the color before
- // the newline. We're making the assumption that we end up writing
- // to a `LineBufferedWriter`, which means that emitting the reset
- // after the newline ends up buffering the reset until we print
- // another line or exit. Buffering the reset is a problem if we're
- // sharing the terminal with any other programs (e.g. other rustc
- // instances via `make -jN`).
- //
- // Note that if `msg` contains any internal newlines, this will
- // result in the `LineBufferedWriter` flushing twice instead of
- // once, which still leaves the opportunity for interleaved output
- // to be miscolored. We assume this is rare enough that we don't
- // have to worry about it.
- t.write_fmt(args)?;
- t.reset()?;
- if print_newline_at_end {
- t.write_all(b"\n")
- } else {
- Ok(())
- }
- }
- Raw(ref mut w) => {
- w.write_fmt(args)?;
- if print_newline_at_end {
- w.write_all(b"\n")
- } else {
- Ok(())
- }
- }
- }
- }
}
impl Write for Destination {
fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace>;
}
-impl RenderSpan {
- fn span(&self) -> &MultiSpan {
- match *self {
- FullSpan(ref msp) |
- Suggestion(CodeSuggestion { ref msp, .. }) =>
- msp
- }
- }
-}
-
impl CodeSuggestion {
/// Returns the assembled code suggestion.
pub fn splice_lines(&self, cm: &CodeMapper) -> String {
// Code for annotating snippets.
-use syntax_pos::{Span, FileMap, CharPos, LineInfo};
-use check_old_skool;
+use syntax_pos::{Span, FileMap};
use CodeMapper;
-use styled_buffer::StyledBuffer;
-use std::cmp;
use std::rc::Rc;
-use std::mem;
use {Level};
#[derive(Clone)]
pub label: Option<String>,
}
-/*
-#[derive(Debug)]
-pub struct RenderedLine {
- pub text: Vec<StyledString>,
- pub kind: RenderedLineKind,
-}
-*/
-
#[derive(Debug)]
pub struct StyledString {
pub text: String,
NoStyle,
ErrorCode,
Level(Level),
-}
-
-/*
-#[derive(Debug, Clone)]
-pub enum RenderedLineKind {
- PrimaryFileName,
- OtherFileName,
- SourceText {
- file: Rc<FileMap>,
- line_index: usize,
- },
- Annotations,
- Elision,
-}
-*/
-
-impl SnippetData {
- pub fn new(codemap: Rc<CodeMapper>,
- primary_span: Option<Span>,
- format_mode: FormatMode) // (*)
- -> Self {
- // (*) The primary span indicates the file that must appear
- // first, and which will have a line number etc in its
- // name. Outside of tests, this is always `Some`, but for many
- // tests it's not relevant to test this portion of the logic,
- // and it's tedious to pick a primary span (read: tedious to
- // port older tests that predate the existence of a primary
- // span).
-
- debug!("SnippetData::new(primary_span={:?})", primary_span);
-
- let mut data = SnippetData {
- codemap: codemap.clone(),
- files: vec![],
- format_mode: format_mode.clone()
- };
- if let Some(primary_span) = primary_span {
- let lo = codemap.lookup_char_pos(primary_span.lo);
- data.files.push(
- FileInfo {
- file: lo.file,
- primary_span: Some(primary_span),
- lines: vec![],
- format_mode: format_mode.clone(),
- });
- }
- data
- }
-
- pub fn push(&mut self, span: Span, is_primary: bool, label: Option<String>) {
- debug!("SnippetData::push(span={:?}, is_primary={}, label={:?})",
- span, is_primary, label);
-
- let file_lines = match self.codemap.span_to_lines(span) {
- Ok(file_lines) => file_lines,
- Err(_) => {
- // ignore unprintable spans completely.
- return;
- }
- };
-
- self.file(&file_lines.file)
- .push_lines(&file_lines.lines, is_primary, label);
- }
-
- fn file(&mut self, file_map: &Rc<FileMap>) -> &mut FileInfo {
- let index = self.files.iter().position(|f| f.file.name == file_map.name);
- if let Some(index) = index {
- return &mut self.files[index];
- }
-
- self.files.push(
- FileInfo {
- file: file_map.clone(),
- lines: vec![],
- primary_span: None,
- format_mode: self.format_mode.clone()
- });
- self.files.last_mut().unwrap()
- }
-
- pub fn render_lines(&self) -> Vec<Vec<StyledString>> {
- debug!("SnippetData::render_lines()");
-
- let mut rendered_lines: Vec<_> =
- self.files.iter()
- .flat_map(|f| f.render_file_lines(&self.codemap))
- .collect();
- //prepend_prefixes(&mut rendered_lines, &self.format_mode);
- //trim_lines(&mut rendered_lines);
- rendered_lines
- }
-}
-
-pub trait StringSource {
- fn make_string(self) -> String;
-}
-
-impl StringSource for String {
- fn make_string(self) -> String {
- self
- }
-}
-
-impl StringSource for Vec<char> {
- fn make_string(self) -> String {
- self.into_iter().collect()
- }
-}
-
-/*
-impl<S> From<(S, Style, RenderedLineKind)> for RenderedLine
- where S: StringSource
-{
- fn from((text, style, kind): (S, Style, RenderedLineKind)) -> Self {
- RenderedLine {
- text: vec![StyledString {
- text: text.make_string(),
- style: style,
- }],
- kind: kind,
- }
- }
-}
-
-impl<S1,S2> From<(S1, Style, S2, Style, RenderedLineKind)> for RenderedLine
- where S1: StringSource, S2: StringSource
-{
- fn from(tuple: (S1, Style, S2, Style, RenderedLineKind)) -> Self {
- let (text1, style1, text2, style2, kind) = tuple;
- RenderedLine {
- text: vec![
- StyledString {
- text: text1.make_string(),
- style: style1,
- },
- StyledString {
- text: text2.make_string(),
- style: style2,
- }
- ],
- kind: kind,
- }
- }
-}
-
-impl RenderedLine {
- fn trim_last(&mut self) {
- if let Some(last_text) = self.text.last_mut() {
- let len = last_text.text.trim_right().len();
- last_text.text.truncate(len);
- }
- }
-}
-
-impl RenderedLineKind {
- fn prefix(&self) -> StyledString {
- match *self {
- RenderedLineKind::SourceText { file: _, line_index } =>
- StyledString {
- text: format!("{}", line_index + 1),
- style: Style::LineNumber,
- },
- RenderedLineKind::Elision =>
- StyledString {
- text: String::from("..."),
- style: Style::LineNumber,
- },
- RenderedLineKind::PrimaryFileName |
- RenderedLineKind::OtherFileName |
- RenderedLineKind::Annotations =>
- StyledString {
- text: String::from(""),
- style: Style::LineNumber,
- },
- }
- }
-}
-*/
-
-impl FileInfo {
- fn get_max_line_num(&self) -> usize {
- let mut max = 0;
-
- for line in &self.lines {
- if line.line_index > max {
- max = line.line_index;
- }
- }
- max
- }
-
- fn push_lines(&mut self,
- lines: &[LineInfo],
- is_primary: bool,
- label: Option<String>) {
- assert!(lines.len() > 0);
-
- // If a span covers multiple lines, we reduce it to a single
- // point at the start of the span. This means that instead
- // of producing output like this:
- //
- // ```
- // --> foo.rs:2:1
- // 2 |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>)
- // |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- // 3 |> -> Set<LR0Item<'grammar>>
- // |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- // (and so on)
- // ```
- //
- // we produce:
- //
- // ```
- // --> foo.rs:2:1
- // 2 |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>)
- // ^
- // ```
- //
- // Basically, although this loses information, multi-line spans just
- // never look good.
-
- let (line, start_col, mut end_col, is_minimized) = if lines.len() == 1 {
- (lines[0].line_index, lines[0].start_col, lines[0].end_col, false)
- } else {
- (lines[0].line_index, lines[0].start_col, CharPos(lines[0].start_col.0 + 1), 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
- // 6..7. This is degenerate input, but it's best to degrade
- // gracefully -- and the parser likes to suply a span like
- // that for EOF, in particular.
- if start_col == end_col {
- end_col.0 += 1;
- }
-
- let index = self.ensure_source_line(line);
- self.lines[index].push_annotation(start_col,
- end_col,
- is_primary,
- is_minimized,
- label);
- }
-
- /// Ensure that we have a `Line` struct corresponding to
- /// `line_index` in the file. If we already have some other lines,
- /// then this will add the intervening lines to ensure that we
- /// have a complete snippet. (Note that when we finally display,
- /// some of those lines may be elided.)
- fn ensure_source_line(&mut self, line_index: usize) -> usize {
- if self.lines.is_empty() {
- self.lines.push(Line::new(line_index));
- return 0;
- }
-
- // Find the range of lines we have thus far.
- let first_line_index = self.lines.first().unwrap().line_index;
- let last_line_index = self.lines.last().unwrap().line_index;
- assert!(first_line_index <= last_line_index);
-
- // If the new line is lower than all the lines we have thus
- // far, then insert the new line and any intervening lines at
- // the front. In a silly attempt at micro-optimization, we
- // don't just call `insert` repeatedly, but instead make a new
- // (empty) vector, pushing the new lines onto it, and then
- // appending the old vector.
- if line_index < first_line_index {
- let lines = mem::replace(&mut self.lines, vec![]);
- self.lines.extend(
- (line_index .. first_line_index)
- .map(|line| Line::new(line))
- .chain(lines));
- return 0;
- }
-
- // If the new line comes after the ones we have so far, insert
- // lines for it.
- if line_index > last_line_index {
- self.lines.extend(
- (last_line_index+1 .. line_index+1)
- .map(|line| Line::new(line)));
- return self.lines.len() - 1;
- }
-
- // Otherwise it should already exist.
- return line_index - first_line_index;
- }
-
- fn render_file_lines(&self, codemap: &Rc<CodeMapper>) -> Vec<Vec<StyledString>> {
- let old_school = match self.format_mode {
- FormatMode::OriginalErrorFormat => true,
- FormatMode::NewErrorFormat => false,
- FormatMode::EnvironmentSelected => check_old_skool()
- };
-
- let mut lines_iter = self.lines.iter();
- let mut output = vec![];
-
- // First insert the name of the file.
- if !old_school {
- match self.primary_span {
- Some(span) => {
- let lo = codemap.lookup_char_pos(span.lo);
- output.push(vec![StyledString {
- text: lo.file.name.clone(),
- style: Style::FileNameStyle,
- }, StyledString {
- text: format!(":{}:{}", lo.line, lo.col.0 + 1),
- style: Style::LineAndColumn,
- }]);
- output.push(vec![StyledString {
- text: "".to_string(),
- style: Style::FileNameStyle,
- }]);
- }
- None => {
- output.push(vec![StyledString {
- text: self.file.name.clone(),
- style: Style::FileNameStyle,
- }]);
- output.push(vec![StyledString {
- text: "".to_string(),
- style: Style::FileNameStyle,
- }]);
- }
- }
- }
-
- let mut next_line = lines_iter.next();
- while next_line.is_some() {
- // Consume lines with annotations.
- while let Some(line) = next_line {
- if line.annotations.is_empty() { break; }
-
- let mut rendered_lines = self.render_line(line);
- assert!(!rendered_lines.is_empty());
- if old_school {
- match self.primary_span {
- Some(span) => {
- let lo = codemap.lookup_char_pos(span.lo);
- let hi = codemap.lookup_char_pos(span.hi);
- //Before each secondary line in old skool-mode, print the label
- //as an old-style note
- if !line.annotations[0].is_primary {
- if let Some(ann) = line.annotations[0].label.clone() {
- output.push(vec![StyledString {
- text: lo.file.name.clone(),
- style: Style::FileNameStyle,
- }, StyledString {
- text: format!(":{}:{}: {}:{} ", lo.line, lo.col.0 + 1,
- hi.line, hi.col.0+1),
- style: Style::LineAndColumn,
- }, StyledString {
- text: format!("note: "),
- style: Style::OldSchoolNote,
- }, StyledString {
- text: format!("{}", ann),
- style: Style::OldSchoolNoteText,
- }]);
- }
- }
- rendered_lines[0].insert(0, StyledString {
- text: format!(":{} ", lo.line),
- style: Style::LineAndColumn,
- });
- rendered_lines[0].insert(0, StyledString {
- text: lo.file.name.clone(),
- style: Style::FileNameStyle,
- });
- let gap_amount =
- rendered_lines[0][0].text.len() +
- rendered_lines[0][1].text.len();
- assert!(rendered_lines.len() >= 2,
- "no annotations resulted from: {:?}",
- line);
- for i in 1..rendered_lines.len() {
- rendered_lines[i].insert(0, StyledString {
- text: vec![" "; gap_amount].join(""),
- style: Style::NoStyle
- });
- }
- }
- _ =>()
- }
- }
- output.append(&mut rendered_lines);
- next_line = lines_iter.next();
- }
-
- // Emit lines without annotations, but only if they are
- // followed by a line with an annotation.
- let unannotated_line = next_line;
- let mut unannotated_lines = 0;
- while let Some(line) = next_line {
- if !line.annotations.is_empty() { break; }
- unannotated_lines += 1;
- next_line = lines_iter.next();
- }
- if unannotated_lines > 1 {
- output.push(vec![StyledString{ text: String::new(), style: Style::NoStyle}]);
- } else if let Some(line) = unannotated_line {
- output.append(&mut self.render_line(line));
- }
- }
-
- output
- }
-
- fn render_line(&self, line: &Line) -> Vec<Vec<StyledString>> {
- let old_school = match self.format_mode {
- FormatMode::OriginalErrorFormat => true,
- FormatMode::NewErrorFormat => false,
- FormatMode::EnvironmentSelected => check_old_skool()
- };
-
- let source_string = self.file.get_line(line.line_index)
- .unwrap_or("");
- /*
- let source_kind = RenderedLineKind::SourceText {
- file: self.file.clone(),
- line_index: line.line_index,
- };
- */
-
- let mut styled_buffer = StyledBuffer::new();
-
- // First create the source line we will highlight.
- styled_buffer.append(0, &source_string, Style::Quotation);
-
- if line.annotations.is_empty() {
- return styled_buffer.render();
- }
-
- // We want to display like this:
- //
- // vec.push(vec.pop().unwrap());
- // --- ^^^ _ previous borrow ends here
- // | |
- // | error occurs here
- // previous borrow of `vec` occurs here
- //
- // But there are some weird edge cases to be aware of:
- //
- // vec.push(vec.pop().unwrap());
- // -------- - previous borrow ends here
- // ||
- // |this makes no sense
- // 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 `~`.
-
- //let mut highlight_line = Self::whitespace(&source_string);
-
- // Sort the annotations by (start, end col)
- let mut annotations = line.annotations.clone();
- annotations.sort();
-
- // Next, create the highlight line.
- for annotation in &annotations {
- if old_school {
- for p in annotation.start_col .. annotation.end_col {
- if p == annotation.start_col {
- styled_buffer.putc(1, p, '^',
- if annotation.is_primary {
- Style::UnderlinePrimary
- } else {
- Style::OldSchoolNote
- });
- }
- else {
- styled_buffer.putc(1, p, '~',
- if annotation.is_primary {
- Style::UnderlinePrimary
- } else {
- Style::OldSchoolNote
- });
- }
- }
- }
- else {
- for p in annotation.start_col .. annotation.end_col {
- if annotation.is_primary {
- styled_buffer.putc(1, p, '^', Style::UnderlinePrimary);
- if !annotation.is_minimized {
- styled_buffer.set_style(0, p, Style::UnderlinePrimary);
- }
- } else {
- styled_buffer.putc(1, p, '-', Style::UnderlineSecondary);
- if !annotation.is_minimized {
- styled_buffer.set_style(0, p, Style::UnderlineSecondary);
- }
- }
- }
- }
- }
-
- // Now we are going to write labels in. To start, we'll exclude
- // the annotations with no labels.
- let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) =
- annotations.into_iter()
- .partition(|a| a.label.is_some());
-
- // If there are no annotations that need text, we're done.
- if labeled_annotations.is_empty() {
- return styled_buffer.render();
- }
- if old_school {
- return styled_buffer.render();
- }
-
- // Now add the text labels. We try, when possible, to stick the rightmost
- // annotation at the end of the highlight line:
- //
- // vec.push(vec.pop().unwrap());
- // --- --- - previous borrow ends here
- //
- // But sometimes that's not possible because one of the other
- // annotations overlaps it. For example, from the test
- // `span_overlap_label`, we have the following annotations
- // (written on distinct lines for clarity):
- //
- // fn foo(x: u32) {
- // --------------
- // -
- //
- // In this case, we can't stick the rightmost-most label on
- // the highlight line, or we would get:
- //
- // fn foo(x: u32) {
- // -------- x_span
- // |
- // fn_span
- //
- // which is totally weird. Instead we want:
- //
- // fn foo(x: u32) {
- // --------------
- // | |
- // | x_span
- // fn_span
- //
- // which is...less weird, at least. In fact, in general, if
- // the rightmost span overlaps with any other span, we should
- // use the "hang below" version, so we can at least make it
- // clear where the span *starts*.
- let mut labeled_annotations = &labeled_annotations[..];
- match labeled_annotations.split_last().unwrap() {
- (last, previous) => {
- if previous.iter()
- .chain(&unlabeled_annotations)
- .all(|a| !overlaps(a, last))
- {
- // append the label afterwards; we keep it in a separate
- // string
- let highlight_label: String = format!(" {}", last.label.as_ref().unwrap());
- if last.is_primary {
- styled_buffer.append(1, &highlight_label, Style::LabelPrimary);
- } else {
- styled_buffer.append(1, &highlight_label, Style::LabelSecondary);
- }
- labeled_annotations = previous;
- }
- }
- }
-
- // If that's the last annotation, we're done
- if labeled_annotations.is_empty() {
- return styled_buffer.render();
- }
-
- for (index, annotation) in labeled_annotations.iter().enumerate() {
- // Leave:
- // - 1 extra line
- // - One line for each thing that comes after
- let comes_after = labeled_annotations.len() - index - 1;
- let blank_lines = 3 + comes_after;
-
- // For each blank line, draw a `|` at our column. The
- // text ought to be long enough for this.
- for index in 2..blank_lines {
- if annotation.is_primary {
- styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlinePrimary);
- } else {
- styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlineSecondary);
- }
- }
-
- if annotation.is_primary {
- styled_buffer.puts(blank_lines, annotation.start_col,
- annotation.label.as_ref().unwrap(), Style::LabelPrimary);
- } else {
- styled_buffer.puts(blank_lines, annotation.start_col,
- annotation.label.as_ref().unwrap(), Style::LabelSecondary);
- }
- }
-
- styled_buffer.render()
- }
-}
-
-/*
-fn prepend_prefixes(rendered_lines: &mut [RenderedLine], format_mode: &FormatMode) {
- let old_school = match *format_mode {
- FormatMode::OriginalErrorFormat => true,
- FormatMode::NewErrorFormat => false,
- FormatMode::EnvironmentSelected => check_old_skool()
- };
- if old_school {
- return;
- }
-
- let prefixes: Vec<_> =
- rendered_lines.iter()
- .map(|rl| rl.kind.prefix())
- .collect();
-
- // find the max amount of spacing we need; add 1 to
- // p.text.len() to leave space between the prefix and the
- // source text
- let padding_len =
- prefixes.iter()
- .map(|p| if p.text.len() == 0 { 0 } else { p.text.len() + 1 })
- .max()
- .unwrap_or(0);
-
- // Ensure we insert at least one character of padding, so that the
- // `-->` arrows can fit etc.
- let padding_len = cmp::max(padding_len, 1);
-
- for (mut prefix, line) in prefixes.into_iter().zip(rendered_lines) {
- let extra_spaces = (prefix.text.len() .. padding_len).map(|_| ' ');
- prefix.text.extend(extra_spaces);
- match line.kind {
- RenderedLineKind::Elision => {
- line.text.insert(0, prefix);
- }
- RenderedLineKind::PrimaryFileName => {
- // --> filename
- // 22 |>
- // ^
- // padding_len
- let dashes = (0..padding_len - 1).map(|_| ' ')
- .chain(Some('-'))
- .chain(Some('-'))
- .chain(Some('>'))
- .chain(Some(' '));
- line.text.insert(0, StyledString {text: dashes.collect(),
- style: Style::LineNumber})
- }
- RenderedLineKind::OtherFileName => {
- // ::: filename
- // 22 |>
- // ^
- // padding_len
- let dashes = (0..padding_len - 1).map(|_| ' ')
- .chain(Some(':'))
- .chain(Some(':'))
- .chain(Some(':'))
- .chain(Some(' '));
- line.text.insert(0, StyledString {text: dashes.collect(),
- style: Style::LineNumber})
- }
- _ => {
- line.text.insert(0, prefix);
- line.text.insert(1, StyledString {text: String::from("|> "),
- style: Style::LineNumber})
- }
- }
- }
-}
-
-fn trim_lines(rendered_lines: &mut [RenderedLine]) {
- for line in rendered_lines {
- while !line.text.is_empty() {
- line.trim_last();
- if line.text.last().unwrap().text.is_empty() {
- line.text.pop();
- } else {
- break;
- }
- }
- }
-}
-*/
-
-impl Line {
- fn new(line_index: usize) -> Line {
- Line {
- line_index: line_index,
- annotations: vec![]
- }
- }
-
- fn push_annotation(&mut self,
- start: CharPos,
- end: CharPos,
- is_primary: bool,
- is_minimized: bool,
- label: Option<String>) {
- self.annotations.push(Annotation {
- start_col: start.0,
- end_col: end.0,
- is_primary: is_primary,
- is_minimized: is_minimized,
- label: label,
- });
- }
-}
-
-fn overlaps(a1: &Annotation,
- a2: &Annotation)
- -> bool
-{
- (a2.start_col .. a2.end_col).contains(a1.start_col) ||
- (a1.start_col .. a1.end_col).contains(a2.start_col)
-}
+}
\ No newline at end of file
println!("r#\"\n{}\"#", str);
assert_eq!(str, &r#"
--> dummy.txt:11:1
- |>
-11 |> e-lä-vän
- |> ^
+ |
+11 | e-lä-vän
+ | ^
"#[1..]);
}
let expect_start = &r#"
--> dummy.txt:1:6
- |>
-1 |> _____aaaaaa____bbbbbb__cccccdd_
- |> ^^^^^^ ^^^^^^ ^^^^^^^
+ |
+1 | _____aaaaaa____bbbbbb__cccccdd_
+ | ^^^^^^ ^^^^^^ ^^^^^^^
"#[1..];
let span = |sp, expected| {
let expect0 = &r#"
--> dummy.txt:5:1
- |>
-5 |> ccccc
- |> ^
+ |
+5 | ccccc
+ | ^
...
-9 |> ddd__eee_
- |> ^^^ ^^^
-10 |> elided
-11 |> __f_gg
- |> ^ ^^
+9 | ddd__eee_
+ | ^^^ ^^^
+10 | elided
+11 | __f_gg
+ | ^ ^^
"#[1..];
let expect = &r#"
--> dummy.txt:1:1
- |>
-1 |> aaaaa
- |> ^
+ |
+1 | aaaaa
+ | ^
...
-9 |> ddd__eee_
- |> ^^^ ^^^
-10 |> elided
-11 |> __f_gg
- |> ^ ^^
+9 | ddd__eee_
+ | ^^^ ^^^
+10 | elided
+11 | __f_gg
+ | ^ ^^
"#[1..];
macro_rules! test {
let text = make_string(&lines);
assert_eq!(&text[..], &"
--> foo.rs:3:2
- |>
-3 |> \tbar;
- |> \t^^^
+ |
+3 | \tbar;
+ | \t^^^
"[1..]);
}
println!("text=\n{}", text);
assert_eq!(&text[..], &r#"
::: foo.rs
- |>
-3 |> vec.push(vec.pop().unwrap());
- |> --- --- - previous borrow ends here
- |> | |
- |> | error occurs here
- |> previous borrow of `vec` occurs here
+ |
+3 | vec.push(vec.pop().unwrap());
+ | --- --- - previous borrow ends here
+ | | |
+ | | error occurs here
+ | previous borrow of `vec` occurs here
"#[1..]);
}
println!("text=\n{}", text);
- // Note that the `|>` remain aligned across both files:
+ // Note that the `|` remain aligned across both files:
assert_eq!(&text[..], &r#"
--> foo.rs:3:14
- |>
-3 |> vec.push(vec.pop().unwrap());
- |> --- ^^^ - c
- |> | |
- |> | b
- |> a
+ |
+3 | vec.push(vec.pop().unwrap());
+ | --- ^^^ - c
+ | | |
+ | | b
+ | a
::: bar.rs
- |>
-17 |> vec.push();
- |> --- - f
- |> |
- |> d
+ |
+17 | vec.push();
+ | --- - f
+ | |
+ | d
...
-21 |> vec.pop().unwrap());
- |> --- e
+21 | vec.pop().unwrap());
+ | --- e
"#[1..]);
}
println!("text=\n{}", text);
assert_eq!(&text[..], &r#"
::: foo.rs
- |>
-3 |> let name = find_id(&data, 22).unwrap();
- |> ---- immutable borrow begins here
+ |
+3 | let name = find_id(&data, 22).unwrap();
+ | ---- immutable borrow begins here
...
-6 |> data.push(Data { name: format!("Hera"), id: 66 });
- |> ---- mutable borrow occurs here
+6 | data.push(Data { name: format!("Hera"), id: 66 });
+ | ---- mutable borrow occurs here
...
-11 |> }
- |> - immutable borrow ends here
+11 | }
+ | - immutable borrow ends here
"#[1..]);
}
println!("text=r#\"\n{}\".trim_left()", text);
assert_eq!(&text[..], &r#"
::: foo.rs
- |>
-3 |> vec.push(vec.pop().unwrap());
- |> -------- ------ D
- |> ||
- |> |C
- |> A
- |> B
+ |
+3 | vec.push(vec.pop().unwrap());
+ | -------- ------ D
+ | ||
+ | |C
+ | A
+ | B
"#[1..]);
}
println!("text=r#\"\n{}\".trim_left()", text);
assert_eq!(&text[..], &r#"
::: foo.rs
- |>
-3 |> vec.push(vec.pop().unwrap());
- |> --- --- - previous borrow ends here
- |> | |
- |> | error occurs here
- |> previous borrow of `vec` occurs here
+ |
+3 | vec.push(vec.pop().unwrap());
+ | --- --- - previous borrow ends here
+ | | |
+ | | error occurs here
+ | previous borrow of `vec` occurs here
"#[1..]);
}
println!("text=r#\"\n{}\".trim_left()", text);
assert_eq!(&text[..], &r#"
::: foo.rs
- |>
-4 |> let mut vec2 = vec;
- |> --- `vec` moved here because it has type `collections::vec::Vec<i32>`
+ |
+4 | let mut vec2 = vec;
+ | --- `vec` moved here because it has type `collections::vec::Vec<i32>`
...
-9 |> vec.push(7);
- |> --- use of moved value: `vec`
+9 | vec.push(7);
+ | --- use of moved value: `vec`
"#[1..]);
}
println!("text=&r#\"\n{}\n\"#[1..]", text);
assert_eq!(text, &r#"
::: foo.rs
- |>
-3 |> let mut vec = vec![0, 1, 2];
- |> --- ---
-4 |> let mut vec2 = vec;
- |> --- ---
+ |
+3 | let mut vec = vec![0, 1, 2];
+ | --- ---
+4 | let mut vec2 = vec;
+ | --- ---
"#[1..]);
}
println!("r#\"\n{}\"", text);
assert_eq!(text, &r#"
::: foo.rs
- |>
-3 |> fn foo(x: u32) {
- |> -
+ |
+3 | fn foo(x: u32) {
+ | -
"#[1..]);
}
println!("r#\"\n{}\"", text);
assert_eq!(text, &r#"
::: foo.rs
- |>
-2 |> fn foo(x: u32) {
- |> --------------
- |> | |
- |> | x_span
- |> fn_span
+ |
+2 | fn foo(x: u32) {
+ | --------------
+ | | |
+ | | x_span
+ | fn_span
"#[1..]);
}
println!("r#\"\n{}\"", text);
assert_eq!(text, &r#"
::: foo.rs
- |>
-2 |> fn foo(x: u32) {
- |> --------------
- |> | |
- |> | x_span
- |> fn_span
+ |
+2 | fn foo(x: u32) {
+ | --------------
+ | | |
+ | | x_span
+ | fn_span
"#[1..]);
}
println!("r#\"\n{}\"", text);
assert_eq!(text, &r#"
::: foo.rs
- |>
-3 |> let closure = || {
- |> - foo
-4 |> inner
- |> ----- bar
+ |
+3 | let closure = || {
+ | - foo
+4 | inner
+ | ----- bar
"#[1..]);
}
println!("r#\"\n{}\"", text);
assert_eq!(text, &r#"
--> foo.rs:11:2
- |>
-11 |> }
- |> -
+ |
+11 | }
+ | -
"#[1..]);
}
}