Skip to content

Commit

Permalink
use structured errors
Browse files Browse the repository at this point in the history
  • Loading branch information
nrc committed Dec 30, 2015
1 parent 253a1ce commit 95dc7ef
Show file tree
Hide file tree
Showing 70 changed files with 2,032 additions and 1,547 deletions.
91 changes: 71 additions & 20 deletions src/librustc/lint/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ use std::mem;
use syntax::ast_util::{self, IdVisitingOperation};
use syntax::attr::{self, AttrMetaMethods};
use syntax::codemap::Span;
use syntax::errors::{self, DiagnosticBuilder};
use syntax::parse::token::InternedString;
use syntax::ast;
use syntax::attr::ThinAttributesExt;
use rustc_front::hir;
use rustc_front::util;
use rustc_front::intravisit as hir_visit;
use syntax::visit as ast_visit;
use syntax::errors;

/// Information about the registered lints.
///
Expand Down Expand Up @@ -363,10 +363,24 @@ pub fn gather_attrs(attrs: &[ast::Attribute])
/// in trans that run after the main lint pass is finished. Most
/// lints elsewhere in the compiler should call
/// `Session::add_lint()` instead.
pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
lvlsrc: LevelSource, span: Option<Span>, msg: &str) {
pub fn raw_emit_lint(sess: &Session,
lint: &'static Lint,
lvlsrc: LevelSource,
span: Option<Span>,
msg: &str) {
raw_struct_lint(sess, lint, lvlsrc, span, msg).map(|mut e| e.emit());
}

pub fn raw_struct_lint<'a>(sess: &'a Session,
lint: &'static Lint,
lvlsrc: LevelSource,
span: Option<Span>,
msg: &str)
-> Option<DiagnosticBuilder<'a>> {
let (mut level, source) = lvlsrc;
if level == Allow { return }
if level == Allow {
return None;
}

let name = lint.name_lower();
let mut def = None;
Expand All @@ -391,17 +405,18 @@ pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
// For purposes of printing, we can treat forbid as deny.
if level == Forbid { level = Deny; }

match (level, span) {
(Warn, Some(sp)) => sess.span_warn(sp, &msg[..]),
(Warn, None) => sess.warn(&msg[..]),
(Deny, Some(sp)) => sess.span_err(sp, &msg[..]),
(Deny, None) => sess.err(&msg[..]),
let mut err = match (level, span) {
(Warn, Some(sp)) => sess.struct_span_warn(sp, &msg[..]),
(Warn, None) => sess.struct_warn(&msg[..]),
(Deny, Some(sp)) => sess.struct_span_err(sp, &msg[..]),
(Deny, None) => sess.struct_err(&msg[..]),
_ => sess.bug("impossible level in raw_emit_lint"),
}
};

if let Some(span) = def {
sess.span_note(span, "lint level defined here");
err.span_note(span, "lint level defined here");
}
Some(err)
}

pub trait LintContext: Sized {
Expand All @@ -418,44 +433,80 @@ pub trait LintContext: Sized {
self.lints().levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl)
}

fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
let (level, src) = match self.lints().levels.get(&LintId::of(lint)) {
None => return,
Some(&(Warn, src)) => {
fn level_src(&self, lint: &'static Lint) -> Option<LevelSource> {
self.lints().levels.get(&LintId::of(lint)).map(|ls| match ls {
&(Warn, src) => {
let lint_id = LintId::of(builtin::WARNINGS);
(self.lints().get_level_source(lint_id).0, src)
}
Some(&pair) => pair,
_ => *ls
})
}

fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
let (level, src) = match self.level_src(lint) {
None => return,
Some(pair) => pair,
};

raw_emit_lint(&self.sess(), lint, (level, src), span, msg);
}

fn lookup(&self,
lint: &'static Lint,
span: Option<Span>,
msg: &str)
-> Option<DiagnosticBuilder> {
let (level, src) = match self.level_src(lint) {
None => return None,
Some(pair) => pair,
};

raw_struct_lint(&self.sess(), lint, (level, src), span, msg)
}

/// Emit a lint at the appropriate level, for a particular span.
fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
self.lookup_and_emit(lint, Some(span), msg);
}

fn struct_span_lint(&self,
lint: &'static Lint,
span: Span,
msg: &str)
-> Option<DiagnosticBuilder> {
self.lookup(lint, Some(span), msg)
}

/// Emit a lint and note at the appropriate level, for a particular span.
fn span_lint_note(&self, lint: &'static Lint, span: Span, msg: &str,
note_span: Span, note: &str) {
self.span_lint(lint, span, msg);
let mut err = match self.lookup(lint, Some(span), msg) {
Some(e) => e,
None => return
};
if self.current_level(lint) != Level::Allow {
if note_span == span {
self.sess().fileline_note(note_span, note)
err.fileline_note(note_span, note);
} else {
self.sess().span_note(note_span, note)
err.span_note(note_span, note);
}
}
err.emit();
}

/// Emit a lint and help at the appropriate level, for a particular span.
fn span_lint_help(&self, lint: &'static Lint, span: Span,
msg: &str, help: &str) {
let mut err = match self.lookup(lint, Some(span), msg) {
Some(e) => e,
None => return
};
self.span_lint(lint, span, msg);
if self.current_level(lint) != Level::Allow {
self.sess().span_help(span, help)
err.span_help(span, help);
}
err.emit();
}

/// Emit a lint at the appropriate level, with no associated span.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/lint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use rustc_front::hir;

pub use lint::context::{LateContext, EarlyContext, LintContext, LintStore,
raw_emit_lint, check_crate, check_ast_crate, gather_attrs,
GatherNodeLevels};
raw_struct_lint, GatherNodeLevels};

/// Specification of a single lint.
#[derive(Copy, Clone, Debug)]
Expand Down
41 changes: 21 additions & 20 deletions src/librustc/middle/check_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,15 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
// this doesn't come from a macro that has #[allow_internal_unstable]
!self.tcx.sess.codemap().span_allows_unstable(expr.span)
{
self.tcx.sess.span_err(
let mut err = self.tcx.sess.struct_span_err(
expr.span,
"const fns are an unstable feature");
fileline_help!(
self.tcx.sess,
&mut err,
expr.span,
"in Nightly builds, add `#![feature(const_fn)]` to the crate \
attributes to enable");
err.emit();
}

let qualif = self.fn_like(fn_like.kind(),
Expand Down Expand Up @@ -714,27 +715,27 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
if !is_const {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
fn span_limited_call_error(tcx: &ty::ctxt, span: Span, s: &str) {
span_err!(tcx.sess, span, E0015, "{}", s);
}

// FIXME(#24111) Remove this check when const fn stabilizes
if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
span_limited_call_error(&v.tcx, e.span,
&format!("function calls in {}s are limited to \
struct and enum constructors",
v.msg()));
v.tcx.sess.span_note(e.span,
"a limited form of compile-time function \
evaluation is available on a nightly \
compiler via `const fn`");
let (msg, note) =
if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
(format!("function calls in {}s are limited to \
struct and enum constructors",
v.msg()),
Some("a limited form of compile-time function \
evaluation is available on a nightly \
compiler via `const fn`"))
} else {
span_limited_call_error(&v.tcx, e.span,
&format!("function calls in {}s are limited \
to constant functions, \
struct and enum constructors",
v.msg()));
(format!("function calls in {}s are limited \
to constant functions, \
struct and enum constructors",
v.msg()),
None)
};
let mut err = struct_span_err!(v.tcx.sess, e.span, E0015, "{}", msg);
if let Some(note) = note {
err.span_note(e.span, note);
}
err.emit();
}
}
}
Expand Down
31 changes: 17 additions & 14 deletions src/librustc/middle/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,13 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) {
if inlined_arms.is_empty() {
if !pat_ty.is_empty(cx.tcx) {
// We know the type is inhabited, so this must be wrong
span_err!(cx.tcx.sess, ex.span, E0002,
"non-exhaustive patterns: type {} is non-empty",
pat_ty);
span_help!(cx.tcx.sess, ex.span,
let mut err = struct_span_err!(cx.tcx.sess, ex.span, E0002,
"non-exhaustive patterns: type {} is non-empty",
pat_ty);
span_help!(&mut err, ex.span,
"Please ensure that all possible cases are being handled; \
possibly adding wildcards or more match arms.");
err.emit();
}
// If the type *is* empty, it's vacuously exhaustive
return;
Expand Down Expand Up @@ -251,14 +252,15 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat)
&& variant.kind() == VariantKind::Unit
) {
let ty_path = cx.tcx.item_path_str(edef.did);
span_warn!(cx.tcx.sess, p.span, E0170,
let mut err = struct_span_warn!(cx.tcx.sess, p.span, E0170,
"pattern binding `{}` is named the same as one \
of the variants of the type `{}`",
ident.node, ty_path);
fileline_help!(cx.tcx.sess, p.span,
fileline_help!(err, p.span,
"if you meant to match on a variant, \
consider making the path in the pattern qualified: `{}::{}`",
ty_path, ident.node);
err.emit();
}
}
}
Expand All @@ -282,13 +284,13 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) {
Ok(_) => {}

Err(err) => {
span_err!(cx.tcx.sess, err.span, E0471,
"constant evaluation error: {}",
err.description());
let mut diag = struct_span_err!(cx.tcx.sess, err.span, E0471,
"constant evaluation error: {}",
err.description());
if !p.span.contains(err.span) {
cx.tcx.sess.span_note(p.span,
"in pattern here")
diag.span_note(p.span, "in pattern here");
}
diag.emit();
}
}
}
Expand Down Expand Up @@ -1076,9 +1078,10 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
} else if has_guard {
span_err!(cx.tcx.sess, p.span, E0008, "cannot bind by-move into a pattern guard");
} else if by_ref_span.is_some() {
span_err!(cx.tcx.sess, p.span, E0009,
"cannot bind by-move and by-ref in the same pattern");
span_note!(cx.tcx.sess, by_ref_span.unwrap(), "by-ref binding occurs here");
let mut err = struct_span_err!(cx.tcx.sess, p.span, E0009,
"cannot bind by-move and by-ref in the same pattern");
span_note!(&mut err, by_ref_span.unwrap(), "by-ref binding occurs here");
err.emit();
}
};

Expand Down
9 changes: 5 additions & 4 deletions src/librustc/middle/dependency_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,11 @@ fn add_library(sess: &session::Session,
// This error is probably a little obscure, but I imagine that it
// can be refined over time.
if link2 != link || link == RequireStatic {
sess.err(&format!("cannot satisfy dependencies so `{}` only \
shows up once", sess.cstore.crate_name(cnum)));
sess.help("having upstream crates all available in one format \
will likely make this go away");
sess.struct_err(&format!("cannot satisfy dependencies so `{}` only \
shows up once", sess.cstore.crate_name(cnum)))
.help("having upstream crates all available in one format \
will likely make this go away")
.emit();
}
}
None => { m.insert(cnum, link); }
Expand Down
15 changes: 9 additions & 6 deletions src/librustc/middle/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,20 @@ fn configure_main(this: &mut EntryContext) {
this.session.entry_type.set(Some(config::EntryMain));
} else {
// No main function
this.session.err("main function not found");
let mut err = this.session.struct_err("main function not found");
if !this.non_main_fns.is_empty() {
// There were some functions named 'main' though. Try to give the user a hint.
this.session.note("the main function must be defined at the crate level \
but you have one or more functions named 'main' that are not \
defined at the crate level. Either move the definition or \
attach the `#[main]` attribute to override this behavior.");
err.note("the main function must be defined at the crate level \
but you have one or more functions named 'main' that are not \
defined at the crate level. Either move the definition or \
attach the `#[main]` attribute to override this behavior.");
for &(_, span) in &this.non_main_fns {
this.session.span_note(span, "here is a function named 'main'");
err.span_note(span, "here is a function named 'main'");
}
err.emit();
this.session.abort_if_errors();
} else {
err.emit();
}
}
}
Loading

1 comment on commit 95dc7ef

@BurntSushi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nrc This commit has numerous breaking changes to public APIs used by compiler plugins. Could we please be better about tagging such things with [breaking-change] and include a description of how to fix things?

Please sign in to comment.