]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
Rollup merge of #105174 - chenyukang:yukang/fix-105028-unused, r=eholk
[rust.git] / src / tools / clippy / tests / ui / significant_drop_in_scrutinee.rs
1 // FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
2 // // run-rustfix
3 #![warn(clippy::significant_drop_in_scrutinee)]
4 #![allow(dead_code, unused_assignments)]
5 #![allow(clippy::match_single_binding, clippy::single_match, clippy::uninlined_format_args)]
6
7 use std::num::ParseIntError;
8 use std::ops::Deref;
9 use std::sync::atomic::{AtomicU64, Ordering};
10 use std::sync::RwLock;
11 use std::sync::{Mutex, MutexGuard};
12
13 struct State {}
14
15 impl State {
16     fn foo(&self) -> bool {
17         true
18     }
19
20     fn bar(&self) {}
21 }
22
23 fn should_not_trigger_lint_with_mutex_guard_outside_match() {
24     let mutex = Mutex::new(State {});
25
26     // Should not trigger lint because the temporary should drop at the `;` on line before the match
27     let is_foo = mutex.lock().unwrap().foo();
28     match is_foo {
29         true => {
30             mutex.lock().unwrap().bar();
31         },
32         false => {},
33     };
34 }
35
36 fn should_not_trigger_lint_with_mutex_guard_when_taking_ownership_in_match() {
37     let mutex = Mutex::new(State {});
38
39     // Should not trigger lint because the scrutinee is explicitly returning the MutexGuard,
40     // so its lifetime should not be surprising.
41     match mutex.lock() {
42         Ok(guard) => {
43             guard.foo();
44             mutex.lock().unwrap().bar();
45         },
46         _ => {},
47     };
48 }
49
50 fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() {
51     let mutex = Mutex::new(State {});
52
53     // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it
54     // is preserved until the end of the match, but there is no clear indication that this is the
55     // case.
56     match mutex.lock().unwrap().foo() {
57         true => {
58             mutex.lock().unwrap().bar();
59         },
60         false => {},
61     };
62 }
63
64 fn should_not_trigger_lint_with_mutex_guard_in_match_scrutinee_when_lint_allowed() {
65     let mutex = Mutex::new(State {});
66
67     // Lint should not be triggered because it is "allowed" below.
68     #[allow(clippy::significant_drop_in_scrutinee)]
69     match mutex.lock().unwrap().foo() {
70         true => {
71             mutex.lock().unwrap().bar();
72         },
73         false => {},
74     };
75 }
76
77 fn should_not_trigger_lint_for_insignificant_drop() {
78     // Should not trigger lint because there are no temporaries whose drops have a significant
79     // side effect.
80     match 1u64.to_string().is_empty() {
81         true => {
82             println!("It was empty")
83         },
84         false => {
85             println!("It was not empty")
86         },
87     }
88 }
89
90 struct StateWithMutex {
91     m: Mutex<u64>,
92 }
93
94 struct MutexGuardWrapper<'a> {
95     mg: MutexGuard<'a, u64>,
96 }
97
98 impl<'a> MutexGuardWrapper<'a> {
99     fn get_the_value(&self) -> u64 {
100         *self.mg.deref()
101     }
102 }
103
104 struct MutexGuardWrapperWrapper<'a> {
105     mg: MutexGuardWrapper<'a>,
106 }
107
108 impl<'a> MutexGuardWrapperWrapper<'a> {
109     fn get_the_value(&self) -> u64 {
110         *self.mg.mg.deref()
111     }
112 }
113
114 impl StateWithMutex {
115     fn lock_m(&self) -> MutexGuardWrapper<'_> {
116         MutexGuardWrapper {
117             mg: self.m.lock().unwrap(),
118         }
119     }
120
121     fn lock_m_m(&self) -> MutexGuardWrapperWrapper<'_> {
122         MutexGuardWrapperWrapper {
123             mg: MutexGuardWrapper {
124                 mg: self.m.lock().unwrap(),
125             },
126         }
127     }
128
129     fn foo(&self) -> bool {
130         true
131     }
132
133     fn bar(&self) {}
134 }
135
136 fn should_trigger_lint_with_wrapped_mutex() {
137     let s = StateWithMutex { m: Mutex::new(1) };
138
139     // Should trigger lint because a temporary contains a type with a significant drop and its
140     // lifetime is not obvious. Additionally, it is not obvious from looking at the scrutinee that
141     // the temporary contains such a type, making it potentially even more surprising.
142     match s.lock_m().get_the_value() {
143         1 => {
144             println!("Got 1. Is it still 1?");
145             println!("{}", s.lock_m().get_the_value());
146         },
147         2 => {
148             println!("Got 2. Is it still 2?");
149             println!("{}", s.lock_m().get_the_value());
150         },
151         _ => {},
152     }
153     println!("All done!");
154 }
155
156 fn should_trigger_lint_with_double_wrapped_mutex() {
157     let s = StateWithMutex { m: Mutex::new(1) };
158
159     // Should trigger lint because a temporary contains a type which further contains a type with a
160     // significant drop and its lifetime is not obvious. Additionally, it is not obvious from
161     // looking at the scrutinee that the temporary contains such a type, making it potentially even
162     // more surprising.
163     match s.lock_m_m().get_the_value() {
164         1 => {
165             println!("Got 1. Is it still 1?");
166             println!("{}", s.lock_m().get_the_value());
167         },
168         2 => {
169             println!("Got 2. Is it still 2?");
170             println!("{}", s.lock_m().get_the_value());
171         },
172         _ => {},
173     }
174     println!("All done!");
175 }
176
177 struct Counter {
178     i: AtomicU64,
179 }
180
181 #[clippy::has_significant_drop]
182 struct CounterWrapper<'a> {
183     counter: &'a Counter,
184 }
185
186 impl<'a> CounterWrapper<'a> {
187     fn new(counter: &Counter) -> CounterWrapper {
188         counter.i.fetch_add(1, Ordering::Relaxed);
189         CounterWrapper { counter }
190     }
191 }
192
193 impl<'a> Drop for CounterWrapper<'a> {
194     fn drop(&mut self) {
195         self.counter.i.fetch_sub(1, Ordering::Relaxed);
196     }
197 }
198
199 impl Counter {
200     fn temp_increment(&self) -> Vec<CounterWrapper> {
201         vec![CounterWrapper::new(self), CounterWrapper::new(self)]
202     }
203 }
204
205 fn should_trigger_lint_for_vec() {
206     let counter = Counter { i: AtomicU64::new(0) };
207
208     // Should trigger lint because the temporary in the scrutinee returns a collection of types
209     // which have significant drops. The types with significant drops are also non-obvious when
210     // reading the expression in the scrutinee.
211     match counter.temp_increment().len() {
212         2 => {
213             let current_count = counter.i.load(Ordering::Relaxed);
214             println!("Current count {}", current_count);
215             assert_eq!(current_count, 0);
216         },
217         1 => {},
218         3 => {},
219         _ => {},
220     };
221 }
222
223 struct StateWithField {
224     s: String,
225 }
226
227 // Should trigger lint only on the type in the tuple which is created using a temporary
228 // with a significant drop. Additionally, this test ensures that the format of the tuple
229 // is preserved correctly in the suggestion.
230 fn should_trigger_lint_for_tuple_in_scrutinee() {
231     let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
232
233     {
234         match (mutex1.lock().unwrap().s.len(), true) {
235             (3, _) => {
236                 println!("started");
237                 mutex1.lock().unwrap().s.len();
238                 println!("done");
239             },
240             (_, _) => {},
241         };
242
243         match (true, mutex1.lock().unwrap().s.len(), true) {
244             (_, 3, _) => {
245                 println!("started");
246                 mutex1.lock().unwrap().s.len();
247                 println!("done");
248             },
249             (_, _, _) => {},
250         };
251
252         let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
253         match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
254             (3, _, 3) => {
255                 println!("started");
256                 mutex1.lock().unwrap().s.len();
257                 mutex2.lock().unwrap().s.len();
258                 println!("done");
259             },
260             (_, _, _) => {},
261         };
262
263         let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() });
264         match mutex3.lock().unwrap().s.as_str() {
265             "three" => {
266                 println!("started");
267                 mutex1.lock().unwrap().s.len();
268                 mutex2.lock().unwrap().s.len();
269                 println!("done");
270             },
271             _ => {},
272         };
273
274         match (true, mutex3.lock().unwrap().s.as_str()) {
275             (_, "three") => {
276                 println!("started");
277                 mutex1.lock().unwrap().s.len();
278                 mutex2.lock().unwrap().s.len();
279                 println!("done");
280             },
281             (_, _) => {},
282         };
283     }
284 }
285
286 // Should trigger lint when either side of a binary operation creates a temporary with a
287 // significant drop.
288 // To avoid potential unnecessary copies or creating references that would trigger the significant
289 // drop problem, the lint recommends moving the entire binary operation.
290 fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() {
291     let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
292
293     match mutex.lock().unwrap().s.len() > 1 {
294         true => {
295             mutex.lock().unwrap().s.len();
296         },
297         false => {},
298     };
299
300     match 1 < mutex.lock().unwrap().s.len() {
301         true => {
302             mutex.lock().unwrap().s.len();
303         },
304         false => {},
305     };
306 }
307
308 // Should trigger lint when both sides of a binary operation creates a temporary with a
309 // significant drop.
310 // To avoid potential unnecessary copies or creating references that would trigger the significant
311 // drop problem, the lint recommends moving the entire binary operation.
312 fn should_trigger_lint_for_accessing_fields_in_mutex_in_both_sides_of_binary_op() {
313     let mutex1 = Mutex::new(StateWithField { s: "state".to_owned() });
314     let mutex2 = Mutex::new(StateWithField {
315         s: "statewithfield".to_owned(),
316     });
317
318     match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
319         true => {
320             println!(
321                 "{} < {}",
322                 mutex1.lock().unwrap().s.len(),
323                 mutex2.lock().unwrap().s.len()
324             );
325         },
326         false => {},
327     };
328
329     match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
330         true => {
331             println!(
332                 "{} >= {}",
333                 mutex1.lock().unwrap().s.len(),
334                 mutex2.lock().unwrap().s.len()
335             );
336         },
337         false => {},
338     };
339 }
340
341 fn should_not_trigger_lint_for_closure_in_scrutinee() {
342     let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
343
344     let get_mutex_guard = || mutex1.lock().unwrap().s.len();
345
346     // Should not trigger lint because the temporary with a significant drop will be dropped
347     // at the end of the closure, so the MutexGuard will be unlocked and not have a potentially
348     // surprising lifetime.
349     match get_mutex_guard() > 1 {
350         true => {
351             mutex1.lock().unwrap().s.len();
352         },
353         false => {},
354     };
355 }
356
357 fn should_trigger_lint_for_return_from_closure_in_scrutinee() {
358     let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
359
360     let get_mutex_guard = || mutex1.lock().unwrap();
361
362     // Should trigger lint because the temporary with a significant drop is returned from the
363     // closure but not used directly in any match arms, so it has a potentially surprising lifetime.
364     match get_mutex_guard().s.len() > 1 {
365         true => {
366             mutex1.lock().unwrap().s.len();
367         },
368         false => {},
369     };
370 }
371
372 fn should_trigger_lint_for_return_from_match_in_scrutinee() {
373     let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
374     let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
375
376     let i = 100;
377
378     // Should trigger lint because the nested match within the scrutinee returns a temporary with a
379     // significant drop is but not used directly in any match arms, so it has a potentially
380     // surprising lifetime.
381     match match i {
382         100 => mutex1.lock().unwrap(),
383         _ => mutex2.lock().unwrap(),
384     }
385     .s
386     .len()
387         > 1
388     {
389         true => {
390             mutex1.lock().unwrap().s.len();
391         },
392         false => {
393             println!("nothing to do here");
394         },
395     };
396 }
397
398 fn should_trigger_lint_for_return_from_if_in_scrutinee() {
399     let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
400     let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
401
402     let i = 100;
403
404     // Should trigger lint because the nested if-expression within the scrutinee returns a temporary
405     // with a significant drop is but not used directly in any match arms, so it has a potentially
406     // surprising lifetime.
407     match if i > 1 {
408         mutex1.lock().unwrap()
409     } else {
410         mutex2.lock().unwrap()
411     }
412     .s
413     .len()
414         > 1
415     {
416         true => {
417             mutex1.lock().unwrap().s.len();
418         },
419         false => {},
420     };
421 }
422
423 fn should_not_trigger_lint_for_if_in_scrutinee() {
424     let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
425
426     let i = 100;
427
428     // Should not trigger the lint because the temporary with a significant drop *is* dropped within
429     // the body of the if-expression nested within the match scrutinee, and therefore does not have
430     // a potentially surprising lifetime.
431     match if i > 1 {
432         mutex.lock().unwrap().s.len() > 1
433     } else {
434         false
435     } {
436         true => {
437             mutex.lock().unwrap().s.len();
438         },
439         false => {},
440     };
441 }
442
443 struct StateWithBoxedMutexGuard {
444     u: Mutex<u64>,
445 }
446
447 impl StateWithBoxedMutexGuard {
448     fn new() -> StateWithBoxedMutexGuard {
449         StateWithBoxedMutexGuard { u: Mutex::new(42) }
450     }
451     fn lock(&self) -> Box<MutexGuard<u64>> {
452         Box::new(self.u.lock().unwrap())
453     }
454 }
455
456 fn should_trigger_lint_for_boxed_mutex_guard() {
457     let s = StateWithBoxedMutexGuard::new();
458
459     // Should trigger lint because a temporary Box holding a type with a significant drop in a match
460     // scrutinee may have a potentially surprising lifetime.
461     match s.lock().deref().deref() {
462         0 | 1 => println!("Value was less than 2"),
463         _ => println!("Value is {}", s.lock().deref()),
464     };
465 }
466
467 struct StateStringWithBoxedMutexGuard {
468     s: Mutex<String>,
469 }
470
471 impl StateStringWithBoxedMutexGuard {
472     fn new() -> StateStringWithBoxedMutexGuard {
473         StateStringWithBoxedMutexGuard {
474             s: Mutex::new("A String".to_owned()),
475         }
476     }
477     fn lock(&self) -> Box<MutexGuard<String>> {
478         Box::new(self.s.lock().unwrap())
479     }
480 }
481
482 fn should_trigger_lint_for_boxed_mutex_guard_holding_string() {
483     let s = StateStringWithBoxedMutexGuard::new();
484
485     let matcher = String::from("A String");
486
487     // Should trigger lint because a temporary Box holding a type with a significant drop in a match
488     // scrutinee may have a potentially surprising lifetime.
489     match s.lock().deref().deref() {
490         matcher => println!("Value is {}", s.lock().deref()),
491         _ => println!("Value was not a match"),
492     };
493 }
494
495 struct StateWithIntField {
496     i: u64,
497 }
498
499 // Should trigger lint when either side of an assign expression contains a temporary with a
500 // significant drop, because the temporary's lifetime will be extended to the end of the match.
501 // To avoid potential unnecessary copies or creating references that would trigger the significant
502 // drop problem, the lint recommends moving the entire binary operation.
503 fn should_trigger_lint_in_assign_expr() {
504     let mutex = Mutex::new(StateWithIntField { i: 10 });
505
506     let mut i = 100;
507
508     match mutex.lock().unwrap().i = i {
509         _ => {
510             println!("{}", mutex.lock().unwrap().i);
511         },
512     };
513
514     match i = mutex.lock().unwrap().i {
515         _ => {
516             println!("{}", mutex.lock().unwrap().i);
517         },
518     };
519
520     match mutex.lock().unwrap().i += 1 {
521         _ => {
522             println!("{}", mutex.lock().unwrap().i);
523         },
524     };
525
526     match i += mutex.lock().unwrap().i {
527         _ => {
528             println!("{}", mutex.lock().unwrap().i);
529         },
530     };
531 }
532
533 #[derive(Debug)]
534 enum RecursiveEnum {
535     Foo(Option<Box<RecursiveEnum>>),
536 }
537
538 #[derive(Debug)]
539 enum GenericRecursiveEnum<T> {
540     Foo(T, Option<Box<GenericRecursiveEnum<T>>>),
541 }
542
543 fn should_not_cause_stack_overflow() {
544     // Test that when a type recursively contains itself, a stack overflow does not occur when
545     // checking sub-types for significant drops.
546     let f = RecursiveEnum::Foo(Some(Box::new(RecursiveEnum::Foo(None))));
547     match f {
548         RecursiveEnum::Foo(Some(f)) => {
549             println!("{:?}", f)
550         },
551         RecursiveEnum::Foo(f) => {
552             println!("{:?}", f)
553         },
554     }
555
556     let f = GenericRecursiveEnum::Foo(1u64, Some(Box::new(GenericRecursiveEnum::Foo(2u64, None))));
557     match f {
558         GenericRecursiveEnum::Foo(i, Some(f)) => {
559             println!("{} {:?}", i, f)
560         },
561         GenericRecursiveEnum::Foo(i, f) => {
562             println!("{} {:?}", i, f)
563         },
564     }
565 }
566
567 fn should_not_produce_lint_for_try_desugar() -> Result<u64, ParseIntError> {
568     // TryDesugar (i.e. using `?` for a Result type) will turn into a match but is out of scope
569     // for this lint
570     let rwlock = RwLock::new("1".to_string());
571     let result = rwlock.read().unwrap().parse::<u64>()?;
572     println!("{}", result);
573     rwlock.write().unwrap().push('2');
574     Ok(result)
575 }
576
577 struct ResultReturner {
578     s: String,
579 }
580
581 impl ResultReturner {
582     fn to_number(&self) -> Result<i64, ParseIntError> {
583         self.s.parse::<i64>()
584     }
585 }
586
587 fn should_trigger_lint_for_non_ref_move_and_clone_suggestion() {
588     let rwlock = RwLock::<ResultReturner>::new(ResultReturner { s: "1".to_string() });
589     match rwlock.read().unwrap().to_number() {
590         Ok(n) => println!("Converted to number: {}", n),
591         Err(e) => println!("Could not convert {} to number", e),
592     };
593 }
594
595 fn should_trigger_lint_for_read_write_lock_for_loop() {
596     // For-in loops desugar to match expressions and are prone to the type of deadlock this lint is
597     // designed to look for.
598     let rwlock = RwLock::<Vec<String>>::new(vec!["1".to_string()]);
599     for s in rwlock.read().unwrap().iter() {
600         println!("{}", s);
601     }
602 }
603
604 fn do_bar(mutex: &Mutex<State>) {
605     mutex.lock().unwrap().bar();
606 }
607
608 fn should_trigger_lint_without_significant_drop_in_arm() {
609     let mutex = Mutex::new(State {});
610
611     // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it
612     // is preserved until the end of the match, but there is no clear indication that this is the
613     // case.
614     match mutex.lock().unwrap().foo() {
615         true => do_bar(&mutex),
616         false => {},
617     };
618 }
619
620 fn should_not_trigger_on_significant_iterator_drop() {
621     let lines = std::io::stdin().lines();
622     for line in lines {
623         println!("foo: {}", line.unwrap());
624     }
625 }
626
627 fn main() {}