Show code suggestions in let_undescore lint messages.

This commit uses `span_suggestion_verbose` to add what specific code
changes can be done as suggested by the lint--in this case, either binding
the expression to an unused variable or using `std::mem::drop` to drop
the value explicitly.
This commit is contained in:
Aaron Kofsky 2022-06-03 21:33:13 -04:00
parent ae2ac3b4c5
commit eba6c789dc
4 changed files with 79 additions and 21 deletions

View File

@ -1,6 +1,10 @@
use crate::{LateContext, LateLintPass, LintContext}; use crate::{LateContext, LateLintPass, LintContext};
use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; use rustc_middle::{
lint::LintDiagnosticBuilder,
ty::{self, subst::GenericArgKind, Ty},
};
use rustc_span::Symbol; use rustc_span::Symbol;
declare_lint! { declare_lint! {
@ -141,30 +145,60 @@ fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
}); });
let is_must_use_ty = is_must_use_ty(cx, cx.typeck_results().expr_ty(init)); let is_must_use_ty = is_must_use_ty(cx, cx.typeck_results().expr_ty(init));
let is_must_use_func_call = is_must_use_func_call(cx, init); let is_must_use_func_call = is_must_use_func_call(cx, init);
if is_sync_lock { if is_sync_lock {
cx.struct_span_lint(LET_UNDERSCORE_LOCK, local.span, |lint| { cx.struct_span_lint(LET_UNDERSCORE_LOCK, local.span, |lint| {
lint.build("non-binding let on a synchronization lock") build_and_emit_lint(
.help("consider binding to an unused variable") lint,
.help("consider explicitly droping with `std::mem::drop`") local,
.emit(); init.span,
"non-binding let on a synchronization lock",
)
}) })
} else if is_must_use_ty || is_must_use_func_call { } else if is_must_use_ty || is_must_use_func_call {
cx.struct_span_lint(LET_UNDERSCORE_MUST_USE, local.span, |lint| { cx.struct_span_lint(LET_UNDERSCORE_MUST_USE, local.span, |lint| {
lint.build("non-binding let on a expression marked `must_use`") build_and_emit_lint(
.help("consider binding to an unused variable") lint,
.help("consider explicitly droping with `std::mem::drop`") local,
.emit(); init.span,
"non-binding let on a expression marked `must_use`",
);
}) })
} else if needs_drop { } else if needs_drop {
cx.struct_span_lint(LET_UNDERSCORE_DROP, local.span, |lint| { cx.struct_span_lint(LET_UNDERSCORE_DROP, local.span, |lint| {
lint.build("non-binding let on a type that implements `Drop`") build_and_emit_lint(
.help("consider binding to an unused variable") lint,
.help("consider explicitly droping with `std::mem::drop`") local,
.emit(); init.span,
"non-binding let on a type that implements `Drop`",
);
}) })
} }
} }
fn build_and_emit_lint(
lint: LintDiagnosticBuilder<'_, ()>,
local: &hir::Local<'_>,
init_span: rustc_span::Span,
msg: &str,
) {
lint.build(msg)
.span_suggestion_verbose(
local.pat.span,
"consider binding to an unused variable",
"_unused",
Applicability::MachineApplicable,
)
.span_suggestion_verbose(
init_span,
"consider explicitly droping with `std::mem::drop`",
"drop(...)",
Applicability::HasPlaceholders,
)
.emit();
}
// return true if `ty` is a type that is marked as `must_use`
fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
match ty.kind() { match ty.kind() {
ty::Adt(adt, _) => has_must_use_attr(cx, adt.did()), ty::Adt(adt, _) => has_must_use_attr(cx, adt.did()),

View File

@ -5,8 +5,14 @@ LL | let _ = NontrivialDrop;
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
| |
= note: requested on the command line with `-W let-underscore-drop` = note: requested on the command line with `-W let-underscore-drop`
= help: consider binding to an unused variable help: consider binding to an unused variable
= help: consider explicitly droping with `std::mem::drop` |
LL | let _unused = NontrivialDrop;
| ~~~~~~~
help: consider explicitly droping with `std::mem::drop`
|
LL | let _ = drop(...);
| ~~~~~~~~~
warning: 1 warning emitted warning: 1 warning emitted

View File

@ -5,8 +5,14 @@ LL | let _ = data.lock().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= note: `#[warn(let_underscore_lock)]` on by default = note: `#[warn(let_underscore_lock)]` on by default
= help: consider binding to an unused variable help: consider binding to an unused variable
= help: consider explicitly droping with `std::mem::drop` |
LL | let _unused = data.lock().unwrap();
| ~~~~~~~
help: consider explicitly droping with `std::mem::drop`
|
LL | let _ = drop(...);
| ~~~~~~~~~
warning: 1 warning emitted warning: 1 warning emitted

View File

@ -5,8 +5,14 @@ LL | let _ = MustUseType;
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
| |
= note: requested on the command line with `-W let-underscore-must-use` = note: requested on the command line with `-W let-underscore-must-use`
= help: consider binding to an unused variable help: consider binding to an unused variable
= help: consider explicitly droping with `std::mem::drop` |
LL | let _unused = MustUseType;
| ~~~~~~~
help: consider explicitly droping with `std::mem::drop`
|
LL | let _ = drop(...);
| ~~~~~~~~~
warning: non-binding let on a expression marked `must_use` warning: non-binding let on a expression marked `must_use`
--> $DIR/let_underscore_must_use.rs:12:5 --> $DIR/let_underscore_must_use.rs:12:5
@ -14,8 +20,14 @@ warning: non-binding let on a expression marked `must_use`
LL | let _ = must_use_function(); LL | let _ = must_use_function();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: consider binding to an unused variable help: consider binding to an unused variable
= help: consider explicitly droping with `std::mem::drop` |
LL | let _unused = must_use_function();
| ~~~~~~~
help: consider explicitly droping with `std::mem::drop`
|
LL | let _ = drop(...);
| ~~~~~~~~~
warning: 2 warnings emitted warning: 2 warnings emitted