1
Fork 0

Auto merge of #101986 - WaffleLapkin:move_lint_note_to_the_bottom, r=estebank

Move lint level source explanation to the bottom

So, uhhhhh

r? `@estebank`

## User-facing change

"note: `#[warn(...)]` on by default" and such are moved to the bottom of the diagnostic:
```diff
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678>
+   = note: `#[warn(unsupported_calling_conventions)]` on by default
```

Why warning is enabled is the least important thing, so it shouldn't be the first note the user reads, IMO.

## Developer-facing change

`struct_span_lint` and similar methods have a different signature.

Before: `..., impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>)`
After: `..., impl Into<DiagnosticMessage>, impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>) -> &'b mut DiagnosticBuilder<'a, ()>`

The reason for this is that `struct_span_lint` needs to edit the diagnostic _after_ `decorate` closure is called. This also makes lint code a little bit nicer in my opinion.

Another option is to use `impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>) -> DiagnosticBuilder<'a, ()>` altough I don't _really_ see reasons to do `let lint = lint.build(message)` everywhere.

## Subtle problem

By moving the message outside of the closure (that may not be called if the lint is disabled) `format!(...)` is executed earlier, possibly formatting `Ty` which may call a query that trims paths that crashes the compiler if there were no warnings...

I don't think it's that big of a deal, considering that we move from `format!(...)` to `fluent` (which is lazy by-default) anyway, however this required adding a workaround which is unfortunate.

## P.S.

I'm sorry, I do not how to make this PR smaller/easier to review. Changes to the lint API affect SO MUCH 😢
This commit is contained in:
bors 2022-10-01 10:44:25 +00:00
commit 744e397d88
827 changed files with 3370 additions and 3210 deletions

View file

@ -89,15 +89,8 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
UNSAFE_OP_IN_UNSAFE_FN,
self.hir_context,
span,
|lint| {
lint.build(&format!(
"{} is unsafe and requires unsafe block (error E0133)",
description,
))
.span_label(span, kind.simple_description())
.note(note)
.emit();
},
format!("{} is unsafe and requires unsafe block (error E0133)", description,),
|lint| lint.span_label(span, kind.simple_description()).note(note),
)
}
SafetyContext::Safe => {
@ -125,14 +118,13 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
enclosing_unsafe: Option<(Span, &'static str)>,
) {
let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, |lint| {
let msg = "unnecessary `unsafe` block";
let mut db = lint.build(msg);
db.span_label(block_span, msg);
let msg = "unnecessary `unsafe` block";
self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, msg, |lint| {
lint.span_label(block_span, msg);
if let Some((span, kind)) = enclosing_unsafe {
db.span_label(span, format!("because it's nested under this `unsafe` {}", kind));
lint.span_label(span, format!("because it's nested under this `unsafe` {}", kind));
}
db.emit();
lint
});
}

View file

@ -36,16 +36,20 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
let sp = tcx.def_span(def_id);
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
tcx.struct_span_lint_hir(UNCONDITIONAL_RECURSION, hir_id, sp, |lint| {
let mut db = lint.build("function cannot return without recursing");
db.span_label(sp, "cannot return without recursing");
// offer some help to the programmer.
for call_span in vis.reachable_recursive_calls {
db.span_label(call_span, "recursive call site");
}
db.help("a `loop` may express intention better if this is on purpose");
db.emit();
});
tcx.struct_span_lint_hir(
UNCONDITIONAL_RECURSION,
hir_id,
sp,
"function cannot return without recursing",
|lint| {
lint.span_label(sp, "cannot return without recursing");
// offer some help to the programmer.
for call_span in vis.reachable_recursive_calls {
lint.span_label(call_span, "recursive call site");
}
lint.help("a `loop` may express intention better if this is on purpose")
},
);
}
}

View file

@ -7,7 +7,7 @@ use super::{PatCtxt, PatternError};
use rustc_arena::TypedArena;
use rustc_ast::Mutability;
use rustc_errors::{
error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder,
error_code, pluralize, struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder,
ErrorGuaranteed, MultiSpan,
};
use rustc_hir as hir;
@ -347,19 +347,23 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let span_end = affix.last().unwrap().unwrap().0;
let span = span_start.to(span_end);
let cnt = affix.len();
cx.tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, top, span, |lint| {
let s = pluralize!(cnt);
let mut diag = lint.build(&format!("{kind} irrefutable pattern{s} in let chain"));
diag.note(&format!(
"{these} pattern{s} will always match",
these = pluralize!("this", cnt),
));
diag.help(&format!(
"consider moving {} {suggestion}",
if cnt > 1 { "them" } else { "it" }
));
diag.emit()
});
let s = pluralize!(cnt);
cx.tcx.struct_span_lint_hir(
IRREFUTABLE_LET_PATTERNS,
top,
span,
format!("{kind} irrefutable pattern{s} in let chain"),
|lint| {
lint.note(format!(
"{these} pattern{s} will always match",
these = pluralize!("this", cnt),
))
.help(format!(
"consider moving {} {suggestion}",
if cnt > 1 { "them" } else { "it" }
))
},
);
};
if let Some(until) = chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) && until > 0 {
// The chain has a non-zero prefix of irrefutable `let` statements.
@ -561,26 +565,28 @@ fn check_for_bindings_named_same_as_variants(
BINDINGS_WITH_VARIANT_NAME,
p.hir_id,
p.span,
DelayDm(|| format!(
"pattern binding `{}` is named the same as one \
of the variants of the type `{}`",
ident, cx.tcx.def_path_str(edef.did())
)),
|lint| {
let ty_path = cx.tcx.def_path_str(edef.did());
let mut err = lint.build(&format!(
"pattern binding `{}` is named the same as one \
of the variants of the type `{}`",
ident, ty_path
));
err.code(error_code!(E0170));
lint.code(error_code!(E0170));
// If this is an irrefutable pattern, and there's > 1 variant,
// then we can't actually match on this. Applying the below
// suggestion would produce code that breaks on `check_irrefutable`.
if rf == Refutable || variant_count == 1 {
err.span_suggestion(
lint.span_suggestion(
p.span,
"to match on the variant, qualify the path",
format!("{}::{}", ty_path, ident),
Applicability::MachineApplicable,
);
}
err.emit();
lint
},
)
}
@ -598,14 +604,13 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
}
fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, |lint| {
let mut err = lint.build("unreachable pattern");
tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern", |lint| {
if let Some(catchall) = catchall {
// We had a catchall pattern, hint at that.
err.span_label(span, "unreachable pattern");
err.span_label(catchall, "matches any value");
lint.span_label(span, "unreachable pattern");
lint.span_label(catchall, "matches any value");
}
err.emit();
lint
});
}
@ -621,6 +626,11 @@ fn irrefutable_let_patterns(
count: usize,
span: Span,
) {
let span = match source {
LetSource::LetElse(span) => span,
_ => span,
};
macro_rules! emit_diag {
(
$lint:expr,
@ -630,18 +640,23 @@ fn irrefutable_let_patterns(
) => {{
let s = pluralize!(count);
let these = pluralize!("this", count);
let mut diag = $lint.build(&format!("irrefutable {} pattern{s}", $source_name));
diag.note(&format!("{these} pattern{s} will always match, so the {}", $note_sufix));
diag.help(concat!("consider ", $help_sufix));
diag.emit()
tcx.struct_span_lint_hir(
IRREFUTABLE_LET_PATTERNS,
id,
span,
format!("irrefutable {} pattern{s}", $source_name),
|lint| {
lint.note(&format!(
"{these} pattern{s} will always match, so the {}",
$note_sufix
))
.help(concat!("consider ", $help_sufix))
},
)
}};
}
let span = match source {
LetSource::LetElse(span) => span,
_ => span,
};
tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match source {
match source {
LetSource::GenericLet => {
emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
}
@ -677,7 +692,7 @@ fn irrefutable_let_patterns(
"instead using a `loop { ... }` with a `let` inside it"
);
}
});
};
}
fn is_let_irrefutable<'p, 'tcx>(

View file

@ -1,3 +1,4 @@
use rustc_errors::DelayDm;
use rustc_hir as hir;
use rustc_index::vec::Idx;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@ -205,9 +206,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
self.span,
|lint| {
lint.build(&msg).emit();
},
msg,
|lint| lint,
);
} else {
debug!(
@ -286,9 +286,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
id,
span,
|lint| {
lint.build("floating-point types cannot be used in patterns").emit();
},
"floating-point types cannot be used in patterns",
|lint| lint,
);
}
PatKind::Constant { value: cv }
@ -340,15 +339,15 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
id,
span,
|lint| {
let msg = format!(
DelayDm(|| {
format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
cv.ty(),
cv.ty(),
);
lint.build(&msg).emit();
},
)
}),
|lint| lint,
);
}
// Since we are behind a reference, we can just bubble the error up so we get a
@ -488,7 +487,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
self.span,
|lint| {lint.build(&msg).emit();},
msg,
|lint| lint,
);
}
PatKind::Constant { value: cv }
@ -556,9 +556,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
lint::builtin::POINTER_STRUCTURAL_MATCH,
id,
span,
|lint| {
lint.build(msg).emit();
},
msg,
|lint| lint,
);
}
PatKind::Constant { value: cv }
@ -594,9 +593,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
id,
span,
|lint| {
lint.build(&msg).emit();
},
msg,
|lint| lint,
);
}

View file

@ -299,10 +299,10 @@ impl IntRange {
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
hir_id,
pcx.span,
"multiple patterns overlap on their endpoints",
|lint| {
let mut err = lint.build("multiple patterns overlap on their endpoints");
for (int_range, span) in overlaps {
err.span_label(
lint.span_label(
span,
&format!(
"this range overlaps on `{}`...",
@ -310,9 +310,9 @@ impl IntRange {
),
);
}
err.span_label(pcx.span, "... with this range");
err.note("you likely meant to write mutually exclusive ranges");
err.emit();
lint.span_label(pcx.span, "... with this range");
lint.note("you likely meant to write mutually exclusive ranges");
lint
},
);
}

View file

@ -754,9 +754,8 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>(
hir_id: HirId,
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
) {
cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, |build| {
cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, "some variants are not matched explicitly", |lint| {
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
let mut lint = build.build("some variants are not matched explicitly");
lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
lint.help(
"ensure that all variants are matched explicitly by adding the suggested match arms",
@ -765,7 +764,7 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>(
"the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found",
scrut_ty,
));
lint.emit();
lint
});
}