@@ -4,7 +4,7 @@ use clippy_utils::get_attr;
4
4
use clippy_utils::source::{indent_of, snippet};
5
5
use rustc_errors::{Applicability, Diagnostic};
6
6
use rustc_hir::intravisit::{walk_expr, Visitor};
7
- use rustc_hir::{Expr, ExprKind, MatchSource};
7
+ use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
8
8
use rustc_lint::{LateContext, LintContext};
9
9
use rustc_middle::ty::subst::GenericArgKind;
10
10
use rustc_middle::ty::{Ty, TypeAndMut};
@@ -16,12 +16,21 @@ pub(super) fn check<'tcx>(
16
16
cx: &LateContext<'tcx>,
17
17
expr: &'tcx Expr<'tcx>,
18
18
scrutinee: &'tcx Expr<'_>,
19
+ arms: &'tcx [Arm<'_>],
19
20
source: MatchSource,
20
21
) {
21
22
if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) {
22
23
for found in suggestions {
23
24
span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
24
25
set_diagnostic(diag, cx, expr, found);
26
+ let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None);
27
+ diag.span_label(s, "original temporary lives until here");
28
+ if let Some(spans) = has_significant_drop_in_arms(cx, arms) {
29
+ for span in spans {
30
+ diag.span_label(span, "significant drop in arm here");
31
+ }
32
+ diag.note("this might lead to deadlocks or other unexpected behavior");
33
+ }
25
34
});
26
35
}
27
36
}
@@ -80,22 +89,77 @@ fn has_significant_drop_in_scrutinee<'tcx, 'a>(
80
89
let mut helper = SigDropHelper::new(cx);
81
90
helper.find_sig_drop(scrutinee).map(|drops| {
82
91
let message = if source == MatchSource::Normal {
83
- "temporary with significant drop in match scrutinee"
92
+ "temporary with drop impl with side effects in match scrutinee lives to end of block "
84
93
} else {
85
- "temporary with significant drop in for loop"
94
+ "temporary with drop impl with side effects in for loop condition lives to end of block "
86
95
};
87
96
(drops, message)
88
97
})
89
98
}
90
99
100
+ struct SigDropChecker<'a, 'tcx> {
101
+ seen_types: FxHashSet<Ty<'tcx>>,
102
+ cx: &'a LateContext<'tcx>,
103
+ }
104
+
105
+ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
106
+ fn new(cx: &'a LateContext<'tcx>) -> SigDropChecker<'a, 'tcx> {
107
+ SigDropChecker {
108
+ seen_types: FxHashSet::default(),
109
+ cx,
110
+ }
111
+ }
112
+
113
+ fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
114
+ self.cx.typeck_results().expr_ty(ex)
115
+ }
116
+
117
+ fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool {
118
+ !self.seen_types.insert(ty)
119
+ }
120
+
121
+ fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
122
+ if let Some(adt) = ty.ty_adt_def() {
123
+ if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 {
124
+ return true;
125
+ }
126
+ }
127
+
128
+ match ty.kind() {
129
+ rustc_middle::ty::Adt(a, b) => {
130
+ for f in a.all_fields() {
131
+ let ty = f.ty(cx.tcx, b);
132
+ if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) {
133
+ return true;
134
+ }
135
+ }
136
+
137
+ for generic_arg in b.iter() {
138
+ if let GenericArgKind::Type(ty) = generic_arg.unpack() {
139
+ if self.has_sig_drop_attr(cx, ty) {
140
+ return true;
141
+ }
142
+ }
143
+ }
144
+ false
145
+ },
146
+ rustc_middle::ty::Array(ty, _)
147
+ | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. })
148
+ | rustc_middle::ty::Ref(_, ty, _)
149
+ | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
150
+ _ => false,
151
+ }
152
+ }
153
+ }
154
+
91
155
struct SigDropHelper<'a, 'tcx> {
92
156
cx: &'a LateContext<'tcx>,
93
157
is_chain_end: bool,
94
- seen_types: FxHashSet<Ty<'tcx>>,
95
158
has_significant_drop: bool,
96
159
current_sig_drop: Option<FoundSigDrop>,
97
160
sig_drop_spans: Option<Vec<FoundSigDrop>>,
98
161
special_handling_for_binary_op: bool,
162
+ sig_drop_checker: SigDropChecker<'a, 'tcx>,
99
163
}
100
164
101
165
#[expect(clippy::enum_variant_names)]
@@ -118,11 +182,11 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
118
182
SigDropHelper {
119
183
cx,
120
184
is_chain_end: true,
121
- seen_types: FxHashSet::default(),
122
185
has_significant_drop: false,
123
186
current_sig_drop: None,
124
187
sig_drop_spans: None,
125
188
special_handling_for_binary_op: false,
189
+ sig_drop_checker: SigDropChecker::new(cx),
126
190
}
127
191
}
128
192
@@ -163,7 +227,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
163
227
if self.current_sig_drop.is_some() {
164
228
return;
165
229
}
166
- let ty = self.get_type(expr);
230
+ let ty = self.sig_drop_checker. get_type(expr);
167
231
if ty.is_ref() {
168
232
// We checked that the type was ref, so builtin_deref will return Some TypeAndMut,
169
233
// but let's avoid any chance of an ICE
@@ -187,14 +251,6 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
187
251
}
188
252
}
189
253
190
- fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
191
- self.cx.typeck_results().expr_ty(ex)
192
- }
193
-
194
- fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool {
195
- !self.seen_types.insert(ty)
196
- }
197
-
198
254
fn visit_exprs_for_binary_ops(
199
255
&mut self,
200
256
left: &'tcx Expr<'_>,
@@ -214,44 +270,15 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
214
270
215
271
self.special_handling_for_binary_op = false;
216
272
}
217
-
218
- fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
219
- if let Some(adt) = ty.ty_adt_def() {
220
- if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 {
221
- return true;
222
- }
223
- }
224
-
225
- match ty.kind() {
226
- rustc_middle::ty::Adt(a, b) => {
227
- for f in a.all_fields() {
228
- let ty = f.ty(cx.tcx, b);
229
- if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) {
230
- return true;
231
- }
232
- }
233
-
234
- for generic_arg in b.iter() {
235
- if let GenericArgKind::Type(ty) = generic_arg.unpack() {
236
- if self.has_sig_drop_attr(cx, ty) {
237
- return true;
238
- }
239
- }
240
- }
241
- false
242
- },
243
- rustc_middle::ty::Array(ty, _)
244
- | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. })
245
- | rustc_middle::ty::Ref(_, ty, _)
246
- | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
247
- _ => false,
248
- }
249
- }
250
273
}
251
274
252
275
impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
253
276
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
254
- if !self.is_chain_end && self.has_sig_drop_attr(self.cx, self.get_type(ex)) {
277
+ if !self.is_chain_end
278
+ && self
279
+ .sig_drop_checker
280
+ .has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex))
281
+ {
255
282
self.has_significant_drop = true;
256
283
return;
257
284
}
@@ -330,3 +357,40 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
330
357
}
331
358
}
332
359
}
360
+
361
+ struct ArmSigDropHelper<'a, 'tcx> {
362
+ sig_drop_checker: SigDropChecker<'a, 'tcx>,
363
+ found_sig_drop_spans: Option<FxHashSet<Span>>,
364
+ }
365
+
366
+ impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> {
367
+ fn new(cx: &'a LateContext<'tcx>) -> ArmSigDropHelper<'a, 'tcx> {
368
+ ArmSigDropHelper {
369
+ sig_drop_checker: SigDropChecker::new(cx),
370
+ found_sig_drop_spans: None,
371
+ }
372
+ }
373
+ }
374
+
375
+ fn has_significant_drop_in_arms<'tcx, 'a>(cx: &'a LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> Option<FxHashSet<Span>> {
376
+ let mut helper = ArmSigDropHelper::new(cx);
377
+ for arm in arms {
378
+ helper.visit_expr(arm.body);
379
+ }
380
+ helper.found_sig_drop_spans
381
+ }
382
+
383
+ impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> {
384
+ fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
385
+ if self
386
+ .sig_drop_checker
387
+ .has_sig_drop_attr(self.sig_drop_checker.cx, self.sig_drop_checker.get_type(ex))
388
+ {
389
+ self.found_sig_drop_spans
390
+ .get_or_insert_with(FxHashSet::default)
391
+ .insert(ex.span);
392
+ return;
393
+ }
394
+ walk_expr(self, ex);
395
+ }
396
+ }
0 commit comments