-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rollup merge of #97739 - a2aaron:let_underscore, r=estebank
Uplift the `let_underscore` lints from clippy into rustc. This PR resolves #97241. This PR adds three lints from clippy--`let_underscore_drop`, `let_underscore_lock`, and `let_underscore_must_use`, which are meant to capture likely-incorrect uses of `let _ = ...` bindings (in particular, doing this on a type with a non-trivial `Drop` causes the `Drop` to occur immediately, instead of at the end of the scope. For a type like `MutexGuard`, this effectively releases the lock immediately, which is almost certainly the wrong behavior) In porting the lints from clippy I had to copy over a bunch of utility functions from `clippy_util` that these lints also relied upon. Is that the right approach? Note that I've set the `must_use` and `drop` lints to Allow by default and set `lock` to Deny by default (this matches the same settings that clippy has). In talking with `@estebank` he informed me to do a Crater run (I am not sure what type of Crater run to request here--I think it's just "check only"?) On the linked issue, there's some discussion about using `must_use` and `Drop` together as a heuristic for when to warn--I did not implement this yet. r? `@estebank`
- Loading branch information
Showing
10 changed files
with
250 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
use crate::{LateContext, LateLintPass, LintContext}; | ||
use rustc_errors::{Applicability, LintDiagnosticBuilder, MultiSpan}; | ||
use rustc_hir as hir; | ||
use rustc_middle::ty; | ||
use rustc_span::Symbol; | ||
|
||
declare_lint! { | ||
/// The `let_underscore_drop` lint checks for statements which don't bind | ||
/// an expression which has a non-trivial Drop implementation to anything, | ||
/// causing the expression to be dropped immediately instead of at end of | ||
/// scope. | ||
/// | ||
/// ### Example | ||
/// ``` | ||
/// struct SomeStruct; | ||
/// impl Drop for SomeStruct { | ||
/// fn drop(&mut self) { | ||
/// println!("Dropping SomeStruct"); | ||
/// } | ||
/// } | ||
/// | ||
/// fn main() { | ||
/// #[warn(let_underscore_drop)] | ||
/// // SomeStuct is dropped immediately instead of at end of scope, | ||
/// // so "Dropping SomeStruct" is printed before "end of main". | ||
/// // The order of prints would be reversed if SomeStruct was bound to | ||
/// // a name (such as "_foo"). | ||
/// let _ = SomeStruct; | ||
/// println!("end of main"); | ||
/// } | ||
/// ``` | ||
/// | ||
/// {{produces}} | ||
/// | ||
/// ### Explanation | ||
/// | ||
/// Statements which assign an expression to an underscore causes the | ||
/// expression to immediately drop instead of extending the expression's | ||
/// lifetime to the end of the scope. This is usually unintended, | ||
/// especially for types like `MutexGuard`, which are typically used to | ||
/// lock a mutex for the duration of an entire scope. | ||
/// | ||
/// If you want to extend the expression's lifetime to the end of the scope, | ||
/// assign an underscore-prefixed name (such as `_foo`) to the expression. | ||
/// If you do actually want to drop the expression immediately, then | ||
/// calling `std::mem::drop` on the expression is clearer and helps convey | ||
/// intent. | ||
pub LET_UNDERSCORE_DROP, | ||
Allow, | ||
"non-binding let on a type that implements `Drop`" | ||
} | ||
|
||
declare_lint! { | ||
/// The `let_underscore_lock` lint checks for statements which don't bind | ||
/// a mutex to anything, causing the lock to be released immediately instead | ||
/// of at end of scope, which is typically incorrect. | ||
/// | ||
/// ### Example | ||
/// ```compile_fail | ||
/// use std::sync::{Arc, Mutex}; | ||
/// use std::thread; | ||
/// let data = Arc::new(Mutex::new(0)); | ||
/// | ||
/// thread::spawn(move || { | ||
/// // The lock is immediately released instead of at the end of the | ||
/// // scope, which is probably not intended. | ||
/// let _ = data.lock().unwrap(); | ||
/// println!("doing some work"); | ||
/// let mut lock = data.lock().unwrap(); | ||
/// *lock += 1; | ||
/// }); | ||
/// ``` | ||
/// | ||
/// {{produces}} | ||
/// | ||
/// ### Explanation | ||
/// | ||
/// Statements which assign an expression to an underscore causes the | ||
/// expression to immediately drop instead of extending the expression's | ||
/// lifetime to the end of the scope. This is usually unintended, | ||
/// especially for types like `MutexGuard`, which are typically used to | ||
/// lock a mutex for the duration of an entire scope. | ||
/// | ||
/// If you want to extend the expression's lifetime to the end of the scope, | ||
/// assign an underscore-prefixed name (such as `_foo`) to the expression. | ||
/// If you do actually want to drop the expression immediately, then | ||
/// calling `std::mem::drop` on the expression is clearer and helps convey | ||
/// intent. | ||
pub LET_UNDERSCORE_LOCK, | ||
Deny, | ||
"non-binding let on a synchronization lock" | ||
} | ||
|
||
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]); | ||
|
||
const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [ | ||
rustc_span::sym::MutexGuard, | ||
rustc_span::sym::RwLockReadGuard, | ||
rustc_span::sym::RwLockWriteGuard, | ||
]; | ||
|
||
impl<'tcx> LateLintPass<'tcx> for LetUnderscore { | ||
fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) { | ||
if !matches!(local.pat.kind, hir::PatKind::Wild) { | ||
return; | ||
} | ||
if let Some(init) = local.init { | ||
let init_ty = cx.typeck_results().expr_ty(init); | ||
// If the type has a trivial Drop implementation, then it doesn't | ||
// matter that we drop the value immediately. | ||
if !init_ty.needs_drop(cx.tcx, cx.param_env) { | ||
return; | ||
} | ||
let is_sync_lock = match init_ty.kind() { | ||
ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS | ||
.iter() | ||
.any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())), | ||
_ => false, | ||
}; | ||
|
||
if is_sync_lock { | ||
let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]); | ||
span.push_span_label( | ||
local.pat.span, | ||
"this lock is not assigned to a binding and is immediately dropped".to_string(), | ||
); | ||
span.push_span_label( | ||
init.span, | ||
"this binding will immediately drop the value assigned to it".to_string(), | ||
); | ||
cx.struct_span_lint(LET_UNDERSCORE_LOCK, span, |lint| { | ||
build_and_emit_lint( | ||
lint, | ||
local, | ||
init.span, | ||
"non-binding let on a synchronization lock", | ||
) | ||
}) | ||
} else { | ||
cx.struct_span_lint(LET_UNDERSCORE_DROP, local.span, |lint| { | ||
build_and_emit_lint( | ||
lint, | ||
local, | ||
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 to avoid immediately dropping the value", | ||
"_unused", | ||
Applicability::MachineApplicable, | ||
) | ||
.multipart_suggestion( | ||
"consider immediately dropping the value", | ||
vec![ | ||
(local.span.until(init_span), "drop(".to_string()), | ||
(init_span.shrink_to_hi(), ")".to_string()), | ||
], | ||
Applicability::MachineApplicable, | ||
) | ||
.emit(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// check-pass | ||
#![warn(let_underscore_drop)] | ||
|
||
struct NontrivialDrop; | ||
|
||
impl Drop for NontrivialDrop { | ||
fn drop(&mut self) { | ||
println!("Dropping!"); | ||
} | ||
} | ||
|
||
fn main() { | ||
let _ = NontrivialDrop; //~WARNING non-binding let on a type that implements `Drop` | ||
} |
22 changes: 22 additions & 0 deletions
22
src/test/ui/lint/let_underscore/let_underscore_drop.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
warning: non-binding let on a type that implements `Drop` | ||
--> $DIR/let_underscore_drop.rs:13:5 | ||
| | ||
LL | let _ = NontrivialDrop; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/let_underscore_drop.rs:2:9 | ||
| | ||
LL | #![warn(let_underscore_drop)] | ||
| ^^^^^^^^^^^^^^^^^^^ | ||
help: consider binding to an unused variable to avoid immediately dropping the value | ||
| | ||
LL | let _unused = NontrivialDrop; | ||
| ~~~~~~~ | ||
help: consider immediately dropping the value | ||
| | ||
LL | drop(NontrivialDrop); | ||
| ~~~~~ + | ||
|
||
warning: 1 warning emitted | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// check-fail | ||
use std::sync::{Arc, Mutex}; | ||
|
||
fn main() { | ||
let data = Arc::new(Mutex::new(0)); | ||
let _ = data.lock().unwrap(); //~ERROR non-binding let on a synchronization lock | ||
} |
20 changes: 20 additions & 0 deletions
20
src/test/ui/lint/let_underscore/let_underscore_lock.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
error: non-binding let on a synchronization lock | ||
--> $DIR/let_underscore_lock.rs:6:9 | ||
| | ||
LL | let _ = data.lock().unwrap(); | ||
| ^ ^^^^^^^^^^^^^^^^^^^^ this binding will immediately drop the value assigned to it | ||
| | | ||
| this lock is not assigned to a binding and is immediately dropped | ||
| | ||
= note: `#[deny(let_underscore_lock)]` on by default | ||
help: consider binding to an unused variable to avoid immediately dropping the value | ||
| | ||
LL | let _unused = data.lock().unwrap(); | ||
| ~~~~~~~ | ||
help: consider immediately dropping the value | ||
| | ||
LL | drop(data.lock().unwrap()); | ||
| ~~~~~ + | ||
|
||
error: aborting due to previous error | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters