about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/session/config.rs5
-rw-r--r--src/librustc/session/mod.rs4
-rw-r--r--src/librustc_errors/diagnostic.rs6
-rw-r--r--src/librustc_errors/lib.rs18
-rw-r--r--src/librustc_typeck/check/cast.rs10
-rw-r--r--src/librustc_typeck/check/mod.rs6
-rw-r--r--src/librustc_typeck/lib.rs7
-rw-r--r--src/librustc_typeck/structured_errors.rs150
-rw-r--r--src/test/compile-fail/E0617.rs18
-rw-r--r--src/test/compile-fail/issue-32201.rs3
-rw-r--r--src/test/ui/variadic-ffi-3.rs12
-rw-r--r--src/test/ui/variadic-ffi-3.stderr36
12 files changed, 231 insertions, 44 deletions
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index a4f347d4687..b9546143a05 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1167,6 +1167,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<String> = (None, parse_opt_string, [UNTRACKED],
@@ -1664,8 +1666,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 568ae77c964..2765239d5e6 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -860,6 +860,10 @@ impl Session {
         // scientific.
         16
     }
+
+    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 8da4321fa5b..2e654fe9929 100644
--- a/src/librustc_errors/diagnostic.rs
+++ b/src/librustc_errors/diagnostic.rs
@@ -27,7 +27,7 @@ pub struct Diagnostic {
     pub suggestions: Vec<CodeSuggestion>,
 }
 
-#[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<DiagnosticId> {
+        self.code.clone()
+    }
+
     pub fn message(&self) -> String {
         self.message.iter().map(|i| i.0.to_owned()).collect::<String>()
     }
diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs
index 33948ea92b9..3d50c95d3f4 100644
--- a/src/librustc_errors/lib.rs
+++ b/src/librustc_errors/lib.rs
@@ -259,6 +259,11 @@ pub struct Handler {
     delayed_span_bug: RefCell<Option<Diagnostic>>,
     tracked_diagnostics: RefCell<Option<Vec<Diagnostic>>>,
 
+    // 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<FxHashSet<DiagnosticId>>,
+
     // 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.
@@ -317,6 +322,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()),
         }
     }
@@ -589,6 +595,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;
 
@@ -596,6 +610,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 992a510f713..48bd7b14fc9 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 b7021f9dfd8..6147743437b 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;
@@ -2599,9 +2600,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 75a74e1069c..bd7e200d620 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -123,16 +123,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 00000000000..afcdc7575a3
--- /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 <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.
+
+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 7b769ff4ae2..9375fd9cade 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 bcc53df68a3..bf9f8ecbc80 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 12beebc181b..9807952c636 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 be158c1e398..54275fbc4f2 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