Skip to content

Commit 0b9d70c

Browse files
committed
rustc_errors: take self by value in DiagnosticBuilder::cancel.
1 parent 8562d6b commit 0b9d70c

File tree

31 files changed

+176
-146
lines changed

31 files changed

+176
-146
lines changed

compiler/rustc_borrowck/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ fn do_mir_borrowck<'a, 'tcx>(
379379
// Convert any reservation warnings into lints.
380380
let reservation_warnings = mem::take(&mut mbcx.reservation_warnings);
381381
for (_, (place, span, location, bk, borrow)) in reservation_warnings {
382-
let mut initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow);
382+
let initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow);
383383

384384
let scope = mbcx.body.source_info(location).scope;
385385
let lint_root = match &mbcx.body.source_scopes[scope].local_data {
@@ -2329,7 +2329,7 @@ mod error {
23292329
move_out_indices: Vec<MoveOutIndex>,
23302330
place_and_err: (PlaceRef<'tcx>, DiagnosticBuilder<'tcx>),
23312331
) -> bool {
2332-
if let Some((_, mut diag)) =
2332+
if let Some((_, diag)) =
23332333
self.errors.buffered_move_errors.insert(move_out_indices, place_and_err)
23342334
{
23352335
// Cancel the old diagnostic so we don't ICE

compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ fn annotation_type_for_level(level: Level) -> AnnotationType {
7272
Level::Warning => AnnotationType::Warning,
7373
Level::Note => AnnotationType::Note,
7474
Level::Help => AnnotationType::Help,
75-
// FIXME(#59346): Not sure how to map these two levels
76-
Level::Cancelled | Level::FailureNote => AnnotationType::Error,
75+
// FIXME(#59346): Not sure how to map this level
76+
Level::FailureNote => AnnotationType::Error,
7777
Level::Allow => panic!("Should not call with Allow"),
7878
}
7979
}

compiler/rustc_errors/src/diagnostic.rs

+7-23
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ impl Diagnostic {
133133
| Level::Error { .. }
134134
| Level::FailureNote => true,
135135

136-
Level::Warning | Level::Note | Level::Help | Level::Cancelled | Level::Allow => false,
136+
Level::Warning | Level::Note | Level::Help | Level::Allow => false,
137137
}
138138
}
139139

@@ -151,17 +151,6 @@ impl Diagnostic {
151151
}
152152
}
153153

154-
/// Cancel the diagnostic (a structured diagnostic must either be emitted or
155-
/// canceled or it will panic when dropped).
156-
pub fn cancel(&mut self) {
157-
self.level = Level::Cancelled;
158-
}
159-
160-
/// Check if this diagnostic [was cancelled][Self::cancel()].
161-
pub fn cancelled(&self) -> bool {
162-
self.level == Level::Cancelled
163-
}
164-
165154
/// Delay emission of this diagnostic as a bug.
166155
///
167156
/// This can be useful in contexts where an error indicates a bug but
@@ -174,17 +163,12 @@ impl Diagnostic {
174163
/// locally in whichever way makes the most sense.
175164
#[track_caller]
176165
pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self {
177-
// FIXME(eddyb) this check is only necessary because cancellation exists,
178-
// but hopefully that can be removed in the future, if enough callers
179-
// of `.cancel()` can take `DiagnosticBuilder`, and by-value.
180-
if !self.cancelled() {
181-
assert!(
182-
self.is_error(),
183-
"downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
184-
self.level
185-
);
186-
self.level = Level::DelayedBug;
187-
}
166+
assert!(
167+
self.is_error(),
168+
"downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
169+
self.level
170+
);
171+
self.level = Level::DelayedBug;
188172

189173
self
190174
}

compiler/rustc_errors/src/diagnostic_builder.rs

+89-23
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use tracing::debug;
1616
#[must_use]
1717
#[derive(Clone)]
1818
pub struct DiagnosticBuilder<'a> {
19-
handler: &'a Handler,
19+
state: DiagnosticBuilderState<'a>,
2020

2121
/// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a
2222
/// return value, especially within the frequently-used `PResult` type.
@@ -25,6 +25,34 @@ pub struct DiagnosticBuilder<'a> {
2525
diagnostic: Box<Diagnostic>,
2626
}
2727

28+
#[derive(Clone)]
29+
enum DiagnosticBuilderState<'a> {
30+
/// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`.
31+
///
32+
/// The `Diagnostic` will be emitted through this `Handler`.
33+
Emittable(&'a Handler),
34+
35+
/// State of a `DiagnosticBuilder`, after `.emit()` or *during* `.cancel()`.
36+
///
37+
/// The `Diagnostic` will be ignored when calling `.emit()`, and it can be
38+
/// assumed that `.emit()` was previously called, to end up in this state.
39+
///
40+
/// While this is also used by `.cancel()`, this state is only observed by
41+
/// the `Drop` `impl` of `DiagnosticBuilder`, as `.cancel()` takes `self`
42+
/// by-value specifically to prevent any attempts to `.emit()`.
43+
///
44+
// FIXME(eddyb) currently this doesn't prevent extending the `Diagnostic`,
45+
// despite that being potentially lossy, if important information is added
46+
// *after* the original `.emit()` call.
47+
AlreadyEmittedOrDuringCancellation,
48+
}
49+
50+
// `DiagnosticBuilderState` should be pointer-sized.
51+
rustc_data_structures::static_assert_size!(
52+
DiagnosticBuilderState<'_>,
53+
std::mem::size_of::<&Handler>()
54+
);
55+
2856
/// In general, the `DiagnosticBuilder` uses deref to allow access to
2957
/// the fields and methods of the embedded `diagnostic` in a
3058
/// transparent way. *However,* many of the methods are intended to
@@ -78,8 +106,18 @@ impl<'a> DerefMut for DiagnosticBuilder<'a> {
78106
impl<'a> DiagnosticBuilder<'a> {
79107
/// Emit the diagnostic.
80108
pub fn emit(&mut self) {
81-
self.handler.emit_diagnostic(&self);
82-
self.cancel();
109+
match self.state {
110+
// First `.emit()` call, the `&Handler` is still available.
111+
DiagnosticBuilderState::Emittable(handler) => {
112+
handler.emit_diagnostic(&self);
113+
self.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
114+
}
115+
// `.emit()` was previously called, disallowed from repeating it.
116+
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {
117+
// FIXME(eddyb) rely on this to return a "proof" that an error
118+
// was/will be emitted, despite doing no emission *here and now*.
119+
}
120+
}
83121
}
84122

85123
/// Emit the diagnostic unless `delay` is true,
@@ -93,6 +131,17 @@ impl<'a> DiagnosticBuilder<'a> {
93131
self.emit();
94132
}
95133

134+
/// Cancel the diagnostic (a structured diagnostic must either be emitted or
135+
/// cancelled or it will panic when dropped).
136+
///
137+
/// This method takes `self` by-value to disallow calling `.emit()` on it,
138+
/// which may be expected to *guarantee* the emission of an error, either
139+
/// at the time of the call, or through a prior `.emit()` call.
140+
pub fn cancel(mut self) {
141+
self.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
142+
drop(self);
143+
}
144+
96145
/// Stashes diagnostic for possible later improvement in a different,
97146
/// later stage of the compiler. The diagnostic can be accessed with
98147
/// the provided `span` and `key` through [`Handler::steal_diagnostic()`].
@@ -105,22 +154,29 @@ impl<'a> DiagnosticBuilder<'a> {
105154
}
106155

107156
/// Converts the builder to a `Diagnostic` for later emission,
108-
/// unless handler has disabled such buffering.
157+
/// unless handler has disabled such buffering, or `.emit()` was called.
109158
pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
110-
if self.handler.flags.dont_buffer_diagnostics
111-
|| self.handler.flags.treat_err_as_bug.is_some()
112-
{
159+
let handler = match self.state {
160+
// No `.emit()` calls, the `&Handler` is still available.
161+
DiagnosticBuilderState::Emittable(handler) => handler,
162+
// `.emit()` was previously called, nothing we can do.
163+
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {
164+
return None;
165+
}
166+
};
167+
168+
if handler.flags.dont_buffer_diagnostics || handler.flags.treat_err_as_bug.is_some() {
113169
self.emit();
114170
return None;
115171
}
116172

117-
let handler = self.handler;
118-
119-
// We must use `Level::Cancelled` for `dummy` to avoid an ICE about an
120-
// unused diagnostic.
121-
let dummy = Diagnostic::new(Level::Cancelled, "");
173+
// Take the `Diagnostic` by replacing it with a dummy.
174+
let dummy = Diagnostic::new(Level::Allow, "");
122175
let diagnostic = std::mem::replace(&mut *self.diagnostic, dummy);
123176

177+
// Disable the ICE on `Drop`.
178+
self.cancel();
179+
124180
// Logging here is useful to help track down where in logs an error was
125181
// actually emitted.
126182
debug!("buffer: diagnostic={:?}", diagnostic);
@@ -314,7 +370,10 @@ impl<'a> DiagnosticBuilder<'a> {
314370
/// diagnostic.
315371
crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> {
316372
debug!("Created new diagnostic");
317-
DiagnosticBuilder { handler, diagnostic: Box::new(diagnostic) }
373+
DiagnosticBuilder {
374+
state: DiagnosticBuilderState::Emittable(handler),
375+
diagnostic: Box::new(diagnostic),
376+
}
318377
}
319378
}
320379

@@ -324,19 +383,26 @@ impl<'a> Debug for DiagnosticBuilder<'a> {
324383
}
325384
}
326385

327-
/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or canceled
386+
/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled
328387
/// or we emit a bug.
329388
impl<'a> Drop for DiagnosticBuilder<'a> {
330389
fn drop(&mut self) {
331-
if !panicking() && !self.cancelled() {
332-
let mut db = DiagnosticBuilder::new(
333-
self.handler,
334-
Level::Bug,
335-
"the following error was constructed but not emitted",
336-
);
337-
db.emit();
338-
self.emit();
339-
panic!();
390+
match self.state {
391+
// No `.emit()` or `.cancel()` calls.
392+
DiagnosticBuilderState::Emittable(handler) => {
393+
if !panicking() {
394+
let mut db = DiagnosticBuilder::new(
395+
handler,
396+
Level::Bug,
397+
"the following error was constructed but not emitted",
398+
);
399+
db.emit();
400+
handler.emit_diagnostic(&self);
401+
panic!();
402+
}
403+
}
404+
// `.emit()` was previously called, or maybe we're during `.cancel()`.
405+
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
340406
}
341407
}
342408
}

compiler/rustc_errors/src/lib.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -911,10 +911,6 @@ impl HandlerInner {
911911

912912
// FIXME(eddyb) this should ideally take `diagnostic` by value.
913913
fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) {
914-
if diagnostic.cancelled() {
915-
return;
916-
}
917-
918914
if diagnostic.level == Level::DelayedBug {
919915
// FIXME(eddyb) this should check for `has_errors` and stop pushing
920916
// once *any* errors were emitted (and truncate `delayed_span_bugs`
@@ -1238,7 +1234,6 @@ pub enum Level {
12381234
Warning,
12391235
Note,
12401236
Help,
1241-
Cancelled,
12421237
FailureNote,
12431238
Allow,
12441239
}
@@ -1266,7 +1261,7 @@ impl Level {
12661261
spec.set_fg(Some(Color::Cyan)).set_intense(true);
12671262
}
12681263
FailureNote => {}
1269-
Allow | Cancelled => unreachable!(),
1264+
Allow => unreachable!(),
12701265
}
12711266
spec
12721267
}
@@ -1279,7 +1274,6 @@ impl Level {
12791274
Note => "note",
12801275
Help => "help",
12811276
FailureNote => "failure-note",
1282-
Cancelled => panic!("Shouldn't call on cancelled error"),
12831277
Allow => panic!("Shouldn't call on allowed error"),
12841278
}
12851279
}

compiler/rustc_expand/src/mbe/macro_rules.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ fn emit_frag_parse_err(
9595
match kind {
9696
// Try a statement if an expression is wanted but failed and suggest adding `;` to call.
9797
AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) {
98-
Err(mut err) => err.cancel(),
98+
Err(err) => err.cancel(),
9999
Ok(_) => {
100100
e.note(
101101
"the macro call doesn't expand to an expression, but it can expand to a statement",

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1598,7 +1598,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
15981598
Some((expected, found)) => Some((expected, found)),
15991599
None => {
16001600
// Derived error. Cancel the emitter.
1601-
diag.cancel();
1601+
// NOTE(eddyb) this was `.cancel()`, but `diag`
1602+
// is borrowed, so we can't fully defuse it.
1603+
diag.downgrade_to_delayed_bug();
16021604
return;
16031605
}
16041606
};

compiler/rustc_interface/src/interface.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
102102
}
103103

104104
match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
105-
Ok(mut parser) => match &mut parser.parse_meta_item() {
105+
Ok(mut parser) => match parser.parse_meta_item() {
106106
Ok(meta_item) if parser.token == token::Eof => {
107107
if meta_item.path.segments.len() != 1 {
108108
error!("argument key must be an identifier");
@@ -121,7 +121,7 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
121121
Ok(..) => {}
122122
Err(err) => err.cancel(),
123123
},
124-
Err(errs) => errs.into_iter().for_each(|mut err| err.cancel()),
124+
Err(errs) => drop(errs),
125125
}
126126

127127
// If the user tried to use a key="value" flag, but is missing the quotes, provide
@@ -165,7 +165,7 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
165165
}
166166

167167
match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
168-
Ok(mut parser) => match &mut parser.parse_meta_item() {
168+
Ok(mut parser) => match parser.parse_meta_item() {
169169
Ok(meta_item) if parser.token == token::Eof => {
170170
if let Some(args) = meta_item.meta_item_list() {
171171
if meta_item.has_name(sym::names) {
@@ -210,7 +210,7 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
210210
Ok(..) => {}
211211
Err(err) => err.cancel(),
212212
},
213-
Err(errs) => errs.into_iter().for_each(|mut err| err.cancel()),
213+
Err(errs) => drop(errs),
214214
}
215215

216216
error!(

compiler/rustc_parse/src/parser/attr.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ impl<'a> Parser<'a> {
165165
loop {
166166
// skip any other attributes, we want the item
167167
if snapshot.token.kind == token::Pound {
168-
if let Err(mut err) = snapshot.parse_attribute(InnerAttrPolicy::Permitted) {
168+
if let Err(err) = snapshot.parse_attribute(InnerAttrPolicy::Permitted) {
169169
err.cancel();
170170
return Some(replacement_span);
171171
}
@@ -206,7 +206,7 @@ impl<'a> Parser<'a> {
206206
);
207207
return None;
208208
}
209-
Err(mut item_err) => {
209+
Err(item_err) => {
210210
item_err.cancel();
211211
}
212212
Ok(None) => {}
@@ -412,12 +412,12 @@ impl<'a> Parser<'a> {
412412
fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
413413
match self.parse_unsuffixed_lit() {
414414
Ok(lit) => return Ok(ast::NestedMetaItem::Literal(lit)),
415-
Err(ref mut err) => err.cancel(),
415+
Err(err) => err.cancel(),
416416
}
417417

418418
match self.parse_meta_item() {
419419
Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)),
420-
Err(ref mut err) => err.cancel(),
420+
Err(err) => err.cancel(),
421421
}
422422

423423
let found = pprust::token_to_string(&self.token);

0 commit comments

Comments
 (0)