diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index cd4e3cfed7a33..ac9c6ee91906c 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"), + 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)"), incremental: Option = (None, parse_opt_string, [UNTRACKED], @@ -1620,8 +1622,7 @@ 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"); } let mut output_types = BTreeMap::new(); diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 94fcfb7e2aa57..4eff090b83ea6 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -831,6 +831,10 @@ impl Session { _ => true, } } + + pub fn teach(&self, code: &DiagnosticId) -> bool { + self.opts.debugging_opts.teach && !self.parse_sess.span_diagnostic.code_emitted(code) + } } pub fn build_session(sopts: config::Options, 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..84ac2c0225350 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -245,6 +245,11 @@ pub struct Handler { 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 // this handler. These hashes is used to avoid emitting the same error // twice. @@ -303,6 +308,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 +581,14 @@ 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) + } + fn emit_db(&self, db: &DiagnosticBuilder) { let diagnostic = &**db; @@ -582,6 +596,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 d2f759f5d99a4..b8be0d6c18279 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -281,10 +281,12 @@ 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(); + 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 cb8ce8d5ac331..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,9 +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) { - type_error_struct!(s, span, t, E0617, - "can't pass `{}` to variadic function, cast to `{}`", - t, cast_ty).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..afcdc7575a3cb --- /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().teach(&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 + } +} 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 be158c1e39896..54275fbc4f29f 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` - | ^^^^ +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, 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` - | ^^^^ +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, 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` - | ^^^ +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, 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` - | ^^^ +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, 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` - | ^^^^ +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, 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` - | ^^^^ +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