Skip to content

Commit 3dac0f5

Browse files
committed
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).
1 parent 7d41cba commit 3dac0f5

File tree

5 files changed

+167
-48
lines changed

5 files changed

+167
-48
lines changed

Diff for: src/librustc/session/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,10 @@ impl Session {
831831
_ => true,
832832
}
833833
}
834+
835+
pub fn explain(&self, code: &DiagnosticId) -> bool {
836+
self.opts.debugging_opts.explain && !self.parse_sess.span_diagnostic.code_emitted(code)
837+
}
834838
}
835839

836840
pub fn build_session(sopts: config::Options,

Diff for: src/librustc_typeck/check/cast.rs

+6-29
Original file line numberDiff line numberDiff line change
@@ -281,35 +281,12 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
281281
.emit();
282282
}
283283
CastError::SizedUnsizedCast => {
284-
let mut err = type_error_struct!(
285-
fcx.tcx.sess,
286-
self.span,
287-
self.expr_ty,
288-
E0607,
289-
"cannot cast thin pointer `{}` to fat pointer `{}`",
290-
self.expr_ty,
291-
fcx.ty_to_string(self.cast_ty)
292-
);
293-
if fcx.tcx.sess.opts.debugging_opts.explain
294-
&& !fcx.tcx.sess.parse_sess.span_diagnostic
295-
.code_emitted(&err.get_code().unwrap()) {
296-
err.help(
297-
"Thin pointers are \"simple\" pointers: they are purely a reference to a
298-
memory address.
299-
300-
Fat pointers are pointers referencing \"Dynamically Sized Types\" (also
301-
called DST). DST don't have a statically known size, therefore they can
302-
only exist behind some kind of pointers that contain additional
303-
information. Slices and trait objects are DSTs. In the case of slices,
304-
the additional information the fat pointer holds is their size.
305-
306-
To fix this error, don't try to cast directly between thin and fat
307-
pointers.
308-
309-
For more information about casts, take a look at The Book:
310-
https://doc.rust-lang.org/book/first-edition/casting-between-types.html");
311-
}
312-
err.emit();
284+
use structured_errors::{SizedUnsizedCastError, StructuredDiagnostic};
285+
SizedUnsizedCastError::new(&fcx.tcx.sess,
286+
self.span,
287+
self.expr_ty,
288+
fcx.ty_to_string(self.cast_ty))
289+
.diagnostic().emit();
313290
}
314291
CastError::UnknownCastPtrKind |
315292
CastError::UnknownExprPtrKind => {

Diff for: src/librustc_typeck/check/mod.rs

+3-16
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ use rustc::ty::maps::Providers;
103103
use rustc::ty::util::{Representability, IntTypeExt};
104104
use rustc::ty::layout::LayoutOf;
105105
use errors::{DiagnosticBuilder, DiagnosticId};
106+
106107
use require_c_abi_if_variadic;
107108
use session::{CompileIncomplete, config, Session};
108109
use TypeAndSubsts;
@@ -2591,22 +2592,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
25912592
// arguments which we skipped above.
25922593
if variadic {
25932594
fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) {
2594-
let mut err = type_error_struct!(
2595-
s, span, t, E0617, "can't pass `{}` to variadic function", t);
2596-
if s.opts.debugging_opts.explain {
2597-
err.note(&format!("certain types, like `{}`, must be cast before passing them \
2598-
to a variadic function, because of arcane ABI rules \
2599-
dictated by the C standard",
2600-
t));
2601-
}
2602-
if let Ok(snippet) = s.codemap().span_to_snippet(span) {
2603-
err.span_suggestion(span,
2604-
&format!("cast the value to `{}`", cast_ty),
2605-
format!("{} as {}", snippet, cast_ty));
2606-
} else {
2607-
err.help(&format!("cast the value to `{}`", cast_ty));
2608-
}
2609-
err.emit();
2595+
use structured_errors::{VariadicError, StructuredDiagnostic};
2596+
VariadicError::new(s, span, t, cast_ty).diagnostic().emit();
26102597
}
26112598

26122599
for arg in args.iter().skip(expected_arg_count) {

Diff for: src/librustc_typeck/lib.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,17 @@ use std::iter;
122122
// registered before they are used.
123123
mod diagnostics;
124124

125+
mod astconv;
125126
mod check;
126127
mod check_unused;
127-
mod astconv;
128+
mod coherence;
128129
mod collect;
129130
mod constrained_type_params;
131+
mod structured_errors;
130132
mod impl_wf_check;
131-
mod coherence;
133+
mod namespace;
132134
mod outlives;
133135
mod variance;
134-
mod namespace;
135136

136137
pub struct TypeAndSubsts<'tcx> {
137138
substs: &'tcx Substs<'tcx>,

Diff for: src/librustc_typeck/structured_errors.rs

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use rustc::session::Session;
12+
use syntax_pos::Span;
13+
use errors::{DiagnosticId, DiagnosticBuilder};
14+
use rustc::ty::{Ty, TypeFoldable};
15+
16+
pub trait StructuredDiagnostic<'tcx> {
17+
fn session(&self) -> &Session;
18+
19+
fn code(&self) -> DiagnosticId;
20+
21+
fn common(&self) -> DiagnosticBuilder<'tcx>;
22+
23+
fn diagnostic(&self) -> DiagnosticBuilder<'tcx> {
24+
let err = self.common();
25+
if self.session().explain(&self.code()) {
26+
self.extended(err)
27+
} else {
28+
self.regular(err)
29+
}
30+
}
31+
32+
fn regular(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
33+
err
34+
}
35+
36+
fn extended(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
37+
err
38+
}
39+
}
40+
41+
pub struct VariadicError<'tcx> {
42+
sess: &'tcx Session,
43+
span: Span,
44+
t: Ty<'tcx>,
45+
cast_ty: &'tcx str,
46+
}
47+
48+
impl<'tcx> VariadicError<'tcx> {
49+
pub fn new(sess: &'tcx Session,
50+
span: Span,
51+
t: Ty<'tcx>,
52+
cast_ty: &'tcx str) -> VariadicError<'tcx> {
53+
VariadicError { sess, span, t, cast_ty }
54+
}
55+
}
56+
57+
impl<'tcx> StructuredDiagnostic<'tcx> for VariadicError<'tcx> {
58+
fn session(&self) -> &Session { self.sess }
59+
60+
fn code(&self) -> DiagnosticId {
61+
__diagnostic_used!(E0617);
62+
DiagnosticId::Error("E0617".to_owned())
63+
}
64+
65+
fn common(&self) -> DiagnosticBuilder<'tcx> {
66+
let mut err = if self.t.references_error() {
67+
self.sess.diagnostic().struct_dummy()
68+
} else {
69+
self.sess.struct_span_fatal_with_code(
70+
self.span,
71+
&format!("can't pass `{}` to variadic function", self.t),
72+
self.code(),
73+
)
74+
};
75+
if let Ok(snippet) = self.sess.codemap().span_to_snippet(self.span) {
76+
err.span_suggestion(self.span,
77+
&format!("cast the value to `{}`", self.cast_ty),
78+
format!("{} as {}", snippet, self.cast_ty));
79+
} else {
80+
err.help(&format!("cast the value to `{}`", self.cast_ty));
81+
}
82+
err
83+
}
84+
85+
fn extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
86+
err.note(&format!("certain types, like `{}`, must be cast before passing them to a \
87+
variadic function, because of arcane ABI rules dictated by the C \
88+
standard",
89+
self.t));
90+
err
91+
}
92+
}
93+
94+
pub struct SizedUnsizedCastError<'tcx> {
95+
sess: &'tcx Session,
96+
span: Span,
97+
expr_ty: Ty<'tcx>,
98+
cast_ty: String,
99+
}
100+
101+
impl<'tcx> SizedUnsizedCastError<'tcx> {
102+
pub fn new(sess: &'tcx Session,
103+
span: Span,
104+
expr_ty: Ty<'tcx>,
105+
cast_ty: String) -> SizedUnsizedCastError<'tcx> {
106+
SizedUnsizedCastError { sess, span, expr_ty, cast_ty }
107+
}
108+
}
109+
110+
impl<'tcx> StructuredDiagnostic<'tcx> for SizedUnsizedCastError<'tcx> {
111+
fn session(&self) -> &Session { self.sess }
112+
113+
fn code(&self) -> DiagnosticId {
114+
__diagnostic_used!(E0607);
115+
DiagnosticId::Error("E0607".to_owned())
116+
}
117+
118+
fn common(&self) -> DiagnosticBuilder<'tcx> {
119+
if self.expr_ty.references_error() {
120+
self.sess.diagnostic().struct_dummy()
121+
} else {
122+
self.sess.struct_span_fatal_with_code(
123+
self.span,
124+
&format!("cannot cast thin pointer `{}` to fat pointer `{}`",
125+
self.expr_ty,
126+
self.cast_ty),
127+
self.code(),
128+
)
129+
}
130+
}
131+
132+
fn extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
133+
err.help(
134+
"Thin pointers are \"simple\" pointers: they are purely a reference to a
135+
memory address.
136+
137+
Fat pointers are pointers referencing \"Dynamically Sized Types\" (also
138+
called DST). DST don't have a statically known size, therefore they can
139+
only exist behind some kind of pointers that contain additional
140+
information. Slices and trait objects are DSTs. In the case of slices,
141+
the additional information the fat pointer holds is their size.
142+
143+
To fix this error, don't try to cast directly between thin and fat
144+
pointers.
145+
146+
For more information about casts, take a look at The Book:
147+
https://doc.rust-lang.org/book/first-edition/casting-between-types.html");
148+
err
149+
}
150+
}

0 commit comments

Comments
 (0)