diff options
| author | bors <bors@rust-lang.org> | 2022-02-25 00:46:04 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-02-25 00:46:04 +0000 |
| commit | d4de1f230ca30b7ce08fbf453daebf8b2e7ffcc9 (patch) | |
| tree | 922a1d8bf2850b2f35dda5bc6cc14e9194c375c1 /src/tools | |
| parent | 4e82f35492ea5c78e19609bf4468f0a686d9a756 (diff) | |
| parent | b7e95dee65c35db8f8e07046d445b12d92cbae12 (diff) | |
| download | rust-d4de1f230ca30b7ce08fbf453daebf8b2e7ffcc9.tar.gz rust-d4de1f230ca30b7ce08fbf453daebf8b2e7ffcc9.zip | |
Auto merge of #93368 - eddyb:diagbld-guarantee, r=estebank
rustc_errors: let `DiagnosticBuilder::emit` return a "guarantee of emission".
That is, `DiagnosticBuilder` is now generic over the return type of `.emit()`, so we'll now have:
* `DiagnosticBuilder<ErrorReported>` for error (incl. fatal/bug) diagnostics
* can only be created via a `const L: Level`-generic constructor, that limits allowed variants via a `where` clause, so not even `rustc_errors` can accidentally bypass this limitation
* asserts `diagnostic.is_error()` on emission, just in case the construction restriction was bypassed (e.g. by replacing the whole `Diagnostic` inside `DiagnosticBuilder`)
* `.emit()` returns `ErrorReported`, as a "proof" token that `.emit()` was called
(though note that this isn't a real guarantee until after completing the work on
#69426)
* `DiagnosticBuilder<()>` for everything else (warnings, notes, etc.)
* can also be obtained from other `DiagnosticBuilder`s by calling `.forget_guarantee()`
This PR is a companion to other ongoing work, namely:
* #69426
and it's ongoing implementation:
#93222
the API changes in this PR are needed to get statically-checked "only errors produce `ErrorReported` from `.emit()`", but doesn't itself provide any really strong guarantees without those other `ErrorReported` changes
* #93244
would make the choices of API changes (esp. naming) in this PR fit better overall
In order to be able to let `.emit()` return anything trustable, several changes had to be made:
* `Diagnostic`'s `level` field is now private to `rustc_errors`, to disallow arbitrary "downgrade"s from "some kind of error" to "warning" (or anything else that doesn't cause compilation to fail)
* it's still possible to replace the whole `Diagnostic` inside the `DiagnosticBuilder`, sadly, that's harder to fix, but it's unlikely enough that we can paper over it with asserts on `.emit()`
* `.cancel()` now consumes `DiagnosticBuilder`, preventing `.emit()` calls on a cancelled diagnostic
* it's also now done internally, through `DiagnosticBuilder`-private state, instead of having a `Level::Cancelled` variant that can be read (or worse, written) by the user
* this removes a hazard of calling `.cancel()` on an error then continuing to attach details to it, and even expect to be able to `.emit()` it
* warnings were switched to *only* `can_emit_warnings` on emission (instead of pre-cancelling early)
* `struct_dummy` was removed (as it relied on a pre-`Cancelled` `Diagnostic`)
* since `.emit()` doesn't consume the `DiagnosticBuilder` <sub>(I tried and gave up, it's much more work than this PR)</sub>,
we have to make `.emit()` idempotent wrt the guarantees it returns
* thankfully, `err.emit(); err.emit();` can return `ErrorReported` both times, as the second `.emit()` call has no side-effects *only* because the first one did do the appropriate emission
* `&mut Diagnostic` is now used in a lot of function signatures, which used to take `&mut DiagnosticBuilder` (in the interest of not having to make those functions generic)
* the APIs were already mostly identical, allowing for low-effort porting to this new setup
* only some of the suggestion methods needed some rework, to have the extra `DiagnosticBuilder` functionality on the `Diagnostic` methods themselves (that change is also present in #93259)
* `.emit()`/`.cancel()` aren't available, but IMO calling them from an "error decorator/annotator" function isn't a good practice, and can lead to strange behavior (from the caller's perspective)
* `.downgrade_to_delayed_bug()` was added, letting you convert any `.is_error()` diagnostic into a `delay_span_bug` one (which works because in both cases the guarantees available are the same)
This PR should ideally be reviewed commit-by-commit, since there is a lot of fallout in each.
r? `@estebank` cc `@Manishearth` `@nikomatsakis` `@mark-i-m`
Diffstat (limited to 'src/tools')
| -rw-r--r-- | src/tools/clippy/clippy_lints/src/copies.rs | 4 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_lints/src/doc.rs | 6 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_lints/src/implicit_hasher.rs | 4 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs | 2 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs | 4 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_lints/src/new_without_default.rs | 2 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_lints/src/write.rs | 4 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/diagnostics.rs | 12 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/sugg.rs | 6 | ||||
| -rw-r--r-- | src/tools/rustfmt/src/modules.rs | 2 | ||||
| -rw-r--r-- | src/tools/rustfmt/src/parse/macros/cfg_if.rs | 2 | ||||
| -rw-r--r-- | src/tools/rustfmt/src/parse/macros/lazy_static.rs | 2 | ||||
| -rw-r--r-- | src/tools/rustfmt/src/parse/macros/mod.rs | 2 | ||||
| -rw-r--r-- | src/tools/rustfmt/src/parse/parser.rs | 2 | ||||
| -rw-r--r-- | src/tools/rustfmt/src/parse/session.rs | 29 |
15 files changed, 33 insertions, 50 deletions
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 8b79f1600ae..a20aa12c9ff 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -6,7 +6,7 @@ use clippy_utils::{ }; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Block, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -489,7 +489,7 @@ fn emit_branches_sharing_code_lint( add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit(); } - let add_optional_msgs = |diag: &mut DiagnosticBuilder<'_>| { + let add_optional_msgs = |diag: &mut Diagnostic| { if add_expr_note { diag.note("The end suggestion probably needs some adjustments to use the expression result correctly"); } diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index a00361e6062..16173580fd4 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -628,9 +628,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { Ok(p) => p, Err(errs) => { - for mut err in errs { - err.cancel(); - } + drop(errs); return false; }, }; @@ -668,7 +666,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { _ => {}, }, Ok(None) => break, - Err(mut e) => { + Err(e) => { e.cancel(); return false; }, diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 5e4cde553b5..d5430a8c917 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; -use rustc_errors::DiagnosticBuilder; +use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { fn suggestion<'tcx>( cx: &LateContext<'tcx>, - diag: &mut DiagnosticBuilder<'_>, + diag: &mut Diagnostic, generics_span: Span, generics_suggestion_span: Span, target: &ImplicitHasherType<'_>, diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs index df69d3dcc51..dd7177e0131 100644 --- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs +++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs @@ -1,7 +1,7 @@ //! checks for `#[inline]` on trait methods without bodies use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::sugg::DiagnosticBuilderExt; +use clippy_utils::sugg::DiagnosticExt; use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index d27e1383d01..ebfd908a6fb 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -6,7 +6,7 @@ use clippy_utils::{get_trait_def_id, is_self, paths}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::intravisit::FnKind; use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Node, PatKind, QPath, TyKind}; use rustc_hir::{HirIdMap, HirIdSet}; @@ -196,7 +196,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } // Dereference suggestion - let sugg = |diag: &mut DiagnosticBuilder<'_>| { + let sugg = |diag: &mut Diagnostic| { if let ty::Adt(def, ..) = ty.kind() { if let Some(span) = cx.tcx.hir().span_if_local(def.did) { if can_type_implement_copy(cx.tcx, cx.param_env, ty, traits::ObligationCause::dummy_with_span(span)).is_ok() { diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs index f86af7a7bb6..4cb79648ae3 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::return_ty; use clippy_utils::source::snippet; -use clippy_utils::sugg::DiagnosticBuilderExt; +use clippy_utils::sugg::DiagnosticExt; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 1fa6301ebd7..a328ddda5ae 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -534,7 +534,7 @@ impl Write { match parser .parse_expr() .map(rustc_ast::ptr::P::into_inner) - .map_err(|mut e| e.cancel()) + .map_err(|e| e.cancel()) { // write!(e, ...) Ok(p) if parser.eat(&token::Comma) => Some(p), @@ -563,7 +563,7 @@ impl Write { } let comma_span = parser.prev_token.span; - let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) { + let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|err| err.cancel()) { expr } else { return (Some(fmtstr), None); diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index ca222c3d669..a927788e6a4 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -8,13 +8,13 @@ //! Thank you! //! ~The `INTERNAL_METADATA_COLLECTOR` lint -use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::HirId; use rustc_lint::{LateContext, Lint, LintContext}; use rustc_span::source_map::{MultiSpan, Span}; use std::env; -fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) { +fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { diag.help(&format!( @@ -145,7 +145,7 @@ pub fn span_lint_and_then<C, S, F>(cx: &C, lint: &'static Lint, sp: S, msg: &str where C: LintContext, S: Into<MultiSpan>, - F: FnOnce(&mut DiagnosticBuilder<'_>), + F: FnOnce(&mut Diagnostic), { cx.struct_span_lint(lint, sp, |diag| { let mut diag = diag.build(msg); @@ -169,7 +169,7 @@ pub fn span_lint_hir_and_then( hir_id: HirId, sp: impl Into<MultiSpan>, msg: &str, - f: impl FnOnce(&mut DiagnosticBuilder<'_>), + f: impl FnOnce(&mut Diagnostic), ) { cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| { let mut diag = diag.build(msg); @@ -219,7 +219,7 @@ pub fn span_lint_and_sugg<'a, T: LintContext>( /// appear once per /// replacement. In human-readable format though, it only appears once before /// the whole suggestion. -pub fn multispan_sugg<I>(diag: &mut DiagnosticBuilder<'_>, help_msg: &str, sugg: I) +pub fn multispan_sugg<I>(diag: &mut Diagnostic, help_msg: &str, sugg: I) where I: IntoIterator<Item = (Span, String)>, { @@ -232,7 +232,7 @@ where /// multiple spans. This is tracked in issue [rustfix#141](https://github.com/rust-lang/rustfix/issues/141). /// Suggestions with multiple spans will be silently ignored. pub fn multispan_sugg_with_applicability<I>( - diag: &mut DiagnosticBuilder<'_>, + diag: &mut Diagnostic, help_msg: &str, applicability: Applicability, sugg: I, diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index fa63ddff253..63c442e7008 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -673,8 +673,8 @@ fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> { }) } -/// Convenience extension trait for `DiagnosticBuilder`. -pub trait DiagnosticBuilderExt<T: LintContext> { +/// Convenience extension trait for `Diagnostic`. +pub trait DiagnosticExt<T: LintContext> { /// Suggests to add an attribute to an item. /// /// Correctly handles indentation of the attribute and item. @@ -721,7 +721,7 @@ pub trait DiagnosticBuilderExt<T: LintContext> { fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability); } -impl<T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder<'_> { +impl<T: LintContext> DiagnosticExt<T> for rustc_errors::Diagnostic { fn suggest_item_with_attr<D: Display + ?Sized>( &mut self, cx: &T, diff --git a/src/tools/rustfmt/src/modules.rs b/src/tools/rustfmt/src/modules.rs index 9c964b274e0..d4bddd95785 100644 --- a/src/tools/rustfmt/src/modules.rs +++ b/src/tools/rustfmt/src/modules.rs @@ -439,7 +439,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { } } Err(mod_err) if !mods_outside_ast.is_empty() => { - if let ModError::ParserError(mut e) = mod_err { + if let ModError::ParserError(e) = mod_err { e.cancel(); } Ok(Some(SubModKind::MultiExternal(mods_outside_ast))) diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs index e10fbe64bcd..306b6bb745e 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs @@ -57,7 +57,7 @@ fn parse_cfg_if_inner<'a>( let item = match parser.parse_item(ForceCollect::No) { Ok(Some(item_ptr)) => item_ptr.into_inner(), Ok(None) => continue, - Err(mut err) => { + Err(err) => { err.cancel(); parser.sess.span_diagnostic.reset_err_count(); return Err( diff --git a/src/tools/rustfmt/src/parse/macros/lazy_static.rs b/src/tools/rustfmt/src/parse/macros/lazy_static.rs index 9c8651aa3fa..4c541de04be 100644 --- a/src/tools/rustfmt/src/parse/macros/lazy_static.rs +++ b/src/tools/rustfmt/src/parse/macros/lazy_static.rs @@ -23,7 +23,7 @@ pub(crate) fn parse_lazy_static( val } } - Err(mut err) => { + Err(err) => { err.cancel(); parser.sess.span_diagnostic.reset_err_count(); return None; diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 2e9ce1d35f4..fd738908170 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -36,7 +36,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> { return Some(MacroArg::$macro_arg($f(x)?)); } } - Err(mut e) => { + Err(e) => { e.cancel(); parser.sess.span_diagnostic.reset_err_count(); } diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs index 657217633f4..f0944a88d2f 100644 --- a/src/tools/rustfmt/src/parse/parser.rs +++ b/src/tools/rustfmt/src/parse/parser.rs @@ -115,7 +115,7 @@ impl<'a> Parser<'a> { match parser.parse_mod(&TokenKind::Eof) { Ok(result) => Some(result), Err(mut e) => { - sess.emit_or_cancel_diagnostic(&mut e); + e.emit(); if sess.can_reset_errors() { sess.reset_errors(); } diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 7fc3778376c..40a6d708d8c 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -60,7 +60,7 @@ impl Emitter for SilentOnIgnoredFilesEmitter { None } fn emit_diagnostic(&mut self, db: &Diagnostic) { - if db.level == DiagnosticLevel::Fatal { + if db.level() == DiagnosticLevel::Fatal { return self.handle_non_ignoreable_error(db); } if let Some(primary_span) = &db.span.primary_span() { @@ -230,17 +230,6 @@ impl ParseSess { } } - pub(crate) fn emit_or_cancel_diagnostic(&self, diagnostic: &mut Diagnostic) { - self.parse_sess.span_diagnostic.emit_diagnostic(diagnostic); - // The Handler will check whether the diagnostic should be emitted - // based on the user's rustfmt configuration and the originating file - // that caused the parser error. If the Handler determined it should skip - // emission then we need to ensure the diagnostic is cancelled. - if !diagnostic.cancelled() { - diagnostic.cancel(); - } - } - pub(super) fn can_reset_errors(&self) -> bool { self.can_reset_errors.load(Ordering::Acquire) } @@ -292,7 +281,7 @@ mod tests { use super::*; use crate::config::IgnoreList; use crate::utils::mk_sp; - use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP}; + use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName}; use std::path::PathBuf; use std::sync::atomic::AtomicU32; @@ -310,16 +299,12 @@ mod tests { } fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic { - Diagnostic { - level, - code: None, - message: vec![], - children: vec![], - suggestions: Ok(vec![]), - span: span.unwrap_or_else(MultiSpan::new), - sort_span: DUMMY_SP, - is_lint: false, + let mut diag = Diagnostic::new(level, ""); + diag.message.clear(); + if let Some(span) = span { + diag.span = span; } + diag } fn build_emitter( |
