]> git.lizzy.rs Git - rust.git/blob - tests/run-pass/panic/catch_panic.rs
288ae1965a69d2f1e4b560d73fbacd5d66fcc7fa
[rust.git] / tests / run-pass / panic / catch_panic.rs
1 // ignore-windows: Unwind panicking does not currently work on Windows
2 // normalize-stderr-test "[^ ]*libcore/[a-z/]+.rs[0-9:]*" -> "$$LOC"
3 #![feature(never_type)]
4 #![allow(unconditional_panic)]
5 use std::panic::{catch_unwind, AssertUnwindSafe};
6 use std::cell::Cell;
7
8 thread_local! {
9     static MY_COUNTER: Cell<usize> = Cell::new(0);
10     static DROPPED: Cell<bool> = Cell::new(false);
11     static HOOK_CALLED: Cell<bool> = Cell::new(false);
12 }
13
14 struct DropTester;
15 impl Drop for DropTester {
16     fn drop(&mut self) {
17         DROPPED.with(|c| {
18             c.set(true);
19         });
20     }
21 }
22
23 fn do_panic_counter(do_panic: impl FnOnce(usize) -> !) {
24     // If this gets leaked, it will be easy to spot
25     // in Miri's leak report
26     let _string = "LEAKED FROM do_panic_counter".to_string();
27
28     // When we panic, this should get dropped during unwinding
29     let _drop_tester = DropTester;
30
31     // Check for bugs in Miri's panic implementation.
32     // If do_panic_counter() somehow gets called more than once,
33     // we'll generate a different panic message and stderr will differ.
34     let old_val = MY_COUNTER.with(|c| {
35         let val = c.get();
36         c.set(val + 1);
37         val
38     });
39     do_panic(old_val);
40 }
41
42 fn main() {
43     let prev = std::panic::take_hook();
44     std::panic::set_hook(Box::new(move |panic_info| {
45         HOOK_CALLED.with(|h| h.set(true));
46         prev(panic_info)
47     }));
48
49     // Std panics
50     test(None, |_old_val| std::panic!("Hello from panic: std"));
51     test(None, |old_val| std::panic!(format!("Hello from panic: {:?}", old_val)));
52     test(None, |old_val| std::panic!("Hello from panic: {:?}", old_val));
53     test(None, |_old_val| std::panic!(1337));
54
55     // Core panics
56     test(None, |_old_val| core::panic!("Hello from panic: core"));
57     test(None, |old_val| core::panic!(&format!("Hello from panic: {:?}", old_val)));
58     test(None, |old_val| core::panic!("Hello from panic: {:?}", old_val));
59
60     // Built-in panics; also make sure the message is right.
61     test(
62         Some("index out of bounds: the len is 3 but the index is 4"),
63         |_old_val| { let _val = [0, 1, 2][4]; loop {} },
64     );
65     test(
66         Some("attempt to divide by zero"),
67         |_old_val| { let _val = 1/0; loop {} },
68     );
69
70     test(
71         Some("align_offset: align is not a power-of-two"),
72         |_old_val| { (0usize as *const u8).align_offset(3); loop {} },
73     );
74
75     // Assertion and debug assertion
76     test(None, |_old_val| { assert!(false); loop {} });
77     test(None, |_old_val| { debug_assert!(false); loop {} });
78     test(None, |_old_val| { unsafe { std::char::from_u32_unchecked(0xFD10000); } loop {} }); // trigger debug-assertion in libstd
79
80     eprintln!("Success!"); // Make sure we get this in stderr
81 }
82
83 fn test(expect_msg: Option<&str>, do_panic: impl FnOnce(usize) -> !) {
84     // Reset test flags.
85     DROPPED.with(|c| c.set(false));
86     HOOK_CALLED.with(|c| c.set(false));
87
88     // Cause and catch a panic.
89     let res = catch_unwind(AssertUnwindSafe(|| {
90         let _string = "LEAKED FROM CLOSURE".to_string();
91         do_panic_counter(do_panic)
92     })).expect_err("do_panic() did not panic!");
93
94     // See if we can extract the panic message.
95     let msg = if let Some(s) = res.downcast_ref::<String>() {
96         eprintln!("Caught panic message (String): {}", s);
97         Some(s.as_str())
98     } else if let Some(s) = res.downcast_ref::<&str>() {
99         eprintln!("Caught panic message (&str): {}", s);
100         Some(*s)
101     } else {
102         eprintln!("Failed get caught panic message.");
103         None
104     };
105     if let Some(expect_msg) = expect_msg {
106         assert_eq!(expect_msg, msg.unwrap());
107     }
108
109     // Test flags.
110     assert!(DROPPED.with(|c| c.get()));
111     assert!(HOOK_CALLED.with(|c| c.get()));
112 }