Auto merge of #34789 - jonathandturner:simplify_liberror, r=alexcrichton
Simplify librustc_errors This is part 2 of the error crate refactor, starting with #34403. In this refactor, I focused on slimming down the error crate to fewer moving parts. As such, I've removed quite a few parts and replaced the with simpler, straight-line code. Specifically, this PR: * Removes BasicEmitter * Remove emit from emitter, leaving emit_struct * Renames emit_struct to emit * Removes CoreEmitter and focuses on a single Emitter * Implements the latest changes to error format RFC (#1644) * Removes (now-unused) code in emitter.rs and snippet.rs * Moves more tests to the UI tester, removing some duplicate tests in the process There is probably more that could be done with some additional refactoring, but this felt like it was getting to a good state. r? @alexcrichton cc: @Manishearth (as there may be breaking changes in stuff I removed/changed)
This commit is contained in:
commit
7ed6068d3e
@ -94,7 +94,7 @@ use syntax::ast;
|
||||
use syntax::parse::token;
|
||||
use syntax::ptr::P;
|
||||
use syntax_pos::{self, Pos, Span};
|
||||
use errors::{DiagnosticBuilder, check_old_skool};
|
||||
use errors::{DiagnosticBuilder, check_old_school};
|
||||
|
||||
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn note_and_explain_region(self,
|
||||
@ -485,7 +485,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
"{}",
|
||||
trace.origin);
|
||||
|
||||
if !is_simple_error || check_old_skool() {
|
||||
if !is_simple_error || check_old_school() {
|
||||
err.note_expected_found(&"type", &expected, &found);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,8 @@ use mir::transform as mir_pass;
|
||||
|
||||
use syntax::ast::{NodeId, Name};
|
||||
use errors::{self, DiagnosticBuilder};
|
||||
use errors::emitter::{Emitter, BasicEmitter, EmitterWriter};
|
||||
use errors::emitter::{Emitter, EmitterWriter};
|
||||
use errors::snippet::FormatMode;
|
||||
use syntax::json::JsonEmitter;
|
||||
use syntax::feature_gate;
|
||||
use syntax::parse;
|
||||
@ -439,7 +440,7 @@ pub fn build_session_with_codemap(sopts: config::Options,
|
||||
config::ErrorOutputType::HumanReadable(color_config) => {
|
||||
Box::new(EmitterWriter::stderr(color_config,
|
||||
Some(registry),
|
||||
codemap.clone(),
|
||||
Some(codemap.clone()),
|
||||
errors::snippet::FormatMode::EnvironmentSelected))
|
||||
}
|
||||
config::ErrorOutputType::Json => {
|
||||
@ -575,24 +576,32 @@ unsafe fn configure_llvm(sess: &Session) {
|
||||
}
|
||||
|
||||
pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
|
||||
let mut emitter: Box<Emitter> = match output {
|
||||
let emitter: Box<Emitter> = match output {
|
||||
config::ErrorOutputType::HumanReadable(color_config) => {
|
||||
Box::new(BasicEmitter::stderr(color_config))
|
||||
Box::new(EmitterWriter::stderr(color_config,
|
||||
None,
|
||||
None,
|
||||
FormatMode::EnvironmentSelected))
|
||||
}
|
||||
config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
|
||||
};
|
||||
emitter.emit(&MultiSpan::new(), msg, None, errors::Level::Fatal);
|
||||
let handler = errors::Handler::with_emitter(true, false, emitter);
|
||||
handler.emit(&MultiSpan::new(), msg, errors::Level::Fatal);
|
||||
panic!(errors::FatalError);
|
||||
}
|
||||
|
||||
pub fn early_warn(output: config::ErrorOutputType, msg: &str) {
|
||||
let mut emitter: Box<Emitter> = match output {
|
||||
let emitter: Box<Emitter> = match output {
|
||||
config::ErrorOutputType::HumanReadable(color_config) => {
|
||||
Box::new(BasicEmitter::stderr(color_config))
|
||||
Box::new(EmitterWriter::stderr(color_config,
|
||||
None,
|
||||
None,
|
||||
FormatMode::EnvironmentSelected))
|
||||
}
|
||||
config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
|
||||
};
|
||||
emitter.emit(&MultiSpan::new(), msg, None, errors::Level::Warning);
|
||||
let handler = errors::Handler::with_emitter(true, false, emitter);
|
||||
handler.emit(&MultiSpan::new(), msg, errors::Level::Warning);
|
||||
}
|
||||
|
||||
// Err(0) means compilation was stopped, but no errors were found.
|
||||
|
@ -100,6 +100,7 @@ use syntax::feature_gate::{GatedCfg, UnstableFeatures};
|
||||
use syntax::parse::{self, PResult};
|
||||
use syntax_pos::MultiSpan;
|
||||
use errors::emitter::Emitter;
|
||||
use errors::snippet::FormatMode;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test;
|
||||
@ -138,10 +139,15 @@ pub fn run(args: Vec<String>) -> isize {
|
||||
match session {
|
||||
Some(sess) => sess.fatal(&abort_msg(err_count)),
|
||||
None => {
|
||||
let mut emitter =
|
||||
errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto);
|
||||
emitter.emit(&MultiSpan::new(), &abort_msg(err_count), None,
|
||||
errors::Level::Fatal);
|
||||
let emitter =
|
||||
errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto,
|
||||
None,
|
||||
None,
|
||||
FormatMode::EnvironmentSelected);
|
||||
let handler = errors::Handler::with_emitter(true, false, Box::new(emitter));
|
||||
handler.emit(&MultiSpan::new(),
|
||||
&abort_msg(err_count),
|
||||
errors::Level::Fatal);
|
||||
exit_on_err();
|
||||
}
|
||||
}
|
||||
@ -373,23 +379,26 @@ fn handle_explain(code: &str,
|
||||
|
||||
fn check_cfg(sopts: &config::Options,
|
||||
output: ErrorOutputType) {
|
||||
let mut emitter: Box<Emitter> = match output {
|
||||
let emitter: Box<Emitter> = match output {
|
||||
config::ErrorOutputType::HumanReadable(color_config) => {
|
||||
Box::new(errors::emitter::BasicEmitter::stderr(color_config))
|
||||
Box::new(errors::emitter::EmitterWriter::stderr(color_config,
|
||||
None,
|
||||
None,
|
||||
FormatMode::EnvironmentSelected))
|
||||
}
|
||||
config::ErrorOutputType::Json => Box::new(json::JsonEmitter::basic()),
|
||||
};
|
||||
let handler = errors::Handler::with_emitter(true, false, emitter);
|
||||
|
||||
let mut saw_invalid_predicate = false;
|
||||
for item in sopts.cfg.iter() {
|
||||
match item.node {
|
||||
ast::MetaItemKind::List(ref pred, _) => {
|
||||
saw_invalid_predicate = true;
|
||||
emitter.emit(&MultiSpan::new(),
|
||||
handler.emit(&MultiSpan::new(),
|
||||
&format!("invalid predicate in --cfg command line argument: `{}`",
|
||||
pred),
|
||||
None,
|
||||
errors::Level::Fatal);
|
||||
errors::Level::Fatal);
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
@ -1046,26 +1055,34 @@ pub fn monitor<F: FnOnce() + Send + 'static>(f: F) {
|
||||
if let Err(value) = thread.unwrap().join() {
|
||||
// Thread panicked without emitting a fatal diagnostic
|
||||
if !value.is::<errors::FatalError>() {
|
||||
let mut emitter = errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto);
|
||||
let emitter =
|
||||
Box::new(errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto,
|
||||
None,
|
||||
None,
|
||||
FormatMode::EnvironmentSelected));
|
||||
let handler = errors::Handler::with_emitter(true, false, emitter);
|
||||
|
||||
// a .span_bug or .bug call has already printed what
|
||||
// it wants to print.
|
||||
if !value.is::<errors::ExplicitBug>() {
|
||||
emitter.emit(&MultiSpan::new(), "unexpected panic", None, errors::Level::Bug);
|
||||
handler.emit(&MultiSpan::new(),
|
||||
"unexpected panic",
|
||||
errors::Level::Bug);
|
||||
}
|
||||
|
||||
let xs = ["the compiler unexpectedly panicked. this is a bug.".to_string(),
|
||||
format!("we would appreciate a bug report: {}", BUG_REPORT_URL)];
|
||||
for note in &xs {
|
||||
emitter.emit(&MultiSpan::new(), ¬e[..], None, errors::Level::Note)
|
||||
handler.emit(&MultiSpan::new(),
|
||||
¬e[..],
|
||||
errors::Level::Note);
|
||||
}
|
||||
if match env::var_os("RUST_BACKTRACE") {
|
||||
Some(val) => &val != "0",
|
||||
None => false,
|
||||
} {
|
||||
emitter.emit(&MultiSpan::new(),
|
||||
handler.emit(&MultiSpan::new(),
|
||||
"run with `RUST_BACKTRACE=1` for a backtrace",
|
||||
None,
|
||||
errors::Level::Note);
|
||||
}
|
||||
|
||||
|
@ -33,8 +33,8 @@ use syntax::ast;
|
||||
use syntax::abi::Abi;
|
||||
use syntax::codemap::CodeMap;
|
||||
use errors;
|
||||
use errors::emitter::{CoreEmitter, Emitter};
|
||||
use errors::{Level, RenderSpan};
|
||||
use errors::emitter::Emitter;
|
||||
use errors::{Level, DiagnosticBuilder};
|
||||
use syntax::parse::token;
|
||||
use syntax::feature_gate::UnstableFeatures;
|
||||
use syntax_pos::DUMMY_SP;
|
||||
@ -76,15 +76,12 @@ fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreEmitter for ExpectErrorEmitter {
|
||||
fn emit_message(&mut self,
|
||||
_sp: &RenderSpan,
|
||||
msg: &str,
|
||||
_: Option<&str>,
|
||||
lvl: Level,
|
||||
_is_header: bool,
|
||||
_show_snippet: bool) {
|
||||
remove_message(self, msg, lvl);
|
||||
impl Emitter for ExpectErrorEmitter {
|
||||
fn emit(&mut self, db: &DiagnosticBuilder) {
|
||||
remove_message(self, &db.message, db.level);
|
||||
for child in &db.children {
|
||||
remove_message(self, &child.message, child.level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -49,6 +49,7 @@ use std::thread::panicking;
|
||||
pub mod emitter;
|
||||
pub mod snippet;
|
||||
pub mod registry;
|
||||
pub mod styled_buffer;
|
||||
|
||||
use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION };
|
||||
use syntax_pos::{MacroBacktrace};
|
||||
@ -81,16 +82,6 @@ pub trait CodeMapper {
|
||||
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 {
|
||||
@ -238,7 +229,7 @@ impl<'a> DiagnosticBuilder<'a> {
|
||||
return;
|
||||
}
|
||||
|
||||
self.handler.emit.borrow_mut().emit_struct(&self);
|
||||
self.handler.emitter.borrow_mut().emit(&self);
|
||||
self.cancel();
|
||||
self.handler.panic_if_treat_err_as_bug();
|
||||
|
||||
@ -359,11 +350,20 @@ impl<'a> DiagnosticBuilder<'a> {
|
||||
fn new(handler: &'a Handler,
|
||||
level: Level,
|
||||
message: &str) -> DiagnosticBuilder<'a> {
|
||||
DiagnosticBuilder::new_with_code(handler, level, None, message)
|
||||
}
|
||||
|
||||
/// Convenience function for internal use, clients should use one of the
|
||||
/// struct_* methods on Handler.
|
||||
fn new_with_code(handler: &'a Handler,
|
||||
level: Level,
|
||||
code: Option<String>,
|
||||
message: &str) -> DiagnosticBuilder<'a> {
|
||||
DiagnosticBuilder {
|
||||
handler: handler,
|
||||
level: level,
|
||||
message: message.to_owned(),
|
||||
code: None,
|
||||
code: code,
|
||||
span: MultiSpan::new(),
|
||||
children: vec![],
|
||||
}
|
||||
@ -397,10 +397,10 @@ impl<'a> fmt::Debug for DiagnosticBuilder<'a> {
|
||||
impl<'a> Drop for DiagnosticBuilder<'a> {
|
||||
fn drop(&mut self) {
|
||||
if !panicking() && !self.cancelled() {
|
||||
self.handler.emit.borrow_mut().emit(&MultiSpan::new(),
|
||||
"Error constructed but not emitted",
|
||||
None,
|
||||
Bug);
|
||||
let mut db = DiagnosticBuilder::new(self.handler,
|
||||
Bug,
|
||||
"Error constructed but not emitted");
|
||||
db.emit();
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
@ -411,7 +411,7 @@ impl<'a> Drop for DiagnosticBuilder<'a> {
|
||||
/// others log errors for later reporting.
|
||||
pub struct Handler {
|
||||
err_count: Cell<usize>,
|
||||
emit: RefCell<Box<Emitter>>,
|
||||
emitter: RefCell<Box<Emitter>>,
|
||||
pub can_emit_warnings: bool,
|
||||
treat_err_as_bug: bool,
|
||||
continue_after_error: Cell<bool>,
|
||||
@ -423,7 +423,7 @@ impl Handler {
|
||||
registry: Option<registry::Registry>,
|
||||
can_emit_warnings: bool,
|
||||
treat_err_as_bug: bool,
|
||||
cm: Rc<CodeMapper>)
|
||||
cm: Option<Rc<CodeMapper>>)
|
||||
-> Handler {
|
||||
let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm,
|
||||
snippet::FormatMode::EnvironmentSelected));
|
||||
@ -435,7 +435,7 @@ impl Handler {
|
||||
e: Box<Emitter>) -> Handler {
|
||||
Handler {
|
||||
err_count: Cell::new(0),
|
||||
emit: RefCell::new(e),
|
||||
emitter: RefCell::new(e),
|
||||
can_emit_warnings: can_emit_warnings,
|
||||
treat_err_as_bug: treat_err_as_bug,
|
||||
continue_after_error: Cell::new(true),
|
||||
@ -588,7 +588,7 @@ impl Handler {
|
||||
self.bump_err_count();
|
||||
}
|
||||
pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
|
||||
self.emit.borrow_mut().emit(&sp.into(), msg, None, Note);
|
||||
self.emit(&sp.into(), msg, Note);
|
||||
}
|
||||
pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
|
||||
self.span_bug(sp, &format!("unimplemented {}", msg));
|
||||
@ -597,7 +597,10 @@ impl Handler {
|
||||
if self.treat_err_as_bug {
|
||||
self.bug(msg);
|
||||
}
|
||||
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Fatal);
|
||||
let mut db = DiagnosticBuilder::new(self,
|
||||
Fatal,
|
||||
msg);
|
||||
db.emit();
|
||||
self.bump_err_count();
|
||||
FatalError
|
||||
}
|
||||
@ -605,17 +608,29 @@ impl Handler {
|
||||
if self.treat_err_as_bug {
|
||||
self.bug(msg);
|
||||
}
|
||||
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Error);
|
||||
let mut db = DiagnosticBuilder::new(self,
|
||||
Error,
|
||||
msg);
|
||||
db.emit();
|
||||
self.bump_err_count();
|
||||
}
|
||||
pub fn warn(&self, msg: &str) {
|
||||
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Warning);
|
||||
let mut db = DiagnosticBuilder::new(self,
|
||||
Warning,
|
||||
msg);
|
||||
db.emit();
|
||||
}
|
||||
pub fn note_without_error(&self, msg: &str) {
|
||||
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Note);
|
||||
let mut db = DiagnosticBuilder::new(self,
|
||||
Note,
|
||||
msg);
|
||||
db.emit();
|
||||
}
|
||||
pub fn bug(&self, msg: &str) -> ! {
|
||||
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Bug);
|
||||
let mut db = DiagnosticBuilder::new(self,
|
||||
Bug,
|
||||
msg);
|
||||
db.emit();
|
||||
panic!(ExplicitBug);
|
||||
}
|
||||
pub fn unimpl(&self, msg: &str) -> ! {
|
||||
@ -661,7 +676,9 @@ impl Handler {
|
||||
msg: &str,
|
||||
lvl: Level) {
|
||||
if lvl == Warning && !self.can_emit_warnings { return }
|
||||
self.emit.borrow_mut().emit(&msp, msg, None, lvl);
|
||||
let mut db = DiagnosticBuilder::new(self, lvl, msg);
|
||||
db.set_span(msp.clone());
|
||||
db.emit();
|
||||
if !self.continue_after_error.get() { self.abort_if_errors(); }
|
||||
}
|
||||
pub fn emit_with_code(&self,
|
||||
@ -670,7 +687,12 @@ impl Handler {
|
||||
code: &str,
|
||||
lvl: Level) {
|
||||
if lvl == Warning && !self.can_emit_warnings { return }
|
||||
self.emit.borrow_mut().emit(&msp, msg, Some(code), lvl);
|
||||
let mut db = DiagnosticBuilder::new_with_code(self,
|
||||
lvl,
|
||||
Some(code.to_owned()),
|
||||
msg);
|
||||
db.set_span(msp.clone());
|
||||
db.emit();
|
||||
if !self.continue_after_error.get() { self.abort_if_errors(); }
|
||||
}
|
||||
}
|
||||
@ -734,13 +756,13 @@ pub fn expect<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T where
|
||||
///
|
||||
/// FIXME(#33240)
|
||||
#[cfg(not(test))]
|
||||
pub fn check_old_skool() -> bool {
|
||||
pub fn check_old_school() -> bool {
|
||||
use std::env;
|
||||
env::var("RUST_NEW_ERROR_FORMAT").is_err()
|
||||
}
|
||||
|
||||
/// For unit tests, use the new format.
|
||||
#[cfg(test)]
|
||||
pub fn check_old_skool() -> bool {
|
||||
pub fn check_old_school() -> bool {
|
||||
false
|
||||
}
|
||||
|
@ -10,12 +10,10 @@
|
||||
|
||||
// Code for annotating snippets.
|
||||
|
||||
use syntax_pos::{Span, FileMap, CharPos, LineInfo};
|
||||
use check_old_skool;
|
||||
use syntax_pos::{Span, FileMap};
|
||||
use CodeMapper;
|
||||
use std::cmp;
|
||||
use std::rc::Rc;
|
||||
use std::mem;
|
||||
use {Level};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum FormatMode {
|
||||
@ -49,37 +47,31 @@ pub struct FileInfo {
|
||||
format_mode: FormatMode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Line {
|
||||
line_index: usize,
|
||||
annotations: Vec<Annotation>,
|
||||
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub struct Line {
|
||||
pub line_index: usize,
|
||||
pub annotations: Vec<Annotation>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
|
||||
struct Annotation {
|
||||
pub struct Annotation {
|
||||
/// Start column, 0-based indexing -- counting *characters*, not
|
||||
/// utf-8 bytes. Note that it is important that this field goes
|
||||
/// first, so that when we sort, we sort orderings by start
|
||||
/// column.
|
||||
start_col: usize,
|
||||
pub start_col: usize,
|
||||
|
||||
/// End column within the line (exclusive)
|
||||
end_col: usize,
|
||||
pub end_col: usize,
|
||||
|
||||
/// Is this annotation derived from primary span
|
||||
is_primary: bool,
|
||||
pub is_primary: bool,
|
||||
|
||||
/// Is this a large span minimized down to a smaller span
|
||||
is_minimized: bool,
|
||||
pub is_minimized: bool,
|
||||
|
||||
/// Optional label to display adjacent to the annotation.
|
||||
label: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RenderedLine {
|
||||
pub text: Vec<StyledString>,
|
||||
pub kind: RenderedLineKind,
|
||||
pub label: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -88,14 +80,9 @@ pub struct StyledString {
|
||||
pub style: Style,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StyledBuffer {
|
||||
text: Vec<Vec<char>>,
|
||||
styles: Vec<Vec<Style>>
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Style {
|
||||
HeaderMsg,
|
||||
FileNameStyle,
|
||||
LineAndColumn,
|
||||
LineNumber,
|
||||
@ -104,813 +91,9 @@ pub enum Style {
|
||||
UnderlineSecondary,
|
||||
LabelPrimary,
|
||||
LabelSecondary,
|
||||
OldSkoolNoteText,
|
||||
OldSkoolNote,
|
||||
OldSchoolNoteText,
|
||||
OldSchoolNote,
|
||||
NoStyle,
|
||||
}
|
||||
|
||||
#[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<RenderedLine> {
|
||||
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 StyledBuffer {
|
||||
fn new() -> StyledBuffer {
|
||||
StyledBuffer { text: vec![], styles: vec![] }
|
||||
}
|
||||
|
||||
fn render(&self, source_kind: RenderedLineKind) -> Vec<RenderedLine> {
|
||||
let mut output: Vec<RenderedLine> = vec![];
|
||||
let mut styled_vec: Vec<StyledString> = vec![];
|
||||
|
||||
for (row, row_style) in self.text.iter().zip(&self.styles) {
|
||||
let mut current_style = Style::NoStyle;
|
||||
let mut current_text = String::new();
|
||||
|
||||
for (&c, &s) in row.iter().zip(row_style) {
|
||||
if s != current_style {
|
||||
if !current_text.is_empty() {
|
||||
styled_vec.push(StyledString { text: current_text, style: current_style });
|
||||
}
|
||||
current_style = s;
|
||||
current_text = String::new();
|
||||
}
|
||||
current_text.push(c);
|
||||
}
|
||||
if !current_text.is_empty() {
|
||||
styled_vec.push(StyledString { text: current_text, style: current_style });
|
||||
}
|
||||
|
||||
if output.is_empty() {
|
||||
//We know our first output line is source and the rest are highlights and labels
|
||||
output.push(RenderedLine { text: styled_vec, kind: source_kind.clone() });
|
||||
} else {
|
||||
output.push(RenderedLine { text: styled_vec, kind: RenderedLineKind::Annotations });
|
||||
}
|
||||
styled_vec = vec![];
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
|
||||
while line >= self.text.len() {
|
||||
self.text.push(vec![]);
|
||||
self.styles.push(vec![]);
|
||||
}
|
||||
|
||||
if col < self.text[line].len() {
|
||||
self.text[line][col] = chr;
|
||||
self.styles[line][col] = style;
|
||||
} else {
|
||||
let mut i = self.text[line].len();
|
||||
while i < col {
|
||||
let s = match self.text[0].get(i) {
|
||||
Some(&'\t') => '\t',
|
||||
_ => ' '
|
||||
};
|
||||
self.text[line].push(s);
|
||||
self.styles[line].push(Style::NoStyle);
|
||||
i += 1;
|
||||
}
|
||||
self.text[line].push(chr);
|
||||
self.styles[line].push(style);
|
||||
}
|
||||
}
|
||||
|
||||
fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
|
||||
let mut n = col;
|
||||
for c in string.chars() {
|
||||
self.putc(line, n, c, style);
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn set_style(&mut self, line: usize, col: usize, style: Style) {
|
||||
if self.styles.len() > line && self.styles[line].len() > col {
|
||||
self.styles[line][col] = style;
|
||||
}
|
||||
}
|
||||
|
||||
fn append(&mut self, line: usize, string: &str, style: Style) {
|
||||
if line >= self.text.len() {
|
||||
self.puts(line, 0, string, style);
|
||||
} else {
|
||||
let col = self.text[line].len();
|
||||
self.puts(line, col, string, style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileInfo {
|
||||
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<RenderedLine> {
|
||||
let old_school = match self.format_mode {
|
||||
FormatMode::OriginalErrorFormat => true,
|
||||
FormatMode::NewErrorFormat => false,
|
||||
FormatMode::EnvironmentSelected => check_old_skool()
|
||||
};
|
||||
|
||||
// As a first step, we elide any instance of more than one
|
||||
// continuous unannotated line.
|
||||
|
||||
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(RenderedLine {
|
||||
text: vec![StyledString {
|
||||
text: lo.file.name.clone(),
|
||||
style: Style::FileNameStyle,
|
||||
}, StyledString {
|
||||
text: format!(":{}:{}", lo.line, lo.col.0 + 1),
|
||||
style: Style::LineAndColumn,
|
||||
}],
|
||||
kind: RenderedLineKind::PrimaryFileName,
|
||||
});
|
||||
output.push(RenderedLine {
|
||||
text: vec![StyledString {
|
||||
text: "".to_string(),
|
||||
style: Style::FileNameStyle,
|
||||
}],
|
||||
kind: RenderedLineKind::Annotations,
|
||||
});
|
||||
}
|
||||
None => {
|
||||
output.push(RenderedLine {
|
||||
text: vec![StyledString {
|
||||
text: self.file.name.clone(),
|
||||
style: Style::FileNameStyle,
|
||||
}],
|
||||
kind: RenderedLineKind::OtherFileName,
|
||||
});
|
||||
output.push(RenderedLine {
|
||||
text: vec![StyledString {
|
||||
text: "".to_string(),
|
||||
style: Style::FileNameStyle,
|
||||
}],
|
||||
kind: RenderedLineKind::Annotations,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(RenderedLine {
|
||||
text: 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::OldSkoolNote,
|
||||
}, StyledString {
|
||||
text: format!("{}", ann),
|
||||
style: Style::OldSkoolNoteText,
|
||||
}],
|
||||
kind: RenderedLineKind::Annotations,
|
||||
});
|
||||
}
|
||||
}
|
||||
rendered_lines[0].text.insert(0, StyledString {
|
||||
text: format!(":{} ", lo.line),
|
||||
style: Style::LineAndColumn,
|
||||
});
|
||||
rendered_lines[0].text.insert(0, StyledString {
|
||||
text: lo.file.name.clone(),
|
||||
style: Style::FileNameStyle,
|
||||
});
|
||||
let gap_amount =
|
||||
rendered_lines[0].text[0].text.len() +
|
||||
rendered_lines[0].text[1].text.len();
|
||||
assert!(rendered_lines.len() >= 2,
|
||||
"no annotations resulted from: {:?}",
|
||||
line);
|
||||
for i in 1..rendered_lines.len() {
|
||||
rendered_lines[i].text.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(RenderedLine::from((String::new(),
|
||||
Style::NoStyle,
|
||||
RenderedLineKind::Elision)));
|
||||
} else if let Some(line) = unannotated_line {
|
||||
output.append(&mut self.render_line(line));
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
|
||||
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(source_kind);
|
||||
}
|
||||
|
||||
// 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::OldSkoolNote
|
||||
});
|
||||
}
|
||||
else {
|
||||
styled_buffer.putc(1, p, '~',
|
||||
if annotation.is_primary {
|
||||
Style::UnderlinePrimary
|
||||
} else {
|
||||
Style::OldSkoolNote
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
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(source_kind);
|
||||
}
|
||||
if old_school {
|
||||
return styled_buffer.render(source_kind);
|
||||
}
|
||||
|
||||
// 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(source_kind);
|
||||
}
|
||||
|
||||
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(source_kind)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
ErrorCode,
|
||||
Level(Level),
|
||||
}
|
146
src/librustc_errors/styled_buffer.rs
Normal file
146
src/librustc_errors/styled_buffer.rs
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Code for creating styled buffers
|
||||
|
||||
use snippet::{Style, StyledString};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StyledBuffer {
|
||||
text: Vec<Vec<char>>,
|
||||
styles: Vec<Vec<Style>>,
|
||||
}
|
||||
|
||||
impl StyledBuffer {
|
||||
pub fn new() -> StyledBuffer {
|
||||
StyledBuffer {
|
||||
text: vec![],
|
||||
styles: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_tabs(&mut self, row: usize) {
|
||||
if row < self.text.len() {
|
||||
for i in row+1..self.text.len() {
|
||||
for j in 0..self.text[i].len() {
|
||||
if self.text[row].len() > j &&
|
||||
self.text[row][j] == '\t' &&
|
||||
self.text[i][j] == ' ' {
|
||||
self.text[i][j] = '\t';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&mut self) -> Vec<Vec<StyledString>> {
|
||||
let mut output: Vec<Vec<StyledString>> = vec![];
|
||||
let mut styled_vec: Vec<StyledString> = vec![];
|
||||
|
||||
//before we render, do a little patch-up work to support tabs
|
||||
self.copy_tabs(3);
|
||||
|
||||
for (row, row_style) in self.text.iter().zip(&self.styles) {
|
||||
let mut current_style = Style::NoStyle;
|
||||
let mut current_text = String::new();
|
||||
|
||||
for (&c, &s) in row.iter().zip(row_style) {
|
||||
if s != current_style {
|
||||
if !current_text.is_empty() {
|
||||
styled_vec.push(StyledString {
|
||||
text: current_text,
|
||||
style: current_style,
|
||||
});
|
||||
}
|
||||
current_style = s;
|
||||
current_text = String::new();
|
||||
}
|
||||
current_text.push(c);
|
||||
}
|
||||
if !current_text.is_empty() {
|
||||
styled_vec.push(StyledString {
|
||||
text: current_text,
|
||||
style: current_style,
|
||||
});
|
||||
}
|
||||
|
||||
// We're done with the row, push and keep going
|
||||
output.push(styled_vec);
|
||||
|
||||
styled_vec = vec![];
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn ensure_lines(&mut self, line: usize) {
|
||||
while line >= self.text.len() {
|
||||
self.text.push(vec![]);
|
||||
self.styles.push(vec![]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
|
||||
self.ensure_lines(line);
|
||||
if col < self.text[line].len() {
|
||||
self.text[line][col] = chr;
|
||||
self.styles[line][col] = style;
|
||||
} else {
|
||||
let mut i = self.text[line].len();
|
||||
while i < col {
|
||||
self.text[line].push(' ');
|
||||
self.styles[line].push(Style::NoStyle);
|
||||
i += 1;
|
||||
}
|
||||
self.text[line].push(chr);
|
||||
self.styles[line].push(style);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
|
||||
let mut n = col;
|
||||
for c in string.chars() {
|
||||
self.putc(line, n, c, style);
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_style(&mut self, line: usize, col: usize, style: Style) {
|
||||
if self.styles.len() > line && self.styles[line].len() > col {
|
||||
self.styles[line][col] = style;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepend(&mut self, line: usize, string: &str, style: Style) {
|
||||
self.ensure_lines(line);
|
||||
let string_len = string.len();
|
||||
|
||||
// Push the old content over to make room for new content
|
||||
for _ in 0..string_len {
|
||||
self.styles[line].insert(0, Style::NoStyle);
|
||||
self.text[line].insert(0, ' ');
|
||||
}
|
||||
|
||||
self.puts(line, 0, string, style);
|
||||
}
|
||||
|
||||
pub fn append(&mut self, line: usize, string: &str, style: Style) {
|
||||
if line >= self.text.len() {
|
||||
self.puts(line, 0, string, style);
|
||||
} else {
|
||||
let col = self.text[line].len();
|
||||
self.puts(line, col, string, style);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_lines(&self) -> usize {
|
||||
self.text.len()
|
||||
}
|
||||
}
|
@ -19,8 +19,8 @@ use llvm::SMDiagnosticRef;
|
||||
use {CrateTranslation, ModuleTranslation};
|
||||
use util::common::time;
|
||||
use util::common::path2cstr;
|
||||
use errors::{self, Handler, Level, RenderSpan};
|
||||
use errors::emitter::CoreEmitter;
|
||||
use errors::{self, Handler, Level, DiagnosticBuilder};
|
||||
use errors::emitter::Emitter;
|
||||
use syntax_pos::MultiSpan;
|
||||
|
||||
use std::collections::HashMap;
|
||||
@ -100,23 +100,23 @@ impl SharedEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreEmitter for SharedEmitter {
|
||||
fn emit_message(&mut self,
|
||||
_rsp: &RenderSpan,
|
||||
msg: &str,
|
||||
code: Option<&str>,
|
||||
lvl: Level,
|
||||
_is_header: bool,
|
||||
_show_snippet: bool) {
|
||||
impl Emitter for SharedEmitter {
|
||||
fn emit(&mut self, db: &DiagnosticBuilder) {
|
||||
self.buffer.lock().unwrap().push(Diagnostic {
|
||||
msg: msg.to_string(),
|
||||
code: code.map(|s| s.to_string()),
|
||||
lvl: lvl,
|
||||
msg: db.message.to_string(),
|
||||
code: db.code.clone(),
|
||||
lvl: db.level,
|
||||
});
|
||||
for child in &db.children {
|
||||
self.buffer.lock().unwrap().push(Diagnostic {
|
||||
msg: child.message.to_string(),
|
||||
code: None,
|
||||
lvl: child.level,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// On android, we by default compile for armv7 processors. This enables
|
||||
// things like double word CAS instructions (rather than emulating them)
|
||||
// which are *far* more efficient. This is obviously undesirable in some
|
||||
|
@ -131,7 +131,7 @@ pub fn run_core(search_paths: SearchPaths,
|
||||
None,
|
||||
true,
|
||||
false,
|
||||
codemap.clone());
|
||||
Some(codemap.clone()));
|
||||
|
||||
let dep_graph = DepGraph::new(false);
|
||||
let _ignore = dep_graph.in_ignore();
|
||||
|
@ -77,7 +77,7 @@ pub fn run(input: &str,
|
||||
None,
|
||||
true,
|
||||
false,
|
||||
codemap.clone());
|
||||
Some(codemap.clone()));
|
||||
|
||||
let dep_graph = DepGraph::new(false);
|
||||
let _ignore = dep_graph.in_ignore();
|
||||
@ -229,7 +229,7 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
|
||||
let codemap = Rc::new(CodeMap::new());
|
||||
let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()),
|
||||
None,
|
||||
codemap.clone(),
|
||||
Some(codemap.clone()),
|
||||
errors::snippet::FormatMode::EnvironmentSelected);
|
||||
let old = io::set_panic(box Sink(data.clone()));
|
||||
let _bomb = Bomb(data.clone(), old.unwrap_or(box io::stdout()));
|
||||
|
@ -827,12 +827,6 @@ impl CodeMapper for CodeMap {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use errors::{Level, CodeSuggestion};
|
||||
use errors::emitter::EmitterWriter;
|
||||
use errors::snippet::{SnippetData, RenderedLine, FormatMode};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::io::{self, Write};
|
||||
use std::str::from_utf8;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
@ -1122,24 +1116,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn splice(start: Span, end: Span) -> Span {
|
||||
Span {
|
||||
lo: start.lo,
|
||||
hi: end.hi,
|
||||
expn_id: NO_EXPANSION,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_string(lines: &[RenderedLine]) -> String {
|
||||
lines.iter()
|
||||
.flat_map(|rl| {
|
||||
rl.text.iter()
|
||||
.map(|s| &s.text[..])
|
||||
.chain(Some("\n"))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn init_expansion_chain(cm: &CodeMap) -> Span {
|
||||
// Creates an expansion chain containing two recursive calls
|
||||
// root -> expA -> expA -> expB -> expB -> end
|
||||
@ -1219,761 +1195,4 @@ r"blork2.rs:2:1: 2:12
|
||||
";
|
||||
assert_eq!(sstr, res_str);
|
||||
}
|
||||
|
||||
struct Sink(Arc<Mutex<Vec<u8>>>);
|
||||
impl Write for Sink {
|
||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
Write::write(&mut *self.0.lock().unwrap(), data)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
// Diagnostic doesn't align properly in span where line number increases by one digit
|
||||
#[test]
|
||||
fn test_hilight_suggestion_issue_11715() {
|
||||
let data = Arc::new(Mutex::new(Vec::new()));
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())),
|
||||
None,
|
||||
cm.clone(),
|
||||
FormatMode::NewErrorFormat);
|
||||
let content = "abcdefg
|
||||
koksi
|
||||
line3
|
||||
line4
|
||||
cinq
|
||||
line6
|
||||
line7
|
||||
line8
|
||||
line9
|
||||
line10
|
||||
e-lä-vän
|
||||
tolv
|
||||
dreizehn
|
||||
";
|
||||
let file = cm.new_filemap_and_lines("dummy.txt", None, content);
|
||||
let start = file.lines.borrow()[10];
|
||||
let end = file.lines.borrow()[11];
|
||||
let sp = mk_sp(start, end);
|
||||
let lvl = Level::Error;
|
||||
println!("highlight_lines");
|
||||
ew.highlight_lines(&sp.into(), lvl).unwrap();
|
||||
println!("done");
|
||||
let vec = data.lock().unwrap().clone();
|
||||
let vec: &[u8] = &vec;
|
||||
let str = from_utf8(vec).unwrap();
|
||||
println!("r#\"\n{}\"#", str);
|
||||
assert_eq!(str, &r#"
|
||||
--> dummy.txt:11:1
|
||||
|>
|
||||
11 |> e-lä-vän
|
||||
|> ^
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_span_splice() {
|
||||
// Test that a `MultiSpan` containing a single span splices a substition correctly
|
||||
let cm = CodeMap::new();
|
||||
let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
|
||||
let selection = " \n ~~\n~~~\n~~~~~ \n \n";
|
||||
cm.new_filemap_and_lines("blork.rs", None, inputtext);
|
||||
let sp = span_from_selection(inputtext, selection);
|
||||
let msp: MultiSpan = sp.into();
|
||||
|
||||
// check that we are extracting the text we thought we were extracting
|
||||
assert_eq!(&cm.span_to_snippet(sp).unwrap(), "BB\nCCC\nDDDDD");
|
||||
|
||||
let substitute = "ZZZZZZ".to_owned();
|
||||
let expected = "bbbbZZZZZZddddd";
|
||||
let suggest = CodeSuggestion {
|
||||
msp: msp,
|
||||
substitutes: vec![substitute],
|
||||
};
|
||||
assert_eq!(suggest.splice_lines(&cm), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_span_splice() {
|
||||
// Test that a `MultiSpan` containing multiple spans splices a substition correctly
|
||||
let cm = CodeMap::new();
|
||||
let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
|
||||
let selection1 = " \n \n \n \n ~ \n"; // intentionally out of order
|
||||
let selection2 = " \n ~~\n~~~\n~~~~~ \n \n";
|
||||
cm.new_filemap_and_lines("blork.rs", None, inputtext);
|
||||
let sp1 = span_from_selection(inputtext, selection1);
|
||||
let sp2 = span_from_selection(inputtext, selection2);
|
||||
let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]);
|
||||
|
||||
let expected = "bbbbZZZZZZddddd\neXYZe";
|
||||
let suggest = CodeSuggestion {
|
||||
msp: msp,
|
||||
substitutes: vec!["ZZZZZZ".to_owned(),
|
||||
"XYZ".to_owned()]
|
||||
};
|
||||
|
||||
assert_eq!(suggest.splice_lines(&cm), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multispan_highlight() {
|
||||
let data = Arc::new(Mutex::new(Vec::new()));
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())),
|
||||
None,
|
||||
cm.clone(),
|
||||
FormatMode::NewErrorFormat);
|
||||
|
||||
let inp = "_____aaaaaa____bbbbbb__cccccdd_";
|
||||
let sp1 = " ~~~~~~ ";
|
||||
let sp2 = " ~~~~~~ ";
|
||||
let sp3 = " ~~~~~ ";
|
||||
let sp4 = " ~~~~ ";
|
||||
let sp34 = " ~~~~~~~ ";
|
||||
|
||||
let expect_start = &r#"
|
||||
--> dummy.txt:1:6
|
||||
|>
|
||||
1 |> _____aaaaaa____bbbbbb__cccccdd_
|
||||
|> ^^^^^^ ^^^^^^ ^^^^^^^
|
||||
"#[1..];
|
||||
|
||||
let span = |sp, expected| {
|
||||
let sp = span_from_selection(inp, sp);
|
||||
assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected);
|
||||
sp
|
||||
};
|
||||
cm.new_filemap_and_lines("dummy.txt", None, inp);
|
||||
let sp1 = span(sp1, "aaaaaa");
|
||||
let sp2 = span(sp2, "bbbbbb");
|
||||
let sp3 = span(sp3, "ccccc");
|
||||
let sp4 = span(sp4, "ccdd");
|
||||
let sp34 = span(sp34, "cccccdd");
|
||||
|
||||
let spans = vec![sp1, sp2, sp3, sp4];
|
||||
|
||||
let test = |expected, highlight: &mut FnMut()| {
|
||||
data.lock().unwrap().clear();
|
||||
highlight();
|
||||
let vec = data.lock().unwrap().clone();
|
||||
let actual = from_utf8(&vec[..]).unwrap();
|
||||
println!("actual=\n{}", actual);
|
||||
assert_eq!(actual, expected);
|
||||
};
|
||||
|
||||
let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]);
|
||||
test(expect_start, &mut || {
|
||||
diag.highlight_lines(&msp, Level::Error).unwrap();
|
||||
});
|
||||
test(expect_start, &mut || {
|
||||
let msp = MultiSpan::from_spans(spans.clone());
|
||||
diag.highlight_lines(&msp, Level::Error).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_huge_multispan_highlight() {
|
||||
let data = Arc::new(Mutex::new(Vec::new()));
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())),
|
||||
None,
|
||||
cm.clone(),
|
||||
FormatMode::NewErrorFormat);
|
||||
|
||||
let inp = "aaaaa\n\
|
||||
aaaaa\n\
|
||||
aaaaa\n\
|
||||
bbbbb\n\
|
||||
ccccc\n\
|
||||
xxxxx\n\
|
||||
yyyyy\n\
|
||||
_____\n\
|
||||
ddd__eee_\n\
|
||||
elided\n\
|
||||
__f_gg";
|
||||
let file = cm.new_filemap_and_lines("dummy.txt", None, inp);
|
||||
|
||||
let span = |lo, hi, (off_lo, off_hi)| {
|
||||
let lines = file.lines.borrow();
|
||||
let (mut lo, mut hi): (BytePos, BytePos) = (lines[lo], lines[hi]);
|
||||
lo.0 += off_lo;
|
||||
hi.0 += off_hi;
|
||||
mk_sp(lo, hi)
|
||||
};
|
||||
let sp0 = span(4, 6, (0, 5));
|
||||
let sp1 = span(0, 6, (0, 5));
|
||||
let sp2 = span(8, 8, (0, 3));
|
||||
let sp3 = span(8, 8, (5, 8));
|
||||
let sp4 = span(10, 10, (2, 3));
|
||||
let sp5 = span(10, 10, (4, 6));
|
||||
|
||||
let expect0 = &r#"
|
||||
--> dummy.txt:5:1
|
||||
|>
|
||||
5 |> ccccc
|
||||
|> ^
|
||||
...
|
||||
9 |> ddd__eee_
|
||||
|> ^^^ ^^^
|
||||
10 |> elided
|
||||
11 |> __f_gg
|
||||
|> ^ ^^
|
||||
"#[1..];
|
||||
|
||||
let expect = &r#"
|
||||
--> dummy.txt:1:1
|
||||
|>
|
||||
1 |> aaaaa
|
||||
|> ^
|
||||
...
|
||||
9 |> ddd__eee_
|
||||
|> ^^^ ^^^
|
||||
10 |> elided
|
||||
11 |> __f_gg
|
||||
|> ^ ^^
|
||||
"#[1..];
|
||||
|
||||
macro_rules! test {
|
||||
($expected: expr, $highlight: expr) => ({
|
||||
data.lock().unwrap().clear();
|
||||
$highlight();
|
||||
let vec = data.lock().unwrap().clone();
|
||||
let actual = from_utf8(&vec[..]).unwrap();
|
||||
println!("actual:");
|
||||
println!("{}", actual);
|
||||
println!("expected:");
|
||||
println!("{}", $expected);
|
||||
assert_eq!(&actual[..], &$expected[..]);
|
||||
});
|
||||
}
|
||||
|
||||
let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]);
|
||||
let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]);
|
||||
|
||||
test!(expect0, || {
|
||||
diag.highlight_lines(&msp0, Level::Error).unwrap();
|
||||
});
|
||||
test!(expect, || {
|
||||
diag.highlight_lines(&msp, Level::Error).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tab() {
|
||||
let file_text = "
|
||||
fn foo() {
|
||||
\tbar;
|
||||
}
|
||||
";
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span_bar = cm.span_substr(&foo, file_text, "bar", 0);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, Some(span_bar), FormatMode::NewErrorFormat);
|
||||
snippet.push(span_bar, true, None);
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
let text = make_string(&lines);
|
||||
assert_eq!(&text[..], &"
|
||||
--> foo.rs:3:2
|
||||
|>
|
||||
3 |> \tbar;
|
||||
|> \t^^^
|
||||
"[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_line() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
vec.push(vec.pop().unwrap());
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
|
||||
let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
|
||||
let span_semi = cm.span_substr(&foo, file_text, ";", 0);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
|
||||
snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
|
||||
snippet.push(span_vec1, false, Some(format!("error occurs here")));
|
||||
snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
|
||||
let text: String = make_string(&lines);
|
||||
|
||||
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
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_files() {
|
||||
let file_text_foo = r#"
|
||||
fn foo() {
|
||||
vec.push(vec.pop().unwrap());
|
||||
}
|
||||
"#;
|
||||
|
||||
let file_text_bar = r#"
|
||||
fn bar() {
|
||||
// these blank links here
|
||||
// serve to ensure that the line numbers
|
||||
// from bar.rs
|
||||
// require more digits
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
vec.push();
|
||||
|
||||
// this line will get elided
|
||||
|
||||
vec.pop().unwrap());
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo_map = cm.new_filemap_and_lines("foo.rs", None, file_text_foo);
|
||||
let span_foo_vec0 = cm.span_substr(&foo_map, file_text_foo, "vec", 0);
|
||||
let span_foo_vec1 = cm.span_substr(&foo_map, file_text_foo, "vec", 1);
|
||||
let span_foo_semi = cm.span_substr(&foo_map, file_text_foo, ";", 0);
|
||||
|
||||
let bar_map = cm.new_filemap_and_lines("bar.rs", None, file_text_bar);
|
||||
let span_bar_vec0 = cm.span_substr(&bar_map, file_text_bar, "vec", 0);
|
||||
let span_bar_vec1 = cm.span_substr(&bar_map, file_text_bar, "vec", 1);
|
||||
let span_bar_semi = cm.span_substr(&bar_map, file_text_bar, ";", 0);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, Some(span_foo_vec1), FormatMode::NewErrorFormat);
|
||||
snippet.push(span_foo_vec0, false, Some(format!("a")));
|
||||
snippet.push(span_foo_vec1, true, Some(format!("b")));
|
||||
snippet.push(span_foo_semi, false, Some(format!("c")));
|
||||
snippet.push(span_bar_vec0, false, Some(format!("d")));
|
||||
snippet.push(span_bar_vec1, false, Some(format!("e")));
|
||||
snippet.push(span_bar_semi, false, Some(format!("f")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
|
||||
let text: String = make_string(&lines);
|
||||
|
||||
println!("text=\n{}", text);
|
||||
|
||||
// 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
|
||||
::: bar.rs
|
||||
|>
|
||||
17 |> vec.push();
|
||||
|> --- - f
|
||||
|> |
|
||||
|> d
|
||||
...
|
||||
21 |> vec.pop().unwrap());
|
||||
|> --- e
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_line() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
let name = find_id(&data, 22).unwrap();
|
||||
|
||||
// Add one more item we forgot to the vector. Silly us.
|
||||
data.push(Data { name: format!("Hera"), id: 66 });
|
||||
|
||||
// Print everything out.
|
||||
println!("Name: {:?}", name);
|
||||
println!("Data: {:?}", data);
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span_data0 = cm.span_substr(&foo, file_text, "data", 0);
|
||||
let span_data1 = cm.span_substr(&foo, file_text, "data", 1);
|
||||
let span_rbrace = cm.span_substr(&foo, file_text, "}", 3);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
|
||||
snippet.push(span_data0, false, Some(format!("immutable borrow begins here")));
|
||||
snippet.push(span_data1, false, Some(format!("mutable borrow occurs here")));
|
||||
snippet.push(span_rbrace, false, Some(format!("immutable borrow ends here")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
|
||||
let text: String = make_string(&lines);
|
||||
|
||||
println!("text=\n{}", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
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
|
||||
...
|
||||
11 |> }
|
||||
|> - immutable borrow ends here
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlapping() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
vec.push(vec.pop().unwrap());
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span0 = cm.span_substr(&foo, file_text, "vec.push", 0);
|
||||
let span1 = cm.span_substr(&foo, file_text, "vec", 0);
|
||||
let span2 = cm.span_substr(&foo, file_text, "ec.push", 0);
|
||||
let span3 = cm.span_substr(&foo, file_text, "unwrap", 0);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
|
||||
snippet.push(span0, false, Some(format!("A")));
|
||||
snippet.push(span1, false, Some(format!("B")));
|
||||
snippet.push(span2, false, Some(format!("C")));
|
||||
snippet.push(span3, false, Some(format!("D")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
let text: String = make_string(&lines);
|
||||
|
||||
println!("text=r#\"\n{}\".trim_left()", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> vec.push(vec.pop().unwrap());
|
||||
|> -------- ------ D
|
||||
|> ||
|
||||
|> |C
|
||||
|> A
|
||||
|> B
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_line_out_of_order() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
vec.push(vec.pop().unwrap());
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
|
||||
let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
|
||||
let span_semi = cm.span_substr(&foo, file_text, ";", 0);
|
||||
|
||||
// intentionally don't push the snippets left to right
|
||||
let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
|
||||
snippet.push(span_vec1, false, Some(format!("error occurs here")));
|
||||
snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
|
||||
snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
let text: String = make_string(&lines);
|
||||
|
||||
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
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn elide_unnecessary_lines() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
let mut vec = vec![0, 1, 2];
|
||||
let mut vec2 = vec;
|
||||
vec2.push(3);
|
||||
vec2.push(4);
|
||||
vec2.push(5);
|
||||
vec2.push(6);
|
||||
vec.push(7);
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span_vec0 = cm.span_substr(&foo, file_text, "vec", 3);
|
||||
let span_vec1 = cm.span_substr(&foo, file_text, "vec", 8);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
|
||||
snippet.push(span_vec0, false, Some(format!("`vec` moved here because it \
|
||||
has type `collections::vec::Vec<i32>`")));
|
||||
snippet.push(span_vec1, false, Some(format!("use of moved value: `vec`")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
let text: String = make_string(&lines);
|
||||
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>`
|
||||
...
|
||||
9 |> vec.push(7);
|
||||
|> --- use of moved value: `vec`
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spans_without_labels() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
let mut vec = vec![0, 1, 2];
|
||||
let mut vec2 = vec;
|
||||
vec2.push(3);
|
||||
vec2.push(4);
|
||||
vec2.push(5);
|
||||
vec2.push(6);
|
||||
vec.push(7);
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
|
||||
for i in 0..4 {
|
||||
let span_veci = cm.span_substr(&foo, file_text, "vec", i);
|
||||
snippet.push(span_veci, false, None);
|
||||
}
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
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;
|
||||
|> --- ---
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_long_selection() {
|
||||
let file_text = r#"
|
||||
impl SomeTrait for () {
|
||||
fn foo(x: u32) {
|
||||
// impl 1
|
||||
// impl 2
|
||||
// impl 3
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
|
||||
let fn_span = cm.span_substr(&foo, file_text, "fn", 0);
|
||||
let rbrace_span = cm.span_substr(&foo, file_text, "}", 0);
|
||||
snippet.push(splice(fn_span, rbrace_span), false, None);
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> fn foo(x: u32) {
|
||||
|> -
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_overlap_label() {
|
||||
// Test that we don't put `x_span` to the right of its highlight,
|
||||
// since there is another highlight that overlaps it.
|
||||
|
||||
let file_text = r#"
|
||||
fn foo(x: u32) {
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
|
||||
let fn_span = cm.span_substr(&foo, file_text, "fn foo(x: u32)", 0);
|
||||
let x_span = cm.span_substr(&foo, file_text, "x", 0);
|
||||
snippet.push(fn_span, false, Some(format!("fn_span")));
|
||||
snippet.push(x_span, false, Some(format!("x_span")));
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
2 |> fn foo(x: u32) {
|
||||
|> --------------
|
||||
|> | |
|
||||
|> | x_span
|
||||
|> fn_span
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_overlap_label2() {
|
||||
// Test that we don't put `x_span` to the right of its highlight,
|
||||
// since there is another highlight that overlaps it. In this
|
||||
// case, the overlap is only at the beginning, but it's still
|
||||
// better to show the beginning more clearly.
|
||||
|
||||
let file_text = r#"
|
||||
fn foo(x: u32) {
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
|
||||
let fn_span = cm.span_substr(&foo, file_text, "fn foo(x", 0);
|
||||
let x_span = cm.span_substr(&foo, file_text, "x: u32)", 0);
|
||||
snippet.push(fn_span, false, Some(format!("fn_span")));
|
||||
snippet.push(x_span, false, Some(format!("x_span")));
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
2 |> fn foo(x: u32) {
|
||||
|> --------------
|
||||
|> | |
|
||||
|> | x_span
|
||||
|> fn_span
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_overlap_label3() {
|
||||
// Test that we don't put `x_span` to the right of its highlight,
|
||||
// since there is another highlight that overlaps it. In this
|
||||
// case, the overlap is only at the beginning, but it's still
|
||||
// better to show the beginning more clearly.
|
||||
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
let closure = || {
|
||||
inner
|
||||
};
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
|
||||
|
||||
let closure_span = {
|
||||
let closure_start_span = cm.span_substr(&foo, file_text, "||", 0);
|
||||
let closure_end_span = cm.span_substr(&foo, file_text, "}", 0);
|
||||
splice(closure_start_span, closure_end_span)
|
||||
};
|
||||
|
||||
let inner_span = cm.span_substr(&foo, file_text, "inner", 0);
|
||||
|
||||
snippet.push(closure_span, false, Some(format!("foo")));
|
||||
snippet.push(inner_span, false, Some(format!("bar")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> let closure = || {
|
||||
|> - foo
|
||||
4 |> inner
|
||||
|> ----- bar
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_empty() {
|
||||
// In one of the unit tests, we found that the parser sometimes
|
||||
// gives empty spans, and in particular it supplied an EOF span
|
||||
// like this one, which points at the very end. We want to
|
||||
// fallback gracefully in this case.
|
||||
|
||||
let file_text = r#"
|
||||
fn main() {
|
||||
struct Foo;
|
||||
|
||||
impl !Sync for Foo {}
|
||||
|
||||
unsafe impl Send for &'static Foo {
|
||||
// error: cross-crate traits with a default impl, like `core::marker::Send`,
|
||||
// can only be implemented for a struct/enum type, not
|
||||
// `&'static Foo`
|
||||
}"#;
|
||||
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut rbrace_span = cm.span_substr(&foo, file_text, "}", 1);
|
||||
rbrace_span.lo = rbrace_span.hi;
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(),
|
||||
Some(rbrace_span),
|
||||
FormatMode::NewErrorFormat);
|
||||
snippet.push(rbrace_span, false, None);
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
--> foo.rs:11:2
|
||||
|>
|
||||
11 |> }
|
||||
|> -
|
||||
"#[1..]);
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
use codemap::CodeMap;
|
||||
use syntax_pos::{self, MacroBacktrace, Span, SpanLabel, MultiSpan};
|
||||
use errors::registry::Registry;
|
||||
use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper};
|
||||
use errors::{DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper};
|
||||
use errors::emitter::Emitter;
|
||||
|
||||
use std::rc::Rc;
|
||||
@ -53,14 +53,7 @@ impl JsonEmitter {
|
||||
}
|
||||
|
||||
impl Emitter for JsonEmitter {
|
||||
fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, level: Level) {
|
||||
let data = Diagnostic::new(span, msg, code, level, self);
|
||||
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
|
||||
panic!("failed to print diagnostics: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_struct(&mut self, db: &DiagnosticBuilder) {
|
||||
fn emit(&mut self, db: &DiagnosticBuilder) {
|
||||
let data = Diagnostic::from_diagnostic_builder(db, self);
|
||||
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
|
||||
panic!("failed to print diagnostics: {:?}", e);
|
||||
@ -146,22 +139,6 @@ struct DiagnosticCode {
|
||||
}
|
||||
|
||||
impl<'a> Diagnostic<'a> {
|
||||
fn new(msp: &MultiSpan,
|
||||
msg: &'a str,
|
||||
code: Option<&str>,
|
||||
level: Level,
|
||||
je: &JsonEmitter)
|
||||
-> Diagnostic<'a> {
|
||||
Diagnostic {
|
||||
message: msg,
|
||||
code: DiagnosticCode::map_opt_string(code.map(|c| c.to_owned()), je),
|
||||
level: level.to_str(),
|
||||
spans: DiagnosticSpan::from_multispan(msp, je),
|
||||
children: vec![],
|
||||
rendered: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder,
|
||||
je: &JsonEmitter)
|
||||
-> Diagnostic<'c> {
|
||||
|
@ -1686,7 +1686,7 @@ mod tests {
|
||||
// FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
|
||||
let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()),
|
||||
None,
|
||||
cm,
|
||||
Some(cm),
|
||||
errors::snippet::FormatMode::EnvironmentSelected);
|
||||
errors::Handler::with_emitter(true, false, Box::new(emitter))
|
||||
}
|
||||
|
@ -50,7 +50,11 @@ pub struct ParseSess {
|
||||
impl ParseSess {
|
||||
pub fn new() -> ParseSess {
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let handler = Handler::with_tty_emitter(ColorConfig::Auto, None, true, false, cm.clone());
|
||||
let handler = Handler::with_tty_emitter(ColorConfig::Auto,
|
||||
None,
|
||||
true,
|
||||
false,
|
||||
Some(cm.clone()));
|
||||
ParseSess::with_span_handler(handler, cm)
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ use std::rc::Rc;
|
||||
|
||||
use codemap::{self, CodeMap, ExpnInfo, NameAndSpan, MacroAttribute};
|
||||
use errors;
|
||||
use errors::snippet::{RenderedLine, SnippetData};
|
||||
use errors::snippet::{SnippetData};
|
||||
use config;
|
||||
use entry::{self, EntryPointType};
|
||||
use ext::base::{ExtCtxt, DummyMacroLoader};
|
||||
|
@ -568,7 +568,7 @@ impl Sub for CharPos {
|
||||
//
|
||||
|
||||
/// A source code location used for error reporting
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Loc {
|
||||
/// Information about the original source
|
||||
pub file: Rc<FileMap>,
|
||||
|
19
src/test/ui/codemap_tests/empty_span.rs
Normal file
19
src/test/ui/codemap_tests/empty_span.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
#![feature(optin_builtin_traits)]
|
||||
fn main() {
|
||||
struct Foo;
|
||||
|
||||
impl !Sync for Foo {}
|
||||
|
||||
unsafe impl Send for &'static Foo { }
|
||||
}
|
8
src/test/ui/codemap_tests/empty_span.stderr
Normal file
8
src/test/ui/codemap_tests/empty_span.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `&'static main::Foo`
|
||||
--> $DIR/empty_span.rs:18:5
|
||||
|
|
||||
18 | unsafe impl Send for &'static Foo { }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
104
src/test/ui/codemap_tests/huge_multispan_highlight.rs
Normal file
104
src/test/ui/codemap_tests/huge_multispan_highlight.rs
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
|
||||
fn main() {
|
||||
let x = "foo";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let y = &mut x;
|
||||
}
|
||||
|
||||
|
||||
|
11
src/test/ui/codemap_tests/huge_multispan_highlight.stderr
Normal file
11
src/test/ui/codemap_tests/huge_multispan_highlight.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
error: cannot borrow immutable local variable `x` as mutable
|
||||
--> $DIR/huge_multispan_highlight.rs:100:18
|
||||
|
|
||||
14 | let x = "foo";
|
||||
| - use `mut x` here to make mutable
|
||||
...
|
||||
100 | let y = &mut x;
|
||||
| ^ cannot borrow mutably
|
||||
|
||||
error: aborting due to previous error
|
||||
|
104
src/test/ui/codemap_tests/issue-11715.rs
Normal file
104
src/test/ui/codemap_tests/issue-11715.rs
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fn main() {
|
||||
let mut x = "foo";
|
||||
let y = &mut x;
|
||||
let z = &mut x;
|
||||
}
|
||||
|
||||
|
||||
|
12
src/test/ui/codemap_tests/issue-11715.stderr
Normal file
12
src/test/ui/codemap_tests/issue-11715.stderr
Normal file
@ -0,0 +1,12 @@
|
||||
error[E0499]: cannot borrow `x` as mutable more than once at a time
|
||||
--> $DIR/issue-11715.rs:100:18
|
||||
|
|
||||
99 | let y = &mut x;
|
||||
| - first mutable borrow occurs here
|
||||
100 | let z = &mut x;
|
||||
| ^ second mutable borrow occurs here
|
||||
101 | }
|
||||
| - first borrow ends here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
16
src/test/ui/codemap_tests/one_line.rs
Normal file
16
src/test/ui/codemap_tests/one_line.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
|
||||
fn main() {
|
||||
let mut v = vec![Some("foo"), Some("bar")];
|
||||
v.push(v.pop().unwrap());
|
||||
}
|
11
src/test/ui/codemap_tests/one_line.stderr
Normal file
11
src/test/ui/codemap_tests/one_line.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
error[E0499]: cannot borrow `v` as mutable more than once at a time
|
||||
--> $DIR/one_line.rs:15:12
|
||||
|
|
||||
15 | v.push(v.pop().unwrap());
|
||||
| - ^ - first borrow ends here
|
||||
| | |
|
||||
| | second mutable borrow occurs here
|
||||
| first mutable borrow occurs here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
24
src/test/ui/codemap_tests/overlapping_spans.rs
Normal file
24
src/test/ui/codemap_tests/overlapping_spans.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
#[derive(Debug)]
|
||||
struct Foo { }
|
||||
|
||||
struct S {f:String}
|
||||
impl Drop for S {
|
||||
fn drop(&mut self) { println!("{}", self.f); }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
match (S {f:"foo".to_string()}) {
|
||||
S {f:_s} => {}
|
||||
}
|
||||
}
|
11
src/test/ui/codemap_tests/overlapping_spans.stderr
Normal file
11
src/test/ui/codemap_tests/overlapping_spans.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
|
||||
--> $DIR/overlapping_spans.rs:22:9
|
||||
|
|
||||
22 | S {f:_s} => {}
|
||||
| ^^^^^--^
|
||||
| | |
|
||||
| | hint: to prevent move, use `ref _s` or `ref mut _s`
|
||||
| cannot move out of here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
16
src/test/ui/codemap_tests/tab.rs
Normal file
16
src/test/ui/codemap_tests/tab.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
// ignore-tidy-tab
|
||||
fn main() {
|
||||
bar;
|
||||
}
|
||||
|
8
src/test/ui/codemap_tests/tab.stderr
Normal file
8
src/test/ui/codemap_tests/tab.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error[E0425]: unresolved name `bar`
|
||||
--> $DIR/tab.rs:14:2
|
||||
|
|
||||
14 | \tbar;
|
||||
| \t^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
18
src/test/ui/codemap_tests/two_files.rs
Normal file
18
src/test/ui/codemap_tests/two_files.rs
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
include!("two_files_data.rs");
|
||||
|
||||
struct Baz { }
|
||||
|
||||
impl Bar for Baz { }
|
||||
|
||||
fn main() { }
|
13
src/test/ui/codemap_tests/two_files.stderr
Normal file
13
src/test/ui/codemap_tests/two_files.stderr
Normal file
@ -0,0 +1,13 @@
|
||||
error[E0404]: `Bar` is not a trait
|
||||
--> $DIR/two_files.rs:16:6
|
||||
|
|
||||
16 | impl Bar for Baz { }
|
||||
| ^^^ `Bar` is not a trait
|
||||
|
|
||||
::: $DIR/two_files_data.rs
|
||||
|
|
||||
15 | type Bar = Foo;
|
||||
| --------------- type aliases cannot be used for traits
|
||||
|
||||
error: cannot continue compilation due to previous error
|
||||
|
16
src/test/ui/codemap_tests/two_files_data.rs
Normal file
16
src/test/ui/codemap_tests/two_files_data.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
// ignore-test
|
||||
trait Foo { }
|
||||
|
||||
type Bar = Foo;
|
||||
|
14
src/test/ui/codemap_tests/unicode.rs
Normal file
14
src/test/ui/codemap_tests/unicode.rs
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
extern "路濫狼á́́" fn foo() {}
|
||||
|
||||
fn main() { }
|
8
src/test/ui/codemap_tests/unicode.stderr
Normal file
8
src/test/ui/codemap_tests/unicode.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error: invalid ABI: expected one of [cdecl, stdcall, fastcall, vectorcall, aapcs, win64, Rust, C, system, rust-intrinsic, rust-call, platform-intrinsic], found `路濫狼á́́`
|
||||
--> $DIR/unicode.rs:12:8
|
||||
|
|
||||
12 | extern "路濫狼á́́" fn foo() {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1,15 +1,16 @@
|
||||
error: mismatched types [--explain E0308]
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-26480.rs:27:19
|
||||
|>
|
||||
27 |> $arr.len() * size_of($arr[0]));
|
||||
|> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u64, found usize
|
||||
$DIR/issue-26480.rs:38:5: 38:19: note: in this expansion of write! (defined in $DIR/issue-26480.rs)
|
||||
|
|
||||
27 | $arr.len() * size_of($arr[0]));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u64, found usize
|
||||
$DIR/issue-26480.rs:38:5: 38:19 note: in this expansion of write! (defined in $DIR/issue-26480.rs)
|
||||
|
||||
error: non-scalar cast: `_` as `()`
|
||||
--> $DIR/issue-26480.rs:33:19
|
||||
|>
|
||||
33 |> ($x:expr) => ($x as ())
|
||||
|> ^^^^^^^^
|
||||
$DIR/issue-26480.rs:39:5: 39:14: note: in this expansion of cast! (defined in $DIR/issue-26480.rs)
|
||||
|
|
||||
33 | ($x:expr) => ($x as ())
|
||||
| ^^^^^^^^
|
||||
$DIR/issue-26480.rs:39:5: 39:14 note: in this expansion of cast! (defined in $DIR/issue-26480.rs)
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
error: mismatched types [--explain E0308]
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/main.rs:14:18
|
||||
|>
|
||||
14 |> let x: u32 = (
|
||||
|> ^ expected u32, found ()
|
||||
note: expected type `u32`
|
||||
note: found type `()`
|
||||
|
|
||||
14 | let x: u32 = (
|
||||
| ^ expected u32, found ()
|
||||
|
|
||||
= note: expected type `u32`
|
||||
= note: found type `()`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user