Skip to content

Commit e33d87d

Browse files
committed
When setting suggestion, 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.
1 parent 3cc50a4 commit e33d87d

File tree

4 files changed

+134
-75
lines changed

4 files changed

+134
-75
lines changed

clippy_lints/src/significant_drop_in_scrutinee.rs

Lines changed: 57 additions & 50 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};
@@ -154,9 +154,17 @@ fn has_significant_drop_in_scrutinee<'tcx, 'a>(
154154
cx: &'a LateContext<'tcx>,
155155
expr: &'tcx Expr<'tcx>,
156156
) -> Option<Vec<FoundSigDrop>> {
157-
let mut helper = SigDropHelper::new(cx);
158157
match expr.kind {
159-
ExprKind::Match(match_expr, _, _) => helper.find_sig_drop(match_expr),
158+
ExprKind::Match(match_expr, _, source) => {
159+
match source {
160+
MatchSource::Normal | MatchSource::ForLoopDesugar => {
161+
let mut helper = SigDropHelper::new(cx);
162+
helper.find_sig_drop(match_expr)
163+
},
164+
// MatchSource of TryDesugar or AwaitDesugar is out of scope for this lint
165+
MatchSource::TryDesugar | MatchSource::AwaitDesugar => None,
166+
}
167+
},
160168
_ => None,
161169
}
162170
}
@@ -213,6 +221,19 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
213221
self.sig_drop_spans.take()
214222
}
215223

224+
fn replace_current_sig_drop(
225+
&mut self,
226+
found_span: Span,
227+
is_unit_return_val: bool,
228+
lint_suggestion: LintSuggestion,
229+
) {
230+
self.current_sig_drop.replace(FoundSigDrop {
231+
found_span,
232+
is_unit_return_val,
233+
lint_suggestion,
234+
});
235+
}
236+
216237
/// This will try to set the current suggestion (so it can be moved into the suggestions vec
217238
/// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us
218239
/// an opportunity to look for another type in the chain that will be trivially copyable.
@@ -229,25 +250,15 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
229250
// but let's avoid any chance of an ICE
230251
if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) {
231252
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-
});
253+
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy);
237254
} 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-
});
255+
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
243256
}
244257
}
245258
} 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-
});
259+
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveOnly);
260+
} else if allow_move_and_clone {
261+
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
251262
}
252263
}
253264

@@ -279,11 +290,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
279290
// If either side had a significant drop, suggest moving the entire scrutinee to avoid
280291
// unnecessary copies and to simplify cases where both sides have significant drops.
281292
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-
});
293+
self.replace_current_sig_drop(span, is_unit_return_val, LintSuggestion::MoveOnly);
287294
}
288295

289296
self.special_handling_for_binary_op = false;
@@ -363,34 +370,34 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
363370
}
364371
}
365372
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),
373+
ExprKind::Array(..) |
374+
ExprKind::Call(..) |
375+
ExprKind::Unary(..) |
376+
ExprKind::If(..) |
377+
ExprKind::Match(..) |
378+
ExprKind::Field(..) |
379+
ExprKind::Index(..) |
380+
ExprKind::Ret(..) |
381+
ExprKind::Repeat(..) |
382+
ExprKind::Yield(..) |
383+
ExprKind::MethodCall(..) => walk_expr(self, ex),
377384
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(_, _) => {
385+
ExprKind::Block(_, _) |
386+
ExprKind::Break(_, _) |
387+
ExprKind::Cast(_, _) |
388+
// Don't want to check the closure itself, only invocation, which is covered by MethodCall
389+
ExprKind::Closure(_, _, _, _, _) |
390+
ExprKind::ConstBlock(_) |
391+
ExprKind::Continue(_) |
392+
ExprKind::DropTemps(_) |
393+
ExprKind::Err |
394+
ExprKind::InlineAsm(_) |
395+
ExprKind::Let(_) |
396+
ExprKind::Lit(_) |
397+
ExprKind::Loop(_, _, _, _) |
398+
ExprKind::Path(_) |
399+
ExprKind::Struct(_, _, _) |
400+
ExprKind::Type(_, _) => {
394401
return;
395402
}
396403
}

clippy_utils/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2073,7 +2073,8 @@ static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbo
20732073
fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
20742074
let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
20752075
let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
2076-
match map.entry(module) {
2076+
let value = map.entry(module);
2077+
match value {
20772078
Entry::Occupied(entry) => f(entry.get()),
20782079
Entry::Vacant(entry) => {
20792080
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)