From 9adf2b2225af5f4766709b93da463bfb951ca955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 21 Jan 2018 18:18:52 -0800 Subject: [PATCH 01/10] Add `--explain` for extended error explanations --- src/librustc/session/config.rs | 2 ++ src/librustc_typeck/check/mod.rs | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index cd4e3cfed7a33..b5e19d6aad8b4 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1122,6 +1122,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "treat all errors that occur as bugs"), external_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], "show macro backtraces even for non-local macros"), + explain: bool = (false, parse_bool, [TRACKED], + "show extended diagnostic help"), continue_parse_after_error: bool = (false, parse_bool, [TRACKED], "attempt to recover from parse errors (experimental)"), incremental: Option = (None, parse_opt_string, [UNTRACKED], diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index cb8ce8d5ac331..67b1a04d54f8b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2591,9 +2591,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // arguments which we skipped above. if variadic { fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) { - type_error_struct!(s, span, t, E0617, - "can't pass `{}` to variadic function, cast to `{}`", - t, cast_ty).emit(); + let mut err = type_error_struct!( + s, span, t, E0617, "can't pass `{}` to variadic function", t); + if s.opts.debugging_opts.explain { + err.note(&format!("certain types, like `{}`, must be cast before passing them \ + to a variadic function, because of arcane ABI rules \ + dictated by the C standard", + t)); + } + if let Ok(snippet) = s.codemap().span_to_snippet(span) { + err.span_suggestion(span, + &format!("cast the value to `{}`", cast_ty), + format!("{} as {}", snippet, cast_ty)); + } else { + err.help(&format!("cast the value to `{}`", cast_ty)); + } + err.emit(); } for arg in args.iter().skip(expected_arg_count) { From 4e68e2a41ad20a76d48e1491706a5ec26de4bf66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 21 Jan 2018 18:19:53 -0800 Subject: [PATCH 02/10] Mark `--explain` as unstable --- src/librustc/session/config.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index b5e19d6aad8b4..e30a43d3d1a6e 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1622,8 +1622,11 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) let mut debugging_opts = build_debugging_options(matches, error_format); if !debugging_opts.unstable_options && error_format == ErrorOutputType::Json(true) { - early_error(ErrorOutputType::Json(false), - "--error-format=pretty-json is unstable"); + early_error(ErrorOutputType::Json(false), "--error-format=pretty-json is unstable"); + } + + if !debugging_opts.unstable_options && debugging_opts.explain { + early_error(ErrorOutputType::Json(false), "-Z --explain is unstable"); } let mut output_types = BTreeMap::new(); From fdfb9a2963952f19d9f66a8e50981478fe2bca48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 21 Jan 2018 18:20:18 -0800 Subject: [PATCH 03/10] Add explanation for E0607 --- src/librustc_typeck/check/cast.rs | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index d2f759f5d99a4..d31a99dd17110 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -281,10 +281,31 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { .emit(); } CastError::SizedUnsizedCast => { - type_error_struct!(fcx.tcx.sess, self.span, self.expr_ty, E0607, - "cannot cast thin pointer `{}` to fat pointer `{}`", - self.expr_ty, - fcx.ty_to_string(self.cast_ty)).emit(); + let mut err = type_error_struct!( + fcx.tcx.sess, + self.span, + self.expr_ty, + E0607, + "cannot cast thin pointer `{}` to fat pointer `{}`", + self.expr_ty, + fcx.ty_to_string(self.cast_ty) + ); + if fcx.tcx.sess.opts.debugging_opts.explain { + err.note( + "Thin pointers are \"simple\" pointers: they are purely a reference to a \ + memory address.\n\n\ + Fat pointers are pointers referencing \"Dynamically Sized Types\" (also \ + called DST). DST don't have a statically known size, therefore they can \ + only exist behind some kind of pointers that contain additional \ + information. Slices and trait objects are DSTs. In the case of slices, \ + the additional information the fat pointer holds is their size."); + err.note("to fix this error, don't try to cast directly between thin and fat \ + pointers"); + err.help("for more information about casts, take a look at [The Book]\ + (https://doc.rust-lang.org/book/first-edition/\ + casting-between-types.html)"); + } + err.emit(); } CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => { From e76d3f62cc34a77496305639f22a7da168ebc301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 21 Jan 2018 19:19:07 -0800 Subject: [PATCH 04/10] Fix test for variadic error change --- src/librustc/session/config.rs | 2 +- src/test/ui/variadic-ffi-3.stderr | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index e30a43d3d1a6e..4d54f2a3677f7 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1626,7 +1626,7 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) } if !debugging_opts.unstable_options && debugging_opts.explain { - early_error(ErrorOutputType::Json(false), "-Z --explain is unstable"); + early_error(error_format, "-Z --explain is unstable"); } let mut output_types = BTreeMap::new(); diff --git a/src/test/ui/variadic-ffi-3.stderr b/src/test/ui/variadic-ffi-3.stderr index be158c1e39896..1bf3798b95a79 100644 --- a/src/test/ui/variadic-ffi-3.stderr +++ b/src/test/ui/variadic-ffi-3.stderr @@ -34,41 +34,41 @@ error[E0308]: mismatched types = note: expected type `extern "C" fn(isize, u8, ...)` found type `extern "C" fn(isize, u8) {bar}` -error[E0617]: can't pass `f32` to variadic function, cast to `c_double` +error[E0617]: can't pass `f32` to variadic function --> $DIR/variadic-ffi-3.rs:34:19 | 34 | foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function, cast to `c_double` - | ^^^^ + | ^^^^ help: cast the value to `c_double`: `3f32 as c_double` -error[E0617]: can't pass `bool` to variadic function, cast to `c_int` +error[E0617]: can't pass `bool` to variadic function --> $DIR/variadic-ffi-3.rs:35:19 | 35 | foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function, cast to `c_int` - | ^^^^ + | ^^^^ help: cast the value to `c_int`: `true as c_int` -error[E0617]: can't pass `i8` to variadic function, cast to `c_int` +error[E0617]: can't pass `i8` to variadic function --> $DIR/variadic-ffi-3.rs:36:19 | 36 | foo(1, 2, 1i8); //~ ERROR can't pass `i8` to variadic function, cast to `c_int` - | ^^^ + | ^^^ help: cast the value to `c_int`: `1i8 as c_int` -error[E0617]: can't pass `u8` to variadic function, cast to `c_uint` +error[E0617]: can't pass `u8` to variadic function --> $DIR/variadic-ffi-3.rs:37:19 | 37 | foo(1, 2, 1u8); //~ ERROR can't pass `u8` to variadic function, cast to `c_uint` - | ^^^ + | ^^^ help: cast the value to `c_uint`: `1u8 as c_uint` -error[E0617]: can't pass `i16` to variadic function, cast to `c_int` +error[E0617]: can't pass `i16` to variadic function --> $DIR/variadic-ffi-3.rs:38:19 | 38 | foo(1, 2, 1i16); //~ ERROR can't pass `i16` to variadic function, cast to `c_int` - | ^^^^ + | ^^^^ help: cast the value to `c_int`: `1i16 as c_int` -error[E0617]: can't pass `u16` to variadic function, cast to `c_uint` +error[E0617]: can't pass `u16` to variadic function --> $DIR/variadic-ffi-3.rs:39:19 | 39 | foo(1, 2, 1u16); //~ ERROR can't pass `u16` to variadic function, cast to `c_uint` - | ^^^^ + | ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint` error: aborting due to 10 previous errors From 864f6d180baf76365619a7fe76b6c8b87e5ead8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 21 Jan 2018 21:19:37 -0800 Subject: [PATCH 05/10] Only emit expanded diagnostic information once --- src/librustc_errors/diagnostic.rs | 6 +++++- src/librustc_errors/lib.rs | 10 ++++++++++ src/librustc_typeck/check/cast.rs | 22 +++++++++++++--------- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 8da4321fa5b71..2e654fe9929a6 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -27,7 +27,7 @@ pub struct Diagnostic { pub suggestions: Vec, } -#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum DiagnosticId { Error(String), Lint(String), @@ -281,6 +281,10 @@ impl Diagnostic { self } + pub fn get_code(&self) -> Option { + self.code.clone() + } + pub fn message(&self) -> String { self.message.iter().map(|i| i.0.to_owned()).collect::() } diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 1fb673815eea3..cabafa052a32c 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -244,6 +244,7 @@ pub struct Handler { continue_after_error: Cell, delayed_span_bug: RefCell>, tracked_diagnostics: RefCell>>, + tracked_diagnostic_codes: RefCell>, // This set contains a hash of every diagnostic that has been emitted by // this handler. These hashes is used to avoid emitting the same error @@ -303,6 +304,7 @@ impl Handler { continue_after_error: Cell::new(true), delayed_span_bug: RefCell::new(None), tracked_diagnostics: RefCell::new(None), + tracked_diagnostic_codes: RefCell::new(FxHashSet()), emitted_diagnostics: RefCell::new(FxHashSet()), } } @@ -575,6 +577,10 @@ impl Handler { (ret, diagnostics) } + pub fn code_emitted(&self, code: &DiagnosticId) -> bool { + self.tracked_diagnostic_codes.borrow().contains(code) + } + fn emit_db(&self, db: &DiagnosticBuilder) { let diagnostic = &**db; @@ -582,6 +588,10 @@ impl Handler { list.push(diagnostic.clone()); } + if let Some(ref code) = diagnostic.code { + self.tracked_diagnostic_codes.borrow_mut().insert(code.clone()); + } + let diagnostic_hash = { use std::hash::Hash; let mut hasher = StableHasher::new(); diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index d31a99dd17110..3cd93a1c84514 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -290,18 +290,22 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { self.expr_ty, fcx.ty_to_string(self.cast_ty) ); - if fcx.tcx.sess.opts.debugging_opts.explain { + if fcx.tcx.sess.opts.debugging_opts.explain + && !fcx.tcx.sess.parse_sess.span_diagnostic + .code_emitted(&err.get_code().unwrap()) { err.note( - "Thin pointers are \"simple\" pointers: they are purely a reference to a \ - memory address.\n\n\ - Fat pointers are pointers referencing \"Dynamically Sized Types\" (also \ - called DST). DST don't have a statically known size, therefore they can \ - only exist behind some kind of pointers that contain additional \ - information. Slices and trait objects are DSTs. In the case of slices, \ - the additional information the fat pointer holds is their size."); + "Thin pointers are \"simple\" pointers: they are purely a reference to a +memory address. + +Fat pointers are pointers referencing \"Dynamically Sized Types\" (also +called DST). DST don't have a statically known size, therefore they can +only exist behind some kind of pointers that contain additional +information. Slices and trait objects are DSTs. In the case of slices, +the additional information the fat pointer holds is their size."); err.note("to fix this error, don't try to cast directly between thin and fat \ pointers"); - err.help("for more information about casts, take a look at [The Book]\ + err.help("for more information about casts, take a look at + [The Book]\ (https://doc.rust-lang.org/book/first-edition/\ casting-between-types.html)"); } From ffb827aff3340f8e6e012fd4a302616ddcf855fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 22 Jan 2018 08:35:21 -0800 Subject: [PATCH 06/10] Fix test redux --- src/librustc_typeck/check/cast.rs | 16 ++++++++-------- src/test/compile-fail/E0617.rs | 18 ++++++++++++------ src/test/compile-fail/issue-32201.rs | 3 ++- src/test/ui/variadic-ffi-3.rs | 12 ++++++------ src/test/ui/variadic-ffi-3.stderr | 12 ++++++------ 5 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 3cd93a1c84514..334e1b06e37d4 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -293,7 +293,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { if fcx.tcx.sess.opts.debugging_opts.explain && !fcx.tcx.sess.parse_sess.span_diagnostic .code_emitted(&err.get_code().unwrap()) { - err.note( + err.help( "Thin pointers are \"simple\" pointers: they are purely a reference to a memory address. @@ -301,13 +301,13 @@ Fat pointers are pointers referencing \"Dynamically Sized Types\" (also called DST). DST don't have a statically known size, therefore they can only exist behind some kind of pointers that contain additional information. Slices and trait objects are DSTs. In the case of slices, -the additional information the fat pointer holds is their size."); - err.note("to fix this error, don't try to cast directly between thin and fat \ - pointers"); - err.help("for more information about casts, take a look at - [The Book]\ - (https://doc.rust-lang.org/book/first-edition/\ - casting-between-types.html)"); +the additional information the fat pointer holds is their size. + +To fix this error, don't try to cast directly between thin and fat +pointers. + +For more information about casts, take a look at The Book: +https://doc.rust-lang.org/book/first-edition/casting-between-types.html"); } err.emit(); } diff --git a/src/test/compile-fail/E0617.rs b/src/test/compile-fail/E0617.rs index 7b769ff4ae2e0..9375fd9cade35 100644 --- a/src/test/compile-fail/E0617.rs +++ b/src/test/compile-fail/E0617.rs @@ -17,16 +17,22 @@ extern { fn main() { unsafe { printf(::std::ptr::null(), 0f32); - //~^ ERROR can't pass `f32` to variadic function, cast to `c_double` [E0617] + //~^ ERROR can't pass `f32` to variadic function + //~| HELP cast the value to `c_double` printf(::std::ptr::null(), 0i8); - //~^ ERROR can't pass `i8` to variadic function, cast to `c_int` [E0617] + //~^ ERROR can't pass `i8` to variadic function + //~| HELP cast the value to `c_int` printf(::std::ptr::null(), 0i16); - //~^ ERROR can't pass `i16` to variadic function, cast to `c_int` [E0617] + //~^ ERROR can't pass `i16` to variadic function + //~| HELP cast the value to `c_int` printf(::std::ptr::null(), 0u8); - //~^ ERROR can't pass `u8` to variadic function, cast to `c_uint` [E0617] + //~^ ERROR can't pass `u8` to variadic function + //~| HELP cast the value to `c_uint` printf(::std::ptr::null(), 0u16); - //~^ ERROR can't pass `u16` to variadic function, cast to `c_uint` [E0617] + //~^ ERROR can't pass `u16` to variadic function + //~| HELP cast the value to `c_uint` printf(::std::ptr::null(), printf); - //~^ ERROR can't pass `unsafe extern "C" fn(*const i8, ...) {printf}` to variadic function, cast to `unsafe extern "C" fn(*const i8, ...)` [E0617] + //~^ ERROR can't pass `unsafe extern "C" fn(*const i8, ...) {printf}` to variadic function + //~| HELP cast the value to `unsafe extern "C" fn(*const i8, ...)` } } diff --git a/src/test/compile-fail/issue-32201.rs b/src/test/compile-fail/issue-32201.rs index bcc53df68a323..bf9f8ecbc8097 100644 --- a/src/test/compile-fail/issue-32201.rs +++ b/src/test/compile-fail/issue-32201.rs @@ -17,6 +17,7 @@ fn bar(_: *const u8) {} fn main() { unsafe { foo(0, bar); - //~^ ERROR can't pass `fn(*const u8) {bar}` to variadic function, cast to `fn(*const u8)` + //~^ ERROR can't pass `fn(*const u8) {bar}` to variadic function + //~| HELP cast the value to `fn(*const u8)` } } diff --git a/src/test/ui/variadic-ffi-3.rs b/src/test/ui/variadic-ffi-3.rs index 12beebc181baf..9807952c636e1 100644 --- a/src/test/ui/variadic-ffi-3.rs +++ b/src/test/ui/variadic-ffi-3.rs @@ -31,11 +31,11 @@ fn main() { //~| expected type `extern "C" fn(isize, u8, ...)` //~| found type `extern "C" fn(isize, u8) {bar}` - foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function, cast to `c_double` - foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function, cast to `c_int` - foo(1, 2, 1i8); //~ ERROR can't pass `i8` to variadic function, cast to `c_int` - foo(1, 2, 1u8); //~ ERROR can't pass `u8` to variadic function, cast to `c_uint` - foo(1, 2, 1i16); //~ ERROR can't pass `i16` to variadic function, cast to `c_int` - foo(1, 2, 1u16); //~ ERROR can't pass `u16` to variadic function, cast to `c_uint` + foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function + foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function + foo(1, 2, 1i8); //~ ERROR can't pass `i8` to variadic function + foo(1, 2, 1u8); //~ ERROR can't pass `u8` to variadic function + foo(1, 2, 1i16); //~ ERROR can't pass `i16` to variadic function + foo(1, 2, 1u16); //~ ERROR can't pass `u16` to variadic function } } diff --git a/src/test/ui/variadic-ffi-3.stderr b/src/test/ui/variadic-ffi-3.stderr index 1bf3798b95a79..54275fbc4f29f 100644 --- a/src/test/ui/variadic-ffi-3.stderr +++ b/src/test/ui/variadic-ffi-3.stderr @@ -37,37 +37,37 @@ error[E0308]: mismatched types error[E0617]: can't pass `f32` to variadic function --> $DIR/variadic-ffi-3.rs:34:19 | -34 | foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function, cast to `c_double` +34 | foo(1, 2, 3f32); //~ ERROR can't pass `f32` to variadic function | ^^^^ help: cast the value to `c_double`: `3f32 as c_double` error[E0617]: can't pass `bool` to variadic function --> $DIR/variadic-ffi-3.rs:35:19 | -35 | foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function, cast to `c_int` +35 | foo(1, 2, true); //~ ERROR can't pass `bool` to variadic function | ^^^^ help: cast the value to `c_int`: `true as c_int` error[E0617]: can't pass `i8` to variadic function --> $DIR/variadic-ffi-3.rs:36:19 | -36 | foo(1, 2, 1i8); //~ ERROR can't pass `i8` to variadic function, cast to `c_int` +36 | foo(1, 2, 1i8); //~ ERROR can't pass `i8` to variadic function | ^^^ help: cast the value to `c_int`: `1i8 as c_int` error[E0617]: can't pass `u8` to variadic function --> $DIR/variadic-ffi-3.rs:37:19 | -37 | foo(1, 2, 1u8); //~ ERROR can't pass `u8` to variadic function, cast to `c_uint` +37 | foo(1, 2, 1u8); //~ ERROR can't pass `u8` to variadic function | ^^^ help: cast the value to `c_uint`: `1u8 as c_uint` error[E0617]: can't pass `i16` to variadic function --> $DIR/variadic-ffi-3.rs:38:19 | -38 | foo(1, 2, 1i16); //~ ERROR can't pass `i16` to variadic function, cast to `c_int` +38 | foo(1, 2, 1i16); //~ ERROR can't pass `i16` to variadic function | ^^^^ help: cast the value to `c_int`: `1i16 as c_int` error[E0617]: can't pass `u16` to variadic function --> $DIR/variadic-ffi-3.rs:39:19 | -39 | foo(1, 2, 1u16); //~ ERROR can't pass `u16` to variadic function, cast to `c_uint` +39 | foo(1, 2, 1u16); //~ ERROR can't pass `u16` to variadic function | ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint` error: aborting due to 10 previous errors From 7d41cbad6a34976e3d6d0321ba1d4c433e24e21c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 22 Jan 2018 18:05:49 -0800 Subject: [PATCH 07/10] Don't make it necessary to enable `unstable-options` --- src/librustc/session/config.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 4d54f2a3677f7..759e32abd60c4 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1625,10 +1625,6 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) early_error(ErrorOutputType::Json(false), "--error-format=pretty-json is unstable"); } - if !debugging_opts.unstable_options && debugging_opts.explain { - early_error(error_format, "-Z --explain is unstable"); - } - let mut output_types = BTreeMap::new(); if !debugging_opts.parse_only { for list in matches.opt_strs("emit") { From 3dac0f5a9ce8864a06916d3e8017a9b2c5262ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 22 Jan 2018 18:07:35 -0800 Subject: [PATCH 08/10] Create `StructuredDiagnostic` Create the concept of an `StructuredDiagnostic` that is self-contained with enough knowledge of all variables to create a `DiagnosticBuilder`, including different possible versions (one line output and expanded explanations). --- src/librustc/session/mod.rs | 4 + src/librustc_typeck/check/cast.rs | 35 +----- src/librustc_typeck/check/mod.rs | 19 +-- src/librustc_typeck/lib.rs | 7 +- src/librustc_typeck/structured_errors.rs | 150 +++++++++++++++++++++++ 5 files changed, 167 insertions(+), 48 deletions(-) create mode 100644 src/librustc_typeck/structured_errors.rs diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 94fcfb7e2aa57..995aef51cade7 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -831,6 +831,10 @@ impl Session { _ => true, } } + + pub fn explain(&self, code: &DiagnosticId) -> bool { + self.opts.debugging_opts.explain && !self.parse_sess.span_diagnostic.code_emitted(code) + } } pub fn build_session(sopts: config::Options, diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 334e1b06e37d4..b8be0d6c18279 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -281,35 +281,12 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { .emit(); } CastError::SizedUnsizedCast => { - let mut err = type_error_struct!( - fcx.tcx.sess, - self.span, - self.expr_ty, - E0607, - "cannot cast thin pointer `{}` to fat pointer `{}`", - self.expr_ty, - fcx.ty_to_string(self.cast_ty) - ); - if fcx.tcx.sess.opts.debugging_opts.explain - && !fcx.tcx.sess.parse_sess.span_diagnostic - .code_emitted(&err.get_code().unwrap()) { - err.help( - "Thin pointers are \"simple\" pointers: they are purely a reference to a -memory address. - -Fat pointers are pointers referencing \"Dynamically Sized Types\" (also -called DST). DST don't have a statically known size, therefore they can -only exist behind some kind of pointers that contain additional -information. Slices and trait objects are DSTs. In the case of slices, -the additional information the fat pointer holds is their size. - -To fix this error, don't try to cast directly between thin and fat -pointers. - -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/casting-between-types.html"); - } - err.emit(); + use structured_errors::{SizedUnsizedCastError, StructuredDiagnostic}; + SizedUnsizedCastError::new(&fcx.tcx.sess, + self.span, + self.expr_ty, + fcx.ty_to_string(self.cast_ty)) + .diagnostic().emit(); } CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 67b1a04d54f8b..3cd327adf222d 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -103,6 +103,7 @@ use rustc::ty::maps::Providers; use rustc::ty::util::{Representability, IntTypeExt}; use rustc::ty::layout::LayoutOf; use errors::{DiagnosticBuilder, DiagnosticId}; + use require_c_abi_if_variadic; use session::{CompileIncomplete, config, Session}; use TypeAndSubsts; @@ -2591,22 +2592,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // arguments which we skipped above. if variadic { fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) { - let mut err = type_error_struct!( - s, span, t, E0617, "can't pass `{}` to variadic function", t); - if s.opts.debugging_opts.explain { - err.note(&format!("certain types, like `{}`, must be cast before passing them \ - to a variadic function, because of arcane ABI rules \ - dictated by the C standard", - t)); - } - if let Ok(snippet) = s.codemap().span_to_snippet(span) { - err.span_suggestion(span, - &format!("cast the value to `{}`", cast_ty), - format!("{} as {}", snippet, cast_ty)); - } else { - err.help(&format!("cast the value to `{}`", cast_ty)); - } - err.emit(); + use structured_errors::{VariadicError, StructuredDiagnostic}; + VariadicError::new(s, span, t, cast_ty).diagnostic().emit(); } for arg in args.iter().skip(expected_arg_count) { diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 786a678344064..8d5d7dc04ec22 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -122,16 +122,17 @@ use std::iter; // registered before they are used. mod diagnostics; +mod astconv; mod check; mod check_unused; -mod astconv; +mod coherence; mod collect; mod constrained_type_params; +mod structured_errors; mod impl_wf_check; -mod coherence; +mod namespace; mod outlives; mod variance; -mod namespace; pub struct TypeAndSubsts<'tcx> { substs: &'tcx Substs<'tcx>, diff --git a/src/librustc_typeck/structured_errors.rs b/src/librustc_typeck/structured_errors.rs new file mode 100644 index 0000000000000..e9b96ed2d6439 --- /dev/null +++ b/src/librustc_typeck/structured_errors.rs @@ -0,0 +1,150 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::session::Session; +use syntax_pos::Span; +use errors::{DiagnosticId, DiagnosticBuilder}; +use rustc::ty::{Ty, TypeFoldable}; + +pub trait StructuredDiagnostic<'tcx> { + fn session(&self) -> &Session; + + fn code(&self) -> DiagnosticId; + + fn common(&self) -> DiagnosticBuilder<'tcx>; + + fn diagnostic(&self) -> DiagnosticBuilder<'tcx> { + let err = self.common(); + if self.session().explain(&self.code()) { + self.extended(err) + } else { + self.regular(err) + } + } + + fn regular(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { + err + } + + fn extended(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { + err + } +} + +pub struct VariadicError<'tcx> { + sess: &'tcx Session, + span: Span, + t: Ty<'tcx>, + cast_ty: &'tcx str, +} + +impl<'tcx> VariadicError<'tcx> { + pub fn new(sess: &'tcx Session, + span: Span, + t: Ty<'tcx>, + cast_ty: &'tcx str) -> VariadicError<'tcx> { + VariadicError { sess, span, t, cast_ty } + } +} + +impl<'tcx> StructuredDiagnostic<'tcx> for VariadicError<'tcx> { + fn session(&self) -> &Session { self.sess } + + fn code(&self) -> DiagnosticId { + __diagnostic_used!(E0617); + DiagnosticId::Error("E0617".to_owned()) + } + + fn common(&self) -> DiagnosticBuilder<'tcx> { + let mut err = if self.t.references_error() { + self.sess.diagnostic().struct_dummy() + } else { + self.sess.struct_span_fatal_with_code( + self.span, + &format!("can't pass `{}` to variadic function", self.t), + self.code(), + ) + }; + if let Ok(snippet) = self.sess.codemap().span_to_snippet(self.span) { + err.span_suggestion(self.span, + &format!("cast the value to `{}`", self.cast_ty), + format!("{} as {}", snippet, self.cast_ty)); + } else { + err.help(&format!("cast the value to `{}`", self.cast_ty)); + } + err + } + + fn extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { + err.note(&format!("certain types, like `{}`, must be cast before passing them to a \ + variadic function, because of arcane ABI rules dictated by the C \ + standard", + self.t)); + err + } +} + +pub struct SizedUnsizedCastError<'tcx> { + sess: &'tcx Session, + span: Span, + expr_ty: Ty<'tcx>, + cast_ty: String, +} + +impl<'tcx> SizedUnsizedCastError<'tcx> { + pub fn new(sess: &'tcx Session, + span: Span, + expr_ty: Ty<'tcx>, + cast_ty: String) -> SizedUnsizedCastError<'tcx> { + SizedUnsizedCastError { sess, span, expr_ty, cast_ty } + } +} + +impl<'tcx> StructuredDiagnostic<'tcx> for SizedUnsizedCastError<'tcx> { + fn session(&self) -> &Session { self.sess } + + fn code(&self) -> DiagnosticId { + __diagnostic_used!(E0607); + DiagnosticId::Error("E0607".to_owned()) + } + + fn common(&self) -> DiagnosticBuilder<'tcx> { + if self.expr_ty.references_error() { + self.sess.diagnostic().struct_dummy() + } else { + self.sess.struct_span_fatal_with_code( + self.span, + &format!("cannot cast thin pointer `{}` to fat pointer `{}`", + self.expr_ty, + self.cast_ty), + self.code(), + ) + } + } + + fn extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { + err.help( + "Thin pointers are \"simple\" pointers: they are purely a reference to a +memory address. + +Fat pointers are pointers referencing \"Dynamically Sized Types\" (also +called DST). DST don't have a statically known size, therefore they can +only exist behind some kind of pointers that contain additional +information. Slices and trait objects are DSTs. In the case of slices, +the additional information the fat pointer holds is their size. + +To fix this error, don't try to cast directly between thin and fat +pointers. + +For more information about casts, take a look at The Book: +https://doc.rust-lang.org/book/first-edition/casting-between-types.html"); + err + } +} From 482f7f19785455242dc3dfe36858847dd6f280cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 23 Jan 2018 11:34:57 -0800 Subject: [PATCH 09/10] Rename `-Z explain` to `-Z teach` --- src/librustc/session/config.rs | 2 +- src/librustc/session/mod.rs | 4 ++-- src/librustc_typeck/structured_errors.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 759e32abd60c4..ac9c6ee91906c 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1122,7 +1122,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "treat all errors that occur as bugs"), external_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], "show macro backtraces even for non-local macros"), - explain: bool = (false, parse_bool, [TRACKED], + teach: bool = (false, parse_bool, [TRACKED], "show extended diagnostic help"), continue_parse_after_error: bool = (false, parse_bool, [TRACKED], "attempt to recover from parse errors (experimental)"), diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 995aef51cade7..4eff090b83ea6 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -832,8 +832,8 @@ impl Session { } } - pub fn explain(&self, code: &DiagnosticId) -> bool { - self.opts.debugging_opts.explain && !self.parse_sess.span_diagnostic.code_emitted(code) + pub fn teach(&self, code: &DiagnosticId) -> bool { + self.opts.debugging_opts.teach && !self.parse_sess.span_diagnostic.code_emitted(code) } } diff --git a/src/librustc_typeck/structured_errors.rs b/src/librustc_typeck/structured_errors.rs index e9b96ed2d6439..afcdc7575a3cb 100644 --- a/src/librustc_typeck/structured_errors.rs +++ b/src/librustc_typeck/structured_errors.rs @@ -22,7 +22,7 @@ pub trait StructuredDiagnostic<'tcx> { fn diagnostic(&self) -> DiagnosticBuilder<'tcx> { let err = self.common(); - if self.session().explain(&self.code()) { + if self.session().teach(&self.code()) { self.extended(err) } else { self.regular(err) From 2b737334961916daee73ea018eea877f389ad0dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 25 Jan 2018 11:40:33 -0800 Subject: [PATCH 10/10] Add description to field and method --- src/librustc_errors/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index cabafa052a32c..84ac2c0225350 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -244,6 +244,10 @@ pub struct Handler { continue_after_error: Cell, delayed_span_bug: RefCell>, tracked_diagnostics: RefCell>>, + + // This set contains the `DiagnosticId` of all emitted diagnostics to avoid + // emitting the same diagnostic with extended help (`--teach`) twice, which + // would be uneccessary repetition. tracked_diagnostic_codes: RefCell>, // This set contains a hash of every diagnostic that has been emitted by @@ -577,6 +581,10 @@ impl Handler { (ret, diagnostics) } + /// `true` if a diagnostic with this code has already been emitted in this handler. + /// + /// Used to suppress emitting the same error multiple times with extended explanation when + /// calling `-Zteach`. pub fn code_emitted(&self, code: &DiagnosticId) -> bool { self.tracked_diagnostic_codes.borrow().contains(code) }