mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-12-01 00:49:30 +00:00
223 lines
8 KiB
Rust
223 lines
8 KiB
Rust
use clippy_utils::diagnostics::span_lint_and_help;
|
|
use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
|
|
use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths};
|
|
use rustc_hir::{LetStmt, LocalSource, PatKind};
|
|
use rustc_lint::{LateContext, LateLintPass};
|
|
use rustc_middle::lint::in_external_macro;
|
|
use rustc_middle::ty::{GenericArgKind, IsSuggestable};
|
|
use rustc_session::declare_lint_pass;
|
|
use rustc_span::{BytePos, Span};
|
|
|
|
declare_clippy_lint! {
|
|
/// ### What it does
|
|
/// Checks for `let _ = <expr>` where expr is `#[must_use]`
|
|
///
|
|
/// ### Why restrict this?
|
|
/// To ensure that all `#[must_use]` types are used rather than ignored.
|
|
///
|
|
/// ### Example
|
|
/// ```no_run
|
|
/// fn f() -> Result<u32, u32> {
|
|
/// Ok(0)
|
|
/// }
|
|
///
|
|
/// let _ = f();
|
|
/// // is_ok() is marked #[must_use]
|
|
/// let _ = f().is_ok();
|
|
/// ```
|
|
#[clippy::version = "1.42.0"]
|
|
pub LET_UNDERSCORE_MUST_USE,
|
|
restriction,
|
|
"non-binding `let` on a `#[must_use]` expression"
|
|
}
|
|
|
|
declare_clippy_lint! {
|
|
/// ### What it does
|
|
/// Checks for `let _ = sync_lock`. This supports `mutex` and `rwlock` in
|
|
/// `parking_lot`. For `std` locks see the `rustc` lint
|
|
/// [`let_underscore_lock`](https://doc.rust-lang.org/nightly/rustc/lints/listing/deny-by-default.html#let-underscore-lock)
|
|
///
|
|
/// ### Why is this bad?
|
|
/// This statement immediately drops the lock instead of
|
|
/// extending its lifetime to the end of the scope, which is often not intended.
|
|
/// To extend lock lifetime to the end of the scope, use an underscore-prefixed
|
|
/// name instead (i.e. _lock). If you want to explicitly drop the lock,
|
|
/// `std::mem::drop` conveys your intention better and is less error-prone.
|
|
///
|
|
/// ### Example
|
|
/// ```rust,ignore
|
|
/// let _ = mutex.lock();
|
|
/// ```
|
|
///
|
|
/// Use instead:
|
|
/// ```rust,ignore
|
|
/// let _lock = mutex.lock();
|
|
/// ```
|
|
#[clippy::version = "1.43.0"]
|
|
pub LET_UNDERSCORE_LOCK,
|
|
correctness,
|
|
"non-binding `let` on a synchronization lock"
|
|
}
|
|
|
|
declare_clippy_lint! {
|
|
/// ### What it does
|
|
/// Checks for `let _ = <expr>` where the resulting type of expr implements `Future`
|
|
///
|
|
/// ### Why is this bad?
|
|
/// Futures must be polled for work to be done. The original intention was most likely to await the future
|
|
/// and ignore the resulting value.
|
|
///
|
|
/// ### Example
|
|
/// ```no_run
|
|
/// async fn foo() -> Result<(), ()> {
|
|
/// Ok(())
|
|
/// }
|
|
/// let _ = foo();
|
|
/// ```
|
|
///
|
|
/// Use instead:
|
|
/// ```no_run
|
|
/// # async fn context() {
|
|
/// async fn foo() -> Result<(), ()> {
|
|
/// Ok(())
|
|
/// }
|
|
/// let _ = foo().await;
|
|
/// # }
|
|
/// ```
|
|
#[clippy::version = "1.67.0"]
|
|
pub LET_UNDERSCORE_FUTURE,
|
|
suspicious,
|
|
"non-binding `let` on a future"
|
|
}
|
|
|
|
declare_clippy_lint! {
|
|
/// ### What it does
|
|
/// Checks for `let _ = <expr>` without a type annotation, and suggests to either provide one,
|
|
/// or remove the `let` keyword altogether.
|
|
///
|
|
/// ### Why restrict this?
|
|
/// The `let _ = <expr>` expression ignores the value of `<expr>`, but will continue to do so even
|
|
/// if the type were to change, thus potentially introducing subtle bugs. By supplying a type
|
|
/// annotation, one will be forced to re-visit the decision to ignore the value in such cases.
|
|
///
|
|
/// ### Known problems
|
|
/// The `_ = <expr>` is not properly supported by some tools (e.g. IntelliJ) and may seem odd
|
|
/// to many developers. This lint also partially overlaps with the other `let_underscore_*`
|
|
/// lints.
|
|
///
|
|
/// ### Example
|
|
/// ```no_run
|
|
/// fn foo() -> Result<u32, ()> {
|
|
/// Ok(123)
|
|
/// }
|
|
/// let _ = foo();
|
|
/// ```
|
|
/// Use instead:
|
|
/// ```no_run
|
|
/// fn foo() -> Result<u32, ()> {
|
|
/// Ok(123)
|
|
/// }
|
|
/// // Either provide a type annotation:
|
|
/// let _: Result<u32, ()> = foo();
|
|
/// // …or drop the let keyword:
|
|
/// _ = foo();
|
|
/// ```
|
|
#[clippy::version = "1.69.0"]
|
|
pub LET_UNDERSCORE_UNTYPED,
|
|
restriction,
|
|
"non-binding `let` without a type annotation"
|
|
}
|
|
|
|
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]);
|
|
|
|
const SYNC_GUARD_PATHS: [&[&str]; 3] = [
|
|
&paths::PARKING_LOT_MUTEX_GUARD,
|
|
&paths::PARKING_LOT_RWLOCK_READ_GUARD,
|
|
&paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
|
|
];
|
|
|
|
impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
|
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) {
|
|
if matches!(local.source, LocalSource::Normal)
|
|
&& !in_external_macro(cx.tcx.sess, local.span)
|
|
&& let PatKind::Wild = local.pat.kind
|
|
&& let Some(init) = local.init
|
|
{
|
|
let init_ty = cx.typeck_results().expr_ty(init);
|
|
let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
|
|
GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)),
|
|
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
|
|
});
|
|
if contains_sync_guard {
|
|
span_lint_and_help(
|
|
cx,
|
|
LET_UNDERSCORE_LOCK,
|
|
local.span,
|
|
"non-binding `let` on a synchronization lock",
|
|
None,
|
|
"consider using an underscore-prefixed named \
|
|
binding or dropping explicitly with `std::mem::drop`",
|
|
);
|
|
} else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
|
|
&& implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[])
|
|
{
|
|
span_lint_and_help(
|
|
cx,
|
|
LET_UNDERSCORE_FUTURE,
|
|
local.span,
|
|
"non-binding `let` on a future",
|
|
None,
|
|
"consider awaiting the future or dropping explicitly with `std::mem::drop`",
|
|
);
|
|
} else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
|
|
span_lint_and_help(
|
|
cx,
|
|
LET_UNDERSCORE_MUST_USE,
|
|
local.span,
|
|
"non-binding `let` on an expression with `#[must_use]` type",
|
|
None,
|
|
"consider explicitly using expression value",
|
|
);
|
|
} else if is_must_use_func_call(cx, init) {
|
|
span_lint_and_help(
|
|
cx,
|
|
LET_UNDERSCORE_MUST_USE,
|
|
local.span,
|
|
"non-binding `let` on a result of a `#[must_use]` function",
|
|
None,
|
|
"consider explicitly using function result",
|
|
);
|
|
}
|
|
|
|
if local.pat.default_binding_modes && local.ty.is_none() {
|
|
// When `default_binding_modes` is true, the `let` keyword is present.
|
|
|
|
// Ignore unnameable types
|
|
if let Some(init) = local.init
|
|
&& !cx.typeck_results().expr_ty(init).is_suggestable(cx.tcx, true)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Ignore if it is from a procedural macro...
|
|
if is_from_proc_macro(cx, init) {
|
|
return;
|
|
}
|
|
|
|
span_lint_and_help(
|
|
cx,
|
|
LET_UNDERSCORE_UNTYPED,
|
|
local.span,
|
|
"non-binding `let` without a type annotation",
|
|
Some(Span::new(
|
|
local.pat.span.hi(),
|
|
local.pat.span.hi() + BytePos(1),
|
|
local.pat.span.ctxt(),
|
|
local.pat.span.parent(),
|
|
)),
|
|
"consider adding a type annotation",
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|