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] 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 + } +}