Skip to content

Commit 97e5449

Browse files
committed
Auto merge of #8902 - PrestonFrom:add_suggestion_for_move_and_clone_when_not_ref, r=flip1995
When setting suggestion for significant_drop_in_scrutinee, add suggestion for MoveAndClone for non-ref When trying to set the current suggestion, if the type of the expression is not a reference and it is not trivially pure clone copy, we should still trigger and emit a lint message. Since this fix may require cloning an expensive-to-clone type, do not attempt to offer a suggested fix. This change means that matches generated from TryDesugar and AwaitDesugar would normally trigger a lint, but they are out of scope for this lint, so we will explicitly ignore matches with sources of TryDesugar or AwaitDesugar. changelog: Update for ``[`significant_drop_in_scrutinee`]`` to correctly emit lint messages for cases where the type is not a reference *and* not trivially pure clone copy. changelog: [`significant_drop_in_scrutinee`]: No longer lint on Try `?` and `await` desugared expressions.
2 parents e32b66c + bc5a8e9 commit 97e5449

File tree

4 files changed

+146
-84
lines changed

4 files changed

+146
-84
lines changed

clippy_lints/src/significant_drop_in_scrutinee.rs

Lines changed: 69 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use clippy_utils::get_attr;
44
use clippy_utils::source::{indent_of, snippet};
55
use rustc_errors::{Applicability, Diagnostic};
66
use rustc_hir::intravisit::{walk_expr, Visitor};
7-
use rustc_hir::{Expr, ExprKind};
7+
use rustc_hir::{Expr, ExprKind, MatchSource};
88
use rustc_lint::{LateContext, LateLintPass, LintContext};
99
use rustc_middle::ty::subst::GenericArgKind;
1010
use rustc_middle::ty::{Ty, TypeAndMut};
@@ -91,15 +91,11 @@ declare_lint_pass!(SignificantDropInScrutinee => [SIGNIFICANT_DROP_IN_SCRUTINEE]
9191

9292
impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee {
9393
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
94-
if let Some(suggestions) = has_significant_drop_in_scrutinee(cx, expr) {
94+
if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, expr) {
9595
for found in suggestions {
96-
span_lint_and_then(
97-
cx,
98-
SIGNIFICANT_DROP_IN_SCRUTINEE,
99-
found.found_span,
100-
"temporary with significant drop in match scrutinee",
101-
|diag| set_diagnostic(diag, cx, expr, found),
102-
);
96+
span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
97+
set_diagnostic(diag, cx, expr, found);
98+
});
10399
}
104100
}
105101
}
@@ -153,10 +149,25 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t
153149
fn has_significant_drop_in_scrutinee<'tcx, 'a>(
154150
cx: &'a LateContext<'tcx>,
155151
expr: &'tcx Expr<'tcx>,
156-
) -> Option<Vec<FoundSigDrop>> {
157-
let mut helper = SigDropHelper::new(cx);
152+
) -> Option<(Vec<FoundSigDrop>, &'static str)> {
158153
match expr.kind {
159-
ExprKind::Match(match_expr, _, _) => helper.find_sig_drop(match_expr),
154+
ExprKind::Match(match_expr, _, source) => {
155+
match source {
156+
MatchSource::Normal | MatchSource::ForLoopDesugar => {
157+
let mut helper = SigDropHelper::new(cx);
158+
helper.find_sig_drop(match_expr).map(|drops| {
159+
let message = if source == MatchSource::Normal {
160+
"temporary with significant drop in match scrutinee"
161+
} else {
162+
"temporary with significant drop in for loop"
163+
};
164+
(drops, message)
165+
})
166+
},
167+
// MatchSource of TryDesugar or AwaitDesugar is out of scope for this lint
168+
MatchSource::TryDesugar | MatchSource::AwaitDesugar => None,
169+
}
170+
},
160171
_ => None,
161172
}
162173
}
@@ -213,6 +224,19 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
213224
self.sig_drop_spans.take()
214225
}
215226

227+
fn replace_current_sig_drop(
228+
&mut self,
229+
found_span: Span,
230+
is_unit_return_val: bool,
231+
lint_suggestion: LintSuggestion,
232+
) {
233+
self.current_sig_drop.replace(FoundSigDrop {
234+
found_span,
235+
is_unit_return_val,
236+
lint_suggestion,
237+
});
238+
}
239+
216240
/// This will try to set the current suggestion (so it can be moved into the suggestions vec
217241
/// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us
218242
/// an opportunity to look for another type in the chain that will be trivially copyable.
@@ -229,25 +253,15 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
229253
// but let's avoid any chance of an ICE
230254
if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) {
231255
if ty.is_trivially_pure_clone_copy() {
232-
self.current_sig_drop.replace(FoundSigDrop {
233-
found_span: expr.span,
234-
is_unit_return_val: false,
235-
lint_suggestion: LintSuggestion::MoveAndDerefToCopy,
236-
});
256+
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy);
237257
} else if allow_move_and_clone {
238-
self.current_sig_drop.replace(FoundSigDrop {
239-
found_span: expr.span,
240-
is_unit_return_val: false,
241-
lint_suggestion: LintSuggestion::MoveAndClone,
242-
});
258+
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
243259
}
244260
}
245261
} else if ty.is_trivially_pure_clone_copy() {
246-
self.current_sig_drop.replace(FoundSigDrop {
247-
found_span: expr.span,
248-
is_unit_return_val: false,
249-
lint_suggestion: LintSuggestion::MoveOnly,
250-
});
262+
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveOnly);
263+
} else if allow_move_and_clone {
264+
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
251265
}
252266
}
253267

@@ -279,11 +293,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
279293
// If either side had a significant drop, suggest moving the entire scrutinee to avoid
280294
// unnecessary copies and to simplify cases where both sides have significant drops.
281295
if self.has_significant_drop {
282-
self.current_sig_drop.replace(FoundSigDrop {
283-
found_span: span,
284-
is_unit_return_val,
285-
lint_suggestion: LintSuggestion::MoveOnly,
286-
});
296+
self.replace_current_sig_drop(span, is_unit_return_val, LintSuggestion::MoveOnly);
287297
}
288298

289299
self.special_handling_for_binary_op = false;
@@ -363,34 +373,34 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
363373
}
364374
}
365375
ExprKind::Box(..) |
366-
ExprKind::Array(..) |
367-
ExprKind::Call(..) |
368-
ExprKind::Unary(..) |
369-
ExprKind::If(..) |
370-
ExprKind::Match(..) |
371-
ExprKind::Field(..) |
372-
ExprKind::Index(..) |
373-
ExprKind::Ret(..) |
374-
ExprKind::Repeat(..) |
375-
ExprKind::Yield(..) |
376-
ExprKind::MethodCall(..) => walk_expr(self, ex),
376+
ExprKind::Array(..) |
377+
ExprKind::Call(..) |
378+
ExprKind::Unary(..) |
379+
ExprKind::If(..) |
380+
ExprKind::Match(..) |
381+
ExprKind::Field(..) |
382+
ExprKind::Index(..) |
383+
ExprKind::Ret(..) |
384+
ExprKind::Repeat(..) |
385+
ExprKind::Yield(..) |
386+
ExprKind::MethodCall(..) => walk_expr(self, ex),
377387
ExprKind::AddrOf(_, _, _) |
378-
ExprKind::Block(_, _) |
379-
ExprKind::Break(_, _) |
380-
ExprKind::Cast(_, _) |
381-
// Don't want to check the closure itself, only invocation, which is covered by MethodCall
382-
ExprKind::Closure(_, _, _, _, _) |
383-
ExprKind::ConstBlock(_) |
384-
ExprKind::Continue(_) |
385-
ExprKind::DropTemps(_) |
386-
ExprKind::Err |
387-
ExprKind::InlineAsm(_) |
388-
ExprKind::Let(_) |
389-
ExprKind::Lit(_) |
390-
ExprKind::Loop(_, _, _, _) |
391-
ExprKind::Path(_) |
392-
ExprKind::Struct(_, _, _) |
393-
ExprKind::Type(_, _) => {
388+
ExprKind::Block(_, _) |
389+
ExprKind::Break(_, _) |
390+
ExprKind::Cast(_, _) |
391+
// Don't want to check the closure itself, only invocation, which is covered by MethodCall
392+
ExprKind::Closure(_, _, _, _, _) |
393+
ExprKind::ConstBlock(_) |
394+
ExprKind::Continue(_) |
395+
ExprKind::DropTemps(_) |
396+
ExprKind::Err |
397+
ExprKind::InlineAsm(_) |
398+
ExprKind::Let(_) |
399+
ExprKind::Lit(_) |
400+
ExprKind::Loop(_, _, _, _) |
401+
ExprKind::Path(_) |
402+
ExprKind::Struct(_, _, _) |
403+
ExprKind::Type(_, _) => {
394404
return;
395405
}
396406
}

clippy_utils/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2083,7 +2083,8 @@ static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbo
20832083
fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
20842084
let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
20852085
let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
2086-
match map.entry(module) {
2086+
let value = map.entry(module);
2087+
match value {
20872088
Entry::Occupied(entry) => f(entry.get()),
20882089
Entry::Vacant(entry) => {
20892090
let mut names = Vec::new();

tests/ui/significant_drop_in_scrutinee.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
#![allow(unused_assignments)]
88
#![allow(dead_code)]
99

10+
use std::num::ParseIntError;
1011
use std::ops::Deref;
1112
use std::sync::atomic::{AtomicU64, Ordering};
13+
use std::sync::RwLock;
1214
use std::sync::{Mutex, MutexGuard};
1315

1416
struct State {}
@@ -552,4 +554,41 @@ fn should_not_cause_stack_overflow() {
552554
}
553555
}
554556

557+
fn should_not_produce_lint_for_try_desugar() -> Result<u64, ParseIntError> {
558+
// TryDesugar (i.e. using `?` for a Result type) will turn into a match but is out of scope
559+
// for this lint
560+
let rwlock = RwLock::new("1".to_string());
561+
let result = rwlock.read().unwrap().parse::<u64>()?;
562+
println!("{}", result);
563+
rwlock.write().unwrap().push('2');
564+
Ok(result)
565+
}
566+
567+
struct ResultReturner {
568+
s: String,
569+
}
570+
571+
impl ResultReturner {
572+
fn to_number(&self) -> Result<i64, ParseIntError> {
573+
self.s.parse::<i64>()
574+
}
575+
}
576+
577+
fn should_trigger_lint_for_non_ref_move_and_clone_suggestion() {
578+
let rwlock = RwLock::<ResultReturner>::new(ResultReturner { s: "1".to_string() });
579+
match rwlock.read().unwrap().to_number() {
580+
Ok(n) => println!("Converted to number: {}", n),
581+
Err(e) => println!("Could not convert {} to number", e),
582+
};
583+
}
584+
585+
fn should_trigger_lint_for_read_write_lock_for_loop() {
586+
// For-in loops desugar to match expressions and are prone to the type of deadlock this lint is
587+
// designed to look for.
588+
let rwlock = RwLock::<Vec<String>>::new(vec!["1".to_string()]);
589+
for s in rwlock.read().unwrap().iter() {
590+
println!("{}", s);
591+
}
592+
}
593+
555594
fn main() {}

0 commit comments

Comments
 (0)