Skip to content

Commit 888a92c

Browse files
committed
separate Diagnostic from DiagnosticBuilder
1 parent 75bc8bf commit 888a92c

File tree

3 files changed

+358
-215
lines changed

3 files changed

+358
-215
lines changed

src/librustc_errors/diagnostic.rs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
use CodeSuggestion;
2+
use Level;
3+
use RenderSpan;
4+
use RenderSpan::Suggestion;
5+
use std::fmt;
6+
use syntax_pos::{MultiSpan, Span};
7+
8+
#[must_use]
9+
#[derive(Clone)]
10+
pub struct Diagnostic {
11+
pub level: Level,
12+
pub message: String,
13+
pub code: Option<String>,
14+
pub span: MultiSpan,
15+
pub children: Vec<SubDiagnostic>,
16+
}
17+
18+
/// For example a note attached to an error.
19+
#[derive(Clone)]
20+
pub struct SubDiagnostic {
21+
pub level: Level,
22+
pub message: String,
23+
pub span: MultiSpan,
24+
pub render_span: Option<RenderSpan>,
25+
}
26+
27+
impl Diagnostic {
28+
pub fn new(level: Level, message: &str) -> Self {
29+
Diagnostic::new_with_code(level, None, message)
30+
}
31+
32+
pub fn new_with_code(level: Level, code: Option<String>, message: &str) -> Self {
33+
Diagnostic {
34+
level: level,
35+
message: message.to_owned(),
36+
code: code,
37+
span: MultiSpan::new(),
38+
children: vec![],
39+
}
40+
}
41+
42+
/// Cancel the diagnostic (a structured diagnostic must either be emitted or
43+
/// cancelled or it will panic when dropped).
44+
/// BEWARE: if this DiagnosticBuilder is an error, then creating it will
45+
/// bump the error count on the Handler and cancelling it won't undo that.
46+
/// If you want to decrement the error count you should use `Handler::cancel`.
47+
pub fn cancel(&mut self) {
48+
self.level = Level::Cancelled;
49+
}
50+
51+
pub fn cancelled(&self) -> bool {
52+
self.level == Level::Cancelled
53+
}
54+
55+
pub fn is_fatal(&self) -> bool {
56+
self.level == Level::Fatal
57+
}
58+
59+
/// Add a span/label to be included in the resulting snippet.
60+
/// This is pushed onto the `MultiSpan` that was created when the
61+
/// diagnostic was first built. If you don't call this function at
62+
/// all, and you just supplied a `Span` to create the diagnostic,
63+
/// then the snippet will just include that `Span`, which is
64+
/// called the primary span.
65+
pub fn span_label(&mut self, span: Span, label: &fmt::Display)
66+
-> &mut Self {
67+
self.span.push_span_label(span, format!("{}", label));
68+
self
69+
}
70+
71+
pub fn note_expected_found(&mut self,
72+
label: &fmt::Display,
73+
expected: &fmt::Display,
74+
found: &fmt::Display)
75+
-> &mut Self
76+
{
77+
self.note_expected_found_extra(label, expected, found, &"", &"")
78+
}
79+
80+
pub fn note_expected_found_extra(&mut self,
81+
label: &fmt::Display,
82+
expected: &fmt::Display,
83+
found: &fmt::Display,
84+
expected_extra: &fmt::Display,
85+
found_extra: &fmt::Display)
86+
-> &mut Self
87+
{
88+
// For now, just attach these as notes
89+
self.note(&format!("expected {} `{}`{}", label, expected, expected_extra));
90+
self.note(&format!(" found {} `{}`{}", label, found, found_extra));
91+
self
92+
}
93+
94+
pub fn note(&mut self, msg: &str) -> &mut Self {
95+
self.sub(Level::Note, msg, MultiSpan::new(), None);
96+
self
97+
}
98+
99+
pub fn span_note<S: Into<MultiSpan>>(&mut self,
100+
sp: S,
101+
msg: &str)
102+
-> &mut Self {
103+
self.sub(Level::Note, msg, sp.into(), None);
104+
self
105+
}
106+
107+
pub fn warn(&mut self, msg: &str) -> &mut Self {
108+
self.sub(Level::Warning, msg, MultiSpan::new(), None);
109+
self
110+
}
111+
112+
pub fn span_warn<S: Into<MultiSpan>>(&mut self,
113+
sp: S,
114+
msg: &str)
115+
-> &mut Self {
116+
self.sub(Level::Warning, msg, sp.into(), None);
117+
self
118+
}
119+
120+
pub fn help(&mut self , msg: &str) -> &mut Self {
121+
self.sub(Level::Help, msg, MultiSpan::new(), None);
122+
self
123+
}
124+
125+
pub fn span_help<S: Into<MultiSpan>>(&mut self,
126+
sp: S,
127+
msg: &str)
128+
-> &mut Self {
129+
self.sub(Level::Help, msg, sp.into(), None);
130+
self
131+
}
132+
133+
/// Prints out a message with a suggested edit of the code.
134+
///
135+
/// See `diagnostic::RenderSpan::Suggestion` for more information.
136+
pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
137+
sp: S,
138+
msg: &str,
139+
suggestion: String)
140+
-> &mut Self {
141+
self.sub(Level::Help,
142+
msg,
143+
MultiSpan::new(),
144+
Some(Suggestion(CodeSuggestion {
145+
msp: sp.into(),
146+
substitutes: vec![suggestion],
147+
})));
148+
self
149+
}
150+
151+
pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
152+
self.span = sp.into();
153+
self
154+
}
155+
156+
pub fn code(&mut self, s: String) -> &mut Self {
157+
self.code = Some(s);
158+
self
159+
}
160+
161+
pub fn message(&self) -> &str {
162+
&self.message
163+
}
164+
165+
pub fn level(&self) -> Level {
166+
self.level
167+
}
168+
169+
/// Convenience function for internal use, clients should use one of the
170+
/// public methods above.
171+
fn sub(&mut self,
172+
level: Level,
173+
message: &str,
174+
span: MultiSpan,
175+
render_span: Option<RenderSpan>) {
176+
let sub = SubDiagnostic {
177+
level: level,
178+
message: message.to_owned(),
179+
span: span,
180+
render_span: render_span,
181+
};
182+
self.children.push(sub);
183+
}
184+
}
185+
186+
impl fmt::Debug for Diagnostic {
187+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188+
self.message.fmt(f)
189+
}
190+
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
use Diagnostic;
2+
use Level;
3+
use Handler;
4+
use std::fmt::{self, Debug};
5+
use std::ops::{Deref, DerefMut};
6+
use std::thread::panicking;
7+
use syntax_pos::{MultiSpan, Span};
8+
9+
/// Used for emitting structured error messages and other diagnostic information.
10+
#[must_use]
11+
#[derive(Clone)]
12+
pub struct DiagnosticBuilder<'a> {
13+
handler: &'a Handler,
14+
diagnostic: Diagnostic,
15+
}
16+
17+
/// In general, the `DiagnosticBuilder` uses deref to allow access to
18+
/// the fields and methods of the embedded `diagnostic` in a
19+
/// transparent way. *However,* many of the methods are intended to
20+
/// be used in a chained way, and hence ought to return `self`. In
21+
/// that case, we can't just naively forward to the method on the
22+
/// `diagnostic`, because the return type would be a `&Diagnostic`
23+
/// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes
24+
/// it easy to declare such methods on the builder.
25+
macro_rules! forward {
26+
// Forward pattern for &self -> &Self
27+
(pub fn $n:ident(&self, $($name:ident: $ty:ty),*) -> &Self) => {
28+
pub fn $n(&self, $($name: $ty),*) -> &Self {
29+
self.diagnostic.$n($($name),*);
30+
self
31+
}
32+
};
33+
34+
// Forward pattern for &mut self -> &mut Self
35+
(pub fn $n:ident(&mut self, $($name:ident: $ty:ty),*) -> &mut Self) => {
36+
pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
37+
self.diagnostic.$n($($name),*);
38+
self
39+
}
40+
};
41+
42+
// Forward pattern for &mut self -> &mut Self, with S: Into<MultiSpan>
43+
// type parameter. No obvious way to make this more generic.
44+
(pub fn $n:ident<S: Into<MultiSpan>>(&mut self, $($name:ident: $ty:ty),*) -> &mut Self) => {
45+
pub fn $n<S: Into<MultiSpan>>(&mut self, $($name: $ty),*) -> &mut Self {
46+
self.diagnostic.$n($($name),*);
47+
self
48+
}
49+
};
50+
}
51+
52+
impl<'a> Deref for DiagnosticBuilder<'a> {
53+
type Target = Diagnostic;
54+
55+
fn deref(&self) -> &Diagnostic {
56+
&self.diagnostic
57+
}
58+
}
59+
60+
impl<'a> DerefMut for DiagnosticBuilder<'a> {
61+
fn deref_mut(&mut self) -> &mut Diagnostic {
62+
&mut self.diagnostic
63+
}
64+
}
65+
66+
impl<'a> DiagnosticBuilder<'a> {
67+
/// Emit the diagnostic.
68+
pub fn emit(&mut self) {
69+
if self.cancelled() {
70+
return;
71+
}
72+
73+
self.handler.emitter.borrow_mut().emit(&self);
74+
self.cancel();
75+
self.handler.panic_if_treat_err_as_bug();
76+
77+
// if self.is_fatal() {
78+
// panic!(FatalError);
79+
// }
80+
}
81+
82+
/// Add a span/label to be included in the resulting snippet.
83+
/// This is pushed onto the `MultiSpan` that was created when the
84+
/// diagnostic was first built. If you don't call this function at
85+
/// all, and you just supplied a `Span` to create the diagnostic,
86+
/// then the snippet will just include that `Span`, which is
87+
/// called the primary span.
88+
forward!(pub fn span_label(&mut self, span: Span, label: &fmt::Display)
89+
-> &mut Self);
90+
91+
forward!(pub fn note_expected_found(&mut self,
92+
label: &fmt::Display,
93+
expected: &fmt::Display,
94+
found: &fmt::Display)
95+
-> &mut Self);
96+
97+
forward!(pub fn note_expected_found_extra(&mut self,
98+
label: &fmt::Display,
99+
expected: &fmt::Display,
100+
found: &fmt::Display,
101+
expected_extra: &fmt::Display,
102+
found_extra: &fmt::Display)
103+
-> &mut Self);
104+
105+
forward!(pub fn note(&mut self, msg: &str) -> &mut Self);
106+
forward!(pub fn span_note<S: Into<MultiSpan>>(&mut self,
107+
sp: S,
108+
msg: &str)
109+
-> &mut Self);
110+
forward!(pub fn warn(&mut self, msg: &str) -> &mut Self);
111+
forward!(pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self);
112+
forward!(pub fn help(&mut self , msg: &str) -> &mut Self);
113+
forward!(pub fn span_help<S: Into<MultiSpan>>(&mut self,
114+
sp: S,
115+
msg: &str)
116+
-> &mut Self);
117+
forward!(pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
118+
sp: S,
119+
msg: &str,
120+
suggestion: String)
121+
-> &mut Self);
122+
forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
123+
forward!(pub fn code(&mut self, s: String) -> &mut Self);
124+
125+
/// Convenience function for internal use, clients should use one of the
126+
/// struct_* methods on Handler.
127+
pub fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> {
128+
DiagnosticBuilder::new_with_code(handler, level, None, message)
129+
}
130+
131+
/// Convenience function for internal use, clients should use one of the
132+
/// struct_* methods on Handler.
133+
pub fn new_with_code(handler: &'a Handler,
134+
level: Level,
135+
code: Option<String>,
136+
message: &str)
137+
-> DiagnosticBuilder<'a> {
138+
DiagnosticBuilder {
139+
handler: handler,
140+
diagnostic: Diagnostic::new_with_code(level, code, message)
141+
}
142+
}
143+
}
144+
145+
impl<'a> Debug for DiagnosticBuilder<'a> {
146+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147+
self.diagnostic.fmt(f)
148+
}
149+
}
150+
151+
/// Destructor bomb - a DiagnosticBuilder must be either emitted or cancelled or
152+
/// we emit a bug.
153+
impl<'a> Drop for DiagnosticBuilder<'a> {
154+
fn drop(&mut self) {
155+
if !panicking() && !self.cancelled() {
156+
let mut db = DiagnosticBuilder::new(self.handler,
157+
Level::Bug,
158+
"Error constructed but not emitted");
159+
db.emit();
160+
panic!();
161+
}
162+
}
163+
}
164+

0 commit comments

Comments
 (0)