Merge SignificantDropInScrutinee into Matches lint pass

This commit is contained in:
Jason Newcomb 2022-06-03 13:35:10 -04:00
parent 8c8a52eeeb
commit dbc7753fb2
6 changed files with 109 additions and 114 deletions

View File

@ -151,6 +151,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(matches::MATCH_STR_CASE_MISMATCH), LintId::of(matches::MATCH_STR_CASE_MISMATCH),
LintId::of(matches::NEEDLESS_MATCH), LintId::of(matches::NEEDLESS_MATCH),
LintId::of(matches::REDUNDANT_PATTERN_MATCHING), LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
LintId::of(matches::SINGLE_MATCH), LintId::of(matches::SINGLE_MATCH),
LintId::of(matches::WILDCARD_IN_OR_PATTERNS), LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
@ -282,7 +283,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(self_assignment::SELF_ASSIGNMENT), LintId::of(self_assignment::SELF_ASSIGNMENT),
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
LintId::of(serde_api::SERDE_API_MISUSE), LintId::of(serde_api::SERDE_API_MISUSE),
LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE),
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),

View File

@ -276,6 +276,7 @@ store.register_lints(&[
matches::NEEDLESS_MATCH, matches::NEEDLESS_MATCH,
matches::REDUNDANT_PATTERN_MATCHING, matches::REDUNDANT_PATTERN_MATCHING,
matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
matches::SIGNIFICANT_DROP_IN_SCRUTINEE,
matches::SINGLE_MATCH, matches::SINGLE_MATCH,
matches::SINGLE_MATCH_ELSE, matches::SINGLE_MATCH_ELSE,
matches::WILDCARD_ENUM_MATCH_ARM, matches::WILDCARD_ENUM_MATCH_ARM,
@ -479,7 +480,6 @@ store.register_lints(&[
shadow::SHADOW_REUSE, shadow::SHADOW_REUSE,
shadow::SHADOW_SAME, shadow::SHADOW_SAME,
shadow::SHADOW_UNRELATED, shadow::SHADOW_UNRELATED,
significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE,
single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,

View File

@ -24,12 +24,12 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
LintId::of(loops::EMPTY_LOOP), LintId::of(loops::EMPTY_LOOP),
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
LintId::of(loops::MUT_RANGE_BOUND), LintId::of(loops::MUT_RANGE_BOUND),
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::NO_EFFECT_REPLACE),
LintId::of(methods::SUSPICIOUS_MAP), LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(octal_escapes::OCTAL_ESCAPES), LintId::of(octal_escapes::OCTAL_ESCAPES),
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE),
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),

View File

@ -367,7 +367,6 @@ mod self_named_constructors;
mod semicolon_if_nothing_returned; mod semicolon_if_nothing_returned;
mod serde_api; mod serde_api;
mod shadow; mod shadow;
mod significant_drop_in_scrutinee;
mod single_char_lifetime_names; mod single_char_lifetime_names;
mod single_component_path_imports; mod single_component_path_imports;
mod size_of_in_element_count; mod size_of_in_element_count;
@ -886,7 +885,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes)); store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion)); store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
store.register_late_pass(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee));
let allow_dbg_in_tests = conf.allow_dbg_in_tests; let allow_dbg_in_tests = conf.allow_dbg_in_tests;
store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
let cargo_ignore_publish = conf.cargo_ignore_publish; let cargo_ignore_publish = conf.cargo_ignore_publish;

View File

@ -25,6 +25,7 @@ mod needless_match;
mod overlapping_arms; mod overlapping_arms;
mod redundant_pattern_match; mod redundant_pattern_match;
mod rest_pat_in_fully_bound_struct; mod rest_pat_in_fully_bound_struct;
mod significant_drop_in_scrutinee;
mod single_match; mod single_match;
mod wild_in_or_pats; mod wild_in_or_pats;
@ -748,6 +749,82 @@ declare_clippy_lint! {
"creation of a case altering match expression with non-compliant arms" "creation of a case altering match expression with non-compliant arms"
} }
declare_clippy_lint! {
/// ### What it does
/// Check for temporaries returned from function calls in a match scrutinee that have the
/// `clippy::has_significant_drop` attribute.
///
/// ### Why is this bad?
/// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have
/// an important side-effect, such as unlocking a mutex, making it important for users to be
/// able to accurately understand their lifetimes. When a temporary is returned in a function
/// call in a match scrutinee, its lifetime lasts until the end of the match block, which may
/// be surprising.
///
/// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a
/// function call that returns a `MutexGuard` and then tries to lock again in one of the match
/// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of
/// the match block and thus will not unlock.
///
/// ### Example
/// ```rust.ignore
/// # use std::sync::Mutex;
///
/// # struct State {}
///
/// # impl State {
/// # fn foo(&self) -> bool {
/// # true
/// # }
///
/// # fn bar(&self) {}
/// # }
///
///
/// let mutex = Mutex::new(State {});
///
/// match mutex.lock().unwrap().foo() {
/// true => {
/// mutex.lock().unwrap().bar(); // Deadlock!
/// }
/// false => {}
/// };
///
/// println!("All done!");
///
/// ```
/// Use instead:
/// ```rust
/// # use std::sync::Mutex;
///
/// # struct State {}
///
/// # impl State {
/// # fn foo(&self) -> bool {
/// # true
/// # }
///
/// # fn bar(&self) {}
/// # }
///
/// let mutex = Mutex::new(State {});
///
/// let is_foo = mutex.lock().unwrap().foo();
/// match is_foo {
/// true => {
/// mutex.lock().unwrap().bar();
/// }
/// false => {}
/// };
///
/// println!("All done!");
/// ```
#[clippy::version = "1.60.0"]
pub SIGNIFICANT_DROP_IN_SCRUTINEE,
suspicious,
"warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime"
}
#[derive(Default)] #[derive(Default)]
pub struct Matches { pub struct Matches {
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
@ -786,6 +863,7 @@ impl_lint_pass!(Matches => [
MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR,
MATCH_ON_VEC_ITEMS, MATCH_ON_VEC_ITEMS,
MATCH_STR_CASE_MISMATCH, MATCH_STR_CASE_MISMATCH,
SIGNIFICANT_DROP_IN_SCRUTINEE,
]); ]);
impl<'tcx> LateLintPass<'tcx> for Matches { impl<'tcx> LateLintPass<'tcx> for Matches {
@ -796,9 +874,12 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
let from_expansion = expr.span.from_expansion(); let from_expansion = expr.span.from_expansion();
if let ExprKind::Match(ex, arms, source) = expr.kind { if let ExprKind::Match(ex, arms, source) = expr.kind {
if !span_starts_with(cx, expr.span, "match") { if source == MatchSource::Normal && !span_starts_with(cx, expr.span, "match") {
return; return;
} }
if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
significant_drop_in_scrutinee::check(cx, expr, ex, source);
}
collapsible_match::check_match(cx, arms); collapsible_match::check_match(cx, arms);
if !from_expansion { if !from_expansion {

View File

@ -5,98 +5,24 @@ use clippy_utils::source::{indent_of, snippet};
use rustc_errors::{Applicability, Diagnostic}; use rustc_errors::{Applicability, Diagnostic};
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{Expr, ExprKind, MatchSource}; use rustc_hir::{Expr, ExprKind, MatchSource};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{Ty, TypeAndMut}; use rustc_middle::ty::{Ty, TypeAndMut};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span; use rustc_span::Span;
declare_clippy_lint! { use super::SIGNIFICANT_DROP_IN_SCRUTINEE;
/// ### What it does
/// Check for temporaries returned from function calls in a match scrutinee that have the
/// `clippy::has_significant_drop` attribute.
///
/// ### Why is this bad?
/// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have
/// an important side-effect, such as unlocking a mutex, making it important for users to be
/// able to accurately understand their lifetimes. When a temporary is returned in a function
/// call in a match scrutinee, its lifetime lasts until the end of the match block, which may
/// be surprising.
///
/// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a
/// function call that returns a `MutexGuard` and then tries to lock again in one of the match
/// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of
/// the match block and thus will not unlock.
///
/// ### Example
/// ```rust.ignore
/// # use std::sync::Mutex;
///
/// # struct State {}
///
/// # impl State {
/// # fn foo(&self) -> bool {
/// # true
/// # }
///
/// # fn bar(&self) {}
/// # }
///
///
/// let mutex = Mutex::new(State {});
///
/// match mutex.lock().unwrap().foo() {
/// true => {
/// mutex.lock().unwrap().bar(); // Deadlock!
/// }
/// false => {}
/// };
///
/// println!("All done!");
///
/// ```
/// Use instead:
/// ```rust
/// # use std::sync::Mutex;
///
/// # struct State {}
///
/// # impl State {
/// # fn foo(&self) -> bool {
/// # true
/// # }
///
/// # fn bar(&self) {}
/// # }
///
/// let mutex = Mutex::new(State {});
///
/// let is_foo = mutex.lock().unwrap().foo();
/// match is_foo {
/// true => {
/// mutex.lock().unwrap().bar();
/// }
/// false => {}
/// };
///
/// println!("All done!");
/// ```
#[clippy::version = "1.60.0"]
pub SIGNIFICANT_DROP_IN_SCRUTINEE,
suspicious,
"warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime"
}
declare_lint_pass!(SignificantDropInScrutinee => [SIGNIFICANT_DROP_IN_SCRUTINEE]); pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee { expr: &'tcx Expr<'tcx>,
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { scrutinee: &'tcx Expr<'_>,
if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, expr) { source: MatchSource,
for found in suggestions { ) {
span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| { if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) {
set_diagnostic(diag, cx, expr, found); for found in suggestions {
}); span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
} set_diagnostic(diag, cx, expr, found);
});
} }
} }
} }
@ -148,28 +74,18 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t
/// may have a surprising lifetime. /// may have a surprising lifetime.
fn has_significant_drop_in_scrutinee<'tcx, 'a>( fn has_significant_drop_in_scrutinee<'tcx, 'a>(
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
expr: &'tcx Expr<'tcx>, scrutinee: &'tcx Expr<'tcx>,
source: MatchSource,
) -> Option<(Vec<FoundSigDrop>, &'static str)> { ) -> Option<(Vec<FoundSigDrop>, &'static str)> {
match expr.kind { let mut helper = SigDropHelper::new(cx);
ExprKind::Match(match_expr, _, source) => { helper.find_sig_drop(scrutinee).map(|drops| {
match source { let message = if source == MatchSource::Normal {
MatchSource::Normal | MatchSource::ForLoopDesugar => { "temporary with significant drop in match scrutinee"
let mut helper = SigDropHelper::new(cx); } else {
helper.find_sig_drop(match_expr).map(|drops| { "temporary with significant drop in for loop"
let message = if source == MatchSource::Normal { };
"temporary with significant drop in match scrutinee" (drops, message)
} else { })
"temporary with significant drop in for loop"
};
(drops, message)
})
},
// MatchSource of TryDesugar or AwaitDesugar is out of scope for this lint
MatchSource::TryDesugar | MatchSource::AwaitDesugar => None,
}
},
_ => None,
}
} }
struct SigDropHelper<'a, 'tcx> { struct SigDropHelper<'a, 'tcx> {