refactor the Emitter trait

There is now a CoreEmitter that everything desugars to, but without
losing any information. Also remove RenderSpan::FileLine. This lets the
rustc_driver tests build.
This commit is contained in:
Niko Matsakis 2016-04-20 20:00:25 -04:00
parent 9d022f2993
commit 1067850e6a
5 changed files with 92 additions and 113 deletions

View file

@ -34,9 +34,9 @@ use std::cell::RefCell;
use std::rc::Rc;
use syntax::ast;
use syntax::abi::Abi;
use syntax::codemap::{MultiSpan, CodeMap, DUMMY_SP};
use syntax::codemap::{CodeMap, DUMMY_SP};
use syntax::errors;
use syntax::errors::emitter::Emitter;
use syntax::errors::emitter::{CoreEmitter, Emitter};
use syntax::errors::{Level, RenderSpan};
use syntax::parse::token;
use syntax::feature_gate::UnstableFeatures;
@ -78,12 +78,13 @@ fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
}
}
impl Emitter for ExpectErrorEmitter {
fn emit(&mut self,
_sp: Option<&MultiSpan>,
msg: &str,
_: Option<&str>,
lvl: Level) {
impl CoreEmitter for ExpectErrorEmitter {
fn emit_message(&mut self,
_sp: &RenderSpan,
msg: &str,
_: Option<&str>,
lvl: Level,
_is_header: bool) {
remove_message(self, msg, lvl);
}
}

View file

@ -20,8 +20,8 @@ use {CrateTranslation, ModuleTranslation};
use util::common::time;
use util::common::path2cstr;
use syntax::codemap::MultiSpan;
use syntax::errors::{self, Handler, Level};
use syntax::errors::emitter::RudimentaryEmitter;
use syntax::errors::{self, Handler, Level, RenderSpan};
use syntax::errors::emitter::CoreEmitter;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
@ -100,11 +100,13 @@ impl SharedEmitter {
}
}
impl RudimentaryEmitter for SharedEmitter {
fn emit_rudimentary(&mut self,
msg: &str,
code: Option<&str>,
lvl: Level) {
impl CoreEmitter for SharedEmitter {
fn emit_message(&mut self,
_rsp: &RenderSpan,
msg: &str,
code: Option<&str>,
lvl: Level,
_is_header: bool) {
self.buffer.lock().unwrap().push(Diagnostic {
msg: msg.to_string(),
code: code.map(|s| s.to_string()),

View file

@ -24,6 +24,8 @@ use std::io;
use std::rc::Rc;
use term;
/// Emitter trait for emitting errors. Do not implement this directly:
/// implement `CoreEmitter` instead.
pub trait Emitter {
/// Emit a standalone diagnostic message.
fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, lvl: Level);
@ -32,27 +34,44 @@ pub trait Emitter {
fn emit_struct(&mut self, db: &DiagnosticBuilder);
}
/// A core trait that can only handle very simple messages: those
/// without spans or any real structure. Used only in specific contexts.
pub trait RudimentaryEmitter {
fn emit_rudimentary(&mut self, msg: &str, code: Option<&str>, lvl: Level);
pub trait CoreEmitter {
fn emit_message(&mut self,
rsp: &RenderSpan,
msg: &str,
code: Option<&str>,
lvl: Level,
is_header: bool);
}
impl<T: RudimentaryEmitter> Emitter for T {
impl<T: CoreEmitter> Emitter for T {
fn emit(&mut self,
msp: &MultiSpan,
msg: &str,
code: Option<&str>,
lvl: Level) {
assert!(msp.primary_span().is_none(), "Rudimenatry emitters can't handle spans");
self.emit_rudimentary(msg, code, lvl);
self.emit_message(&FullSpan(msp.clone()),
msg,
code,
lvl,
true);
}
fn emit_struct(&mut self, db: &DiagnosticBuilder) {
self.emit(&db.span, &db.message, db.code.as_ref().map(|s| &**s), db.level);
self.emit_message(&FullSpan(db.span.clone()),
&db.message,
db.code.as_ref().map(|s| &**s),
db.level,
true);
for child in &db.children {
assert!(child.render_span.is_none(), "Rudimentary emitters can't handle render spans");
self.emit(&child.span, &child.message, None, child.level);
let render_span = child.render_span
.clone()
.unwrap_or_else(
|| FullSpan(child.span.clone()));
self.emit_message(&render_span,
&child.message,
None,
child.level,
false);
}
}
}
@ -83,11 +102,14 @@ pub struct BasicEmitter {
dst: Destination,
}
impl RudimentaryEmitter for BasicEmitter {
fn emit_rudimentary(&mut self,
msg: &str,
code: Option<&str>,
lvl: Level) {
impl CoreEmitter for BasicEmitter {
fn emit_message(&mut self,
_rsp: &RenderSpan,
msg: &str,
code: Option<&str>,
lvl: Level,
_is_header: bool) {
// we ignore the span as we have no access to a codemap at this point
if let Err(e) = print_diagnostic(&mut self.dst, "", lvl, msg, code) {
panic!("failed to print diagnostics: {:?}", e);
}
@ -112,28 +134,16 @@ pub struct EmitterWriter {
first: bool,
}
impl Emitter for EmitterWriter {
fn emit(&mut self,
msp: &MultiSpan,
msg: &str,
code: Option<&str>,
lvl: Level) {
self.emit_multispan(msp, msg, code, lvl, true);
}
fn emit_struct(&mut self, db: &DiagnosticBuilder) {
self.emit_multispan(&db.span, &db.message,
db.code.as_ref().map(|s| &**s), db.level, true);
for child in &db.children {
match child.render_span {
Some(ref sp) =>
self.emit_renderspan(sp, &child.message,
child.level),
None =>
self.emit_multispan(&child.span,
&child.message, None, child.level, false),
}
impl CoreEmitter for EmitterWriter {
fn emit_message(&mut self,
rsp: &RenderSpan,
msg: &str,
code: Option<&str>,
lvl: Level,
is_header: bool) {
match self.emit_message_(rsp, msg, code, lvl, is_header) {
Ok(()) => { }
Err(e) => panic!("failed to emit error: {}", e)
}
}
}
@ -173,83 +183,56 @@ impl EmitterWriter {
EmitterWriter { dst: Raw(dst), registry: registry, cm: code_map, first: true }
}
fn emit_multispan(&mut self,
span: &MultiSpan,
msg: &str,
code: Option<&str>,
lvl: Level,
is_header: bool) {
fn emit_message_(&mut self,
rsp: &RenderSpan,
msg: &str,
code: Option<&str>,
lvl: Level,
is_header: bool)
-> io::Result<()> {
if is_header {
if self.first {
self.first = false;
} else {
match write!(self.dst, "\n") {
Ok(_) => { }
Err(e) => {
panic!("failed to print diagnostics: {:?}", e)
}
}
write!(self.dst, "\n")?;
}
}
let error = match span.primary_span() {
Some(COMMAND_LINE_SP) => {
self.emit_(&FileLine(span.clone()), msg, code, lvl)
}
Some(DUMMY_SP) | None => {
print_diagnostic(&mut self.dst, "", lvl, msg, code)
}
Some(_) => {
self.emit_(&FullSpan(span.clone()), msg, code, lvl)
}
};
if let Err(e) = error {
panic!("failed to print diagnostics: {:?}", e);
}
}
fn emit_renderspan(&mut self, sp: &RenderSpan, msg: &str, lvl: Level) {
if let Err(e) = self.emit_(sp, msg, None, lvl) {
panic!("failed to print diagnostics: {:?}", e);
}
}
fn emit_(&mut self,
rsp: &RenderSpan,
msg: &str,
code: Option<&str>,
lvl: Level)
-> io::Result<()> {
let msp = rsp.span();
let primary_span = msp.primary_span();
match code {
Some(code) if self.registry.as_ref()
.and_then(|registry| registry.find_description(code)).is_some() =>
{
.and_then(|registry| registry.find_description(code))
.is_some() => {
let code_with_explain = String::from("--explain ") + code;
print_diagnostic(&mut self.dst, "", lvl, msg, Some(&code_with_explain))?
}
_ => print_diagnostic(&mut self.dst, "", lvl, msg, code)?
_ => {
print_diagnostic(&mut self.dst, "", lvl, msg, code)?
}
}
// Watch out for various nasty special spans; don't try to
// print any filename or anything for those.
match rsp.span().primary_span() {
Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => {
return Ok(());
}
_ => { }
}
// Otherwise, print out the snippet etc as needed.
match *rsp {
FullSpan(_) => {
FullSpan(ref msp) => {
self.highlight_lines(msp, lvl)?;
if let Some(primary_span) = primary_span {
if let Some(primary_span) = msp.primary_span() {
self.print_macro_backtrace(primary_span)?;
}
}
Suggestion(ref suggestion) => {
self.highlight_suggestion(suggestion)?;
if let Some(primary_span) = primary_span {
if let Some(primary_span) = rsp.span().primary_span() {
self.print_macro_backtrace(primary_span)?;
}
}
FileLine(..) => {
// no source text in this case!
}
}
Ok(())

View file

@ -294,7 +294,6 @@ impl DiagnosticSpan {
fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
match *rsp {
RenderSpan::FileLine(ref msp) |
RenderSpan::FullSpan(ref msp) =>
DiagnosticSpan::from_multispan(msp, je),
RenderSpan::Suggestion(ref suggestion) =>
@ -356,7 +355,6 @@ impl DiagnosticCode {
impl JsonEmitter {
fn render(&self, render_span: &RenderSpan) -> Option<String> {
match *render_span {
RenderSpan::FileLine(_) |
RenderSpan::FullSpan(_) => {
None
}

View file

@ -38,10 +38,6 @@ pub enum RenderSpan {
/// of hypothetical source code, where each `String` is spliced
/// into the lines in place of the code covered by each span.
Suggestion(CodeSuggestion),
/// A FileLine renders with just a line for the message prefixed
/// by file:linenum.
FileLine(MultiSpan),
}
#[derive(Clone)]
@ -54,8 +50,7 @@ impl RenderSpan {
fn span(&self) -> &MultiSpan {
match *self {
FullSpan(ref msp) |
Suggestion(CodeSuggestion { ref msp, .. }) |
FileLine(ref msp) =>
Suggestion(CodeSuggestion { ref msp, .. }) =>
msp
}
}