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`
This commit is contained in:
bors
2022-02-25 00:46:04 +00:00
134 changed files with 1497 additions and 1143 deletions

View File

@@ -7,7 +7,7 @@ use crate::ty::{
ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
@@ -129,7 +129,7 @@ impl<'tcx> Ty<'tcx> {
pub fn suggest_arbitrary_trait_bound(
generics: &hir::Generics<'_>,
err: &mut DiagnosticBuilder<'_>,
err: &mut Diagnostic,
param_name: &str,
constraint: &str,
) -> bool {
@@ -159,7 +159,7 @@ pub fn suggest_arbitrary_trait_bound(
fn suggest_removing_unsized_bound(
generics: &hir::Generics<'_>,
err: &mut DiagnosticBuilder<'_>,
err: &mut Diagnostic,
param_name: &str,
param: &hir::GenericParam<'_>,
def_id: Option<DefId>,
@@ -266,7 +266,7 @@ fn suggest_removing_unsized_bound(
pub fn suggest_constraining_type_param(
tcx: TyCtxt<'_>,
generics: &hir::Generics<'_>,
err: &mut DiagnosticBuilder<'_>,
err: &mut Diagnostic,
param_name: &str,
constraint: &str,
def_id: Option<DefId>,

View File

@@ -3,7 +3,7 @@ use crate::ty::diagnostics::suggest_constraining_type_param;
use crate::ty::print::{FmtPrinter, Printer};
use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
use rustc_errors::{pluralize, DiagnosticBuilder};
use rustc_errors::{pluralize, Diagnostic};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_span::symbol::{sym, Symbol};
@@ -347,7 +347,8 @@ impl<'tcx> Ty<'tcx> {
impl<'tcx> TyCtxt<'tcx> {
pub fn note_and_explain_type_err(
self,
db: &mut DiagnosticBuilder<'_>,
// FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
db: &mut Diagnostic,
err: &TypeError<'tcx>,
cause: &ObligationCause<'tcx>,
sp: Span,
@@ -584,7 +585,8 @@ impl<T> Trait<T> for X {
fn suggest_constraint(
self,
db: &mut DiagnosticBuilder<'_>,
// FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
db: &mut Diagnostic,
msg: &str,
body_owner_def_id: DefId,
proj_ty: &ty::ProjectionTy<'tcx>,
@@ -671,7 +673,8 @@ impl<T> Trait<T> for X {
/// fn that returns the type.
fn expected_projection(
self,
db: &mut DiagnosticBuilder<'_>,
// FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
db: &mut Diagnostic,
proj_ty: &ty::ProjectionTy<'tcx>,
values: &ExpectedFound<Ty<'tcx>>,
body_owner_def_id: DefId,
@@ -766,7 +769,8 @@ fn foo(&self) -> Self::T { String::new() }
/// a return type. This can occur when dealing with `TryStream` (#71035).
fn suggest_constraining_opaque_associated_type(
self,
db: &mut DiagnosticBuilder<'_>,
// FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
db: &mut Diagnostic,
msg: &str,
proj_ty: &ty::ProjectionTy<'tcx>,
ty: Ty<'tcx>,
@@ -802,7 +806,8 @@ fn foo(&self) -> Self::T { String::new() }
fn point_at_methods_that_satisfy_associated_type(
self,
db: &mut DiagnosticBuilder<'_>,
// FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
db: &mut Diagnostic,
assoc_container_id: DefId,
current_method_ident: Option<Symbol>,
proj_ty_item_def_id: DefId,
@@ -857,7 +862,8 @@ fn foo(&self) -> Self::T { String::new() }
fn point_at_associated_type(
self,
db: &mut DiagnosticBuilder<'_>,
// FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
db: &mut Diagnostic,
body_owner_def_id: DefId,
found: Ty<'tcx>,
) -> bool {
@@ -921,7 +927,8 @@ fn foo(&self) -> Self::T { String::new() }
/// type is defined on a supertrait of the one present in the bounds.
fn constrain_generic_bound_associated_type_structured_suggestion(
self,
db: &mut DiagnosticBuilder<'_>,
// FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
db: &mut Diagnostic,
trait_ref: &ty::TraitRef<'tcx>,
bounds: hir::GenericBounds<'_>,
assoc: &ty::AssocItem,
@@ -958,7 +965,8 @@ fn foo(&self) -> Self::T { String::new() }
/// associated type to a given type `ty`.
fn constrain_associated_type_structured_suggestion(
self,
db: &mut DiagnosticBuilder<'_>,
// FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
db: &mut Diagnostic,
span: Span,
assoc: &ty::AssocItem,
assoc_substs: &[ty::GenericArg<'tcx>],