pub const PANIC_SYMBOL_PATTERNS: &[&str] = &[
"rust_panic$",
"panic_fmt$",
"panic_display",
"slice_index_fail",
"str_index_overflow",
];
pub const ABORT_SYMBOL_PATTERNS: &[&str] = &["std::process::abort"];
pub const LIBRARY_PANIC_PATTERNS: &[&str] = &[
"core::panicking::panic",
"core::panicking::panic_fmt",
"core::panicking::panic_display",
"core::panicking::panic_in_cleanup",
"core::panicking::panic_const",
"core::panicking::panic_bounds_check",
"core::panicking::panic_nounwind_fmt",
"core::panicking::panic_cannot_unwind",
"core::panicking::assert_failed",
"std::panicking::begin_panic",
"std::panicking::begin_panic_fmt",
"core::option::Option<T>::unwrap",
"core::option::Option<T>::expect",
"core::option::unwrap_failed",
"core::result::Result<T,E>::unwrap",
"core::result::Result<T,E>::expect",
"core::result::Result<T,E>::unwrap_err",
"core::result::Result<T,E>::expect_err",
"core::result::unwrap_failed",
];
pub fn is_dependency_path(file_path: &str) -> bool {
if file_path.contains(".cargo/registry/") || file_path.contains(".cargo\\registry\\") {
return true;
}
if file_path.contains("/rustc/")
|| file_path.contains("/.rustup/toolchains/")
|| file_path.contains("/rustlib/src/")
|| file_path.starts_with("/rust/deps/")
|| file_path.starts_with("library/")
{
return true;
}
if file_path.contains("/__/") || file_path.starts_with("__") || file_path.starts_with("src/__")
{
return true;
}
false
}
pub fn is_stdlib_function(name: &str) -> bool {
name.starts_with("core::")
|| name.starts_with("std::")
|| name.starts_with("alloc::")
|| name.starts_with("<core::")
|| name.starts_with("<std::")
|| name.starts_with("<alloc::")
|| name.contains(" core::")
|| name.contains(" std::")
|| name.contains(" alloc::")
|| name.contains("::core::")
|| name.contains("::std::")
|| name.contains("::alloc::")
}
pub const STDLIB_SOURCE_PREFIXES: &[&str] = &[
"/rustc/",
"/library/core/src/",
"/library/std/src/",
"/library/alloc/src/",
"library/core/src/",
"library/std/src/",
"library/alloc/src/",
"/src/libstd/",
"/src/libcore/",
"/src/liballoc/",
];
pub fn is_stdlib_source(file_path: &str) -> bool {
STDLIB_SOURCE_PREFIXES
.iter()
.any(|prefix| file_path.contains(prefix))
}
pub fn is_panic_triggering_function(func_name: &str) -> bool {
func_name.contains("unwrap_failed")
|| func_name.contains("expect_failed")
|| (func_name.contains("unwrap") && !func_name.contains("unwrap_or"))
|| (func_name.contains("expect") && func_name.contains("Option"))
|| (func_name.contains("expect") && func_name.contains("Result"))
|| func_name.contains("panic_fmt")
|| func_name.contains("panic_display")
|| func_name.contains("panic_bounds_check")
|| func_name.contains("panic_const_")
|| func_name.contains("panic_in_cleanup")
|| func_name.contains("panic_cannot_unwind")
|| func_name.contains("panic_nounwind")
|| func_name.contains("panic_misaligned_pointer")
|| func_name.contains("panic_invalid_enum")
|| func_name.contains("assert_failed")
|| func_name.contains("capacity_overflow")
|| func_name.contains("handle_alloc_error")
|| func_name.contains("slice_error_fail")
|| func_name.contains("str_index_overflow_fail")
|| func_name.starts_with("index<")
|| func_name.contains("::index<")
|| func_name.contains("Index::index")
|| func_name.contains(">::index")
}
pub fn is_library_dependency_path(file_path: &str) -> bool {
is_dependency_path(file_path) || file_path.starts_with("/rust/") || file_path.contains("/deps/")
}
use crate::panic_cause::PanicCause;
pub fn detect_panic_cause(func_name: &str, file_path: Option<&str>) -> Option<PanicCause> {
if func_name.contains("panic_in_cleanup") {
return Some(PanicCause::PanicInDrop);
}
if func_name.contains("panic_cannot_unwind") || func_name.contains("panic_nounwind") {
return Some(PanicCause::CannotUnwind);
}
if func_name.contains("panic_bounds_check") {
return Some(PanicCause::BoundsCheck);
}
if func_name.contains("panic_const_add_overflow") {
return Some(PanicCause::ArithmeticOverflow("addition".to_string()));
}
if func_name.contains("panic_const_sub_overflow") {
return Some(PanicCause::ArithmeticOverflow("subtraction".to_string()));
}
if func_name.contains("panic_const_mul_overflow") {
return Some(PanicCause::ArithmeticOverflow("multiplication".to_string()));
}
if func_name.contains("panic_const_div_overflow") {
return Some(PanicCause::ArithmeticOverflow("division".to_string()));
}
if func_name.contains("panic_const_rem_overflow") {
return Some(PanicCause::ArithmeticOverflow("remainder".to_string()));
}
if func_name.contains("panic_const_neg_overflow") {
return Some(PanicCause::ArithmeticOverflow("negation".to_string()));
}
if func_name.contains("panic_const_shl_overflow") {
return Some(PanicCause::ShiftOverflow("left".to_string()));
}
if func_name.contains("panic_const_shr_overflow") {
return Some(PanicCause::ShiftOverflow("right".to_string()));
}
if func_name.contains("panic_const_div_by_zero") {
return Some(PanicCause::DivisionByZero);
}
if func_name.contains("panic_const_rem_by_zero") {
return Some(PanicCause::DivisionByZero);
}
if func_name.contains("Result") && func_name.contains("expect") {
return Some(PanicCause::ExpectErr);
}
if func_name.contains("unwrap_failed") {
let is_result = file_path
.filter(|f| {
f.contains("result.rs")
|| f.contains("core/result")
|| f.contains("option.rs")
|| f.contains("core/option")
})
.map(|f| f.contains("result.rs") || f.contains("core/result"))
.unwrap_or_else(|| func_name.contains("result"));
if is_result {
return Some(PanicCause::UnwrapErr);
} else {
return Some(PanicCause::UnwrapNone);
}
}
if func_name.contains("expect_failed") {
return Some(PanicCause::ExpectNone);
}
if func_name.contains("assert_failed") {
return Some(PanicCause::AssertFailed);
}
if func_name.contains("panic_display") {
return Some(PanicCause::ExplicitPanic);
}
if func_name.contains("unreachable") && func_name.contains("panic") {
return Some(PanicCause::Unreachable);
}
if func_name.contains("core::fmt::") || func_name.contains("alloc::fmt::") {
return Some(PanicCause::FormattingError);
}
if func_name.contains("format_inner") || func_name.contains("write_fmt") {
return Some(PanicCause::FormattingError);
}
if func_name.contains("::fmt") && (func_name.contains("Display") || func_name.contains("Debug"))
{
return Some(PanicCause::FormattingError);
}
if func_name.contains("capacity_overflow") {
return Some(PanicCause::CapacityOverflow);
}
if func_name.contains("handle_alloc_error")
|| func_name.contains("alloc_error_handler")
|| func_name.contains("alloc_error_hook")
{
return Some(PanicCause::OutOfMemory);
}
if func_name.contains("raw_vec") && func_name.contains("grow") {
return Some(PanicCause::CapacityOverflow);
}
if func_name.contains("hashbrown") && func_name.contains("alloc_err") {
return Some(PanicCause::OutOfMemory);
}
if func_name.contains("slice_error_fail") {
return Some(PanicCause::StringSliceError);
}
if func_name.contains("str_index_overflow_fail") {
return Some(PanicCause::StringSliceError);
}
if func_name.contains("slice_start_index_overflow")
|| func_name.contains("slice_end_index_overflow")
{
return Some(PanicCause::StringSliceError);
}
if func_name.starts_with("index<")
|| func_name.contains("::index<")
|| func_name.contains("Index::index")
|| func_name.contains(">::index")
{
let is_map_op = func_name.contains("HashMap")
|| func_name.contains("BTreeMap")
|| func_name.contains("hash::map")
|| func_name.contains("btree::map");
if is_map_op {
return Some(PanicCause::KeyNotFound);
}
let is_string_op = func_name.contains("str::") || func_name.contains("core::str::");
let is_string_file = file_path
.map(|f| {
let normalized = f.replace('\\', "/");
normalized.contains("/library/core/src/str/")
|| normalized.contains("/library/std/src/str/")
|| normalized.contains("/src/libcore/str/")
})
.unwrap_or(false);
if is_string_op || is_string_file {
return Some(PanicCause::StringSliceError);
}
return Some(PanicCause::BoundsCheck);
}
if func_name.contains("panic_invalid_enum_construction") {
return Some(PanicCause::InvalidEnum);
}
if func_name.contains("panic_misaligned_pointer_dereference") {
return Some(PanicCause::MisalignedPointer);
}
if func_name.contains("hashbrown::raw::") {
return Some(PanicCause::CapacityOverflow);
}
if func_name.contains("std::collections::hash::") {
return Some(PanicCause::CapacityOverflow);
}
None
}
#[cfg(test)]
mod tests {
use super::*;
use crate::panic_cause::PanicCause;
#[test]
fn test_cargo_registry_unix() {
assert!(is_dependency_path(
"/home/user/.cargo/registry/src/crates.io/serde-1.0/src/lib.rs"
));
}
#[test]
fn test_cargo_registry_windows() {
assert!(is_dependency_path(
"C:\\Users\\user\\.cargo\\registry\\src\\crates.io\\serde-1.0\\src\\lib.rs"
));
}
#[test]
fn test_rustc_path() {
assert!(is_dependency_path(
"/rustc/abc123/library/core/src/option.rs"
));
}
#[test]
fn test_rustup_toolchain() {
assert!(is_dependency_path(
"/Users/user/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs"
));
}
#[test]
fn test_rustlib_src() {
assert!(is_dependency_path("/usr/lib/rustlib/src/rust/core.rs"));
}
#[test]
fn test_relative_library_path() {
assert!(is_dependency_path("library/core/src/option.rs"));
}
#[test]
fn test_generated_code_boundary() {
assert!(is_dependency_path("src/__generated/bindings.rs"));
assert!(is_dependency_path("__objc2/src/lib.rs"));
assert!(is_dependency_path("some/path/__/generated.rs"));
}
#[test]
fn test_user_code_not_dependency() {
assert!(!is_dependency_path("src/main.rs"));
assert!(!is_dependency_path("src/lib.rs"));
assert!(!is_dependency_path("/Users/user/project/src/module/mod.rs"));
assert!(!is_dependency_path("examples/demo/src/main.rs"));
}
#[test]
fn test_stdlib_direct_namespace() {
assert!(is_stdlib_function("core::option::Option::unwrap"));
assert!(is_stdlib_function("std::io::Read::read"));
assert!(is_stdlib_function("alloc::vec::Vec::push"));
}
#[test]
fn test_stdlib_generic_bounds() {
assert!(is_stdlib_function("<core::option::Option<T>>::unwrap"));
assert!(is_stdlib_function("<std::vec::Vec<T>>::push"));
}
#[test]
fn test_stdlib_trait_impl() {
assert!(is_stdlib_function("<MyStruct as core::fmt::Display>::fmt"));
}
#[test]
fn test_user_function_not_stdlib() {
assert!(!is_stdlib_function("my_crate::module::function"));
assert!(!is_stdlib_function("cause_an_unwrap"));
}
#[test]
fn test_unwrap_variants() {
assert!(is_panic_triggering_function("unwrap_failed"));
assert!(is_panic_triggering_function("expect_failed"));
assert!(is_panic_triggering_function(
"core::option::Option<i32>::unwrap"
));
assert!(!is_panic_triggering_function("unwrap_or_default"));
}
#[test]
fn test_panic_functions() {
assert!(is_panic_triggering_function("panic_fmt"));
assert!(is_panic_triggering_function("panic_bounds_check"));
assert!(is_panic_triggering_function("panic_const_add_overflow"));
assert!(is_panic_triggering_function("panic_misaligned_pointer"));
}
#[test]
fn test_user_function_not_triggering() {
assert!(!is_panic_triggering_function("my_function"));
assert!(!is_panic_triggering_function("process_data"));
}
#[test]
fn test_stdlib_source_paths() {
assert!(is_stdlib_source("/rustc/abc123/library/core/src/option.rs"));
assert!(is_stdlib_source("/library/core/src/panicking.rs"));
assert!(is_stdlib_source("/library/std/src/io/mod.rs"));
assert!(is_stdlib_source("library/core/src/panicking.rs"));
assert!(is_stdlib_source("library/std/src/io/mod.rs"));
assert!(is_stdlib_source("library/alloc/src/vec/mod.rs"));
assert!(!is_stdlib_source("src/main.rs"));
assert!(!is_stdlib_source(
"/Users/user/.cargo/registry/src/serde/lib.rs"
));
}
#[test]
fn test_library_dependency_paths() {
assert!(is_library_dependency_path("/rustc/abc/core.rs"));
assert!(is_library_dependency_path("library/core/src/option.rs"));
assert!(is_library_dependency_path(
"/home/user/.cargo/registry/serde.rs"
));
assert!(is_library_dependency_path(
"/home/user/.rustup/toolchains/stable/lib.rs"
));
assert!(is_library_dependency_path("/rust/deps/std/src/lib.rs"));
assert!(is_library_dependency_path("target/debug/deps/serde.rs"));
assert!(!is_library_dependency_path("src/main.rs"));
assert!(!is_library_dependency_path("src/arch/arm64/mod.rs"));
assert!(!is_library_dependency_path("src/raw/mod.rs"));
}
#[test]
fn test_panic_symbol_patterns_cover_key_entry_points() {
assert!(PANIC_SYMBOL_PATTERNS.iter().any(|p| p.contains("panic")));
assert!(PANIC_SYMBOL_PATTERNS.iter().any(|p| p.contains("slice")));
}
#[test]
fn test_library_panic_patterns_comprehensive() {
assert!(LIBRARY_PANIC_PATTERNS.iter().any(|p| p.contains("panic")));
assert!(LIBRARY_PANIC_PATTERNS.iter().any(|p| p.contains("unwrap")));
assert!(LIBRARY_PANIC_PATTERNS.iter().any(|p| p.contains("expect")));
assert!(LIBRARY_PANIC_PATTERNS.iter().any(|p| p.contains("option")));
assert!(LIBRARY_PANIC_PATTERNS.iter().any(|p| p.contains("result")));
}
#[test]
fn test_detect_panic_cause_bounds_check() {
assert_eq!(
detect_panic_cause("panic_bounds_check", None),
Some(PanicCause::BoundsCheck)
);
}
#[test]
fn test_detect_panic_cause_arithmetic_overflow() {
assert_eq!(
detect_panic_cause("panic_const_add_overflow", None),
Some(PanicCause::ArithmeticOverflow("addition".to_string()))
);
assert_eq!(
detect_panic_cause("panic_const_sub_overflow", None),
Some(PanicCause::ArithmeticOverflow("subtraction".to_string()))
);
assert_eq!(
detect_panic_cause("panic_const_mul_overflow", None),
Some(PanicCause::ArithmeticOverflow("multiplication".to_string()))
);
}
#[test]
fn test_detect_panic_cause_shift_overflow() {
assert_eq!(
detect_panic_cause("panic_const_shl_overflow", None),
Some(PanicCause::ShiftOverflow("left".to_string()))
);
assert_eq!(
detect_panic_cause("panic_const_shr_overflow", None),
Some(PanicCause::ShiftOverflow("right".to_string()))
);
}
#[test]
fn test_detect_panic_cause_division_by_zero() {
assert_eq!(
detect_panic_cause("panic_const_div_by_zero", None),
Some(PanicCause::DivisionByZero)
);
assert_eq!(
detect_panic_cause("panic_const_rem_by_zero", None),
Some(PanicCause::DivisionByZero)
);
}
#[test]
fn test_detect_panic_cause_unwrap_failed_option() {
assert_eq!(
detect_panic_cause("unwrap_failed", Some("option.rs")),
Some(PanicCause::UnwrapNone)
);
}
#[test]
fn test_detect_panic_cause_unwrap_failed_result() {
assert_eq!(
detect_panic_cause("unwrap_failed", Some("result.rs")),
Some(PanicCause::UnwrapErr)
);
assert_eq!(
detect_panic_cause("unwrap_failed", Some("core/result/mod.rs")),
Some(PanicCause::UnwrapErr)
);
}
#[test]
fn test_detect_panic_cause_expect_failed() {
assert_eq!(
detect_panic_cause("expect_failed", None),
Some(PanicCause::ExpectNone)
);
}
#[test]
fn test_detect_panic_cause_result_expect() {
assert_eq!(
detect_panic_cause("Result::expect", None),
Some(PanicCause::ExpectErr)
);
}
#[test]
fn test_detect_panic_cause_assert_failed() {
assert_eq!(
detect_panic_cause("assert_failed", None),
Some(PanicCause::AssertFailed)
);
assert_eq!(
detect_panic_cause("assert_failed", Some("src/main.rs")),
Some(PanicCause::AssertFailed)
);
}
#[test]
fn test_assert_failed_always_assert_regardless_of_path() {
assert_eq!(
detect_panic_cause(
"assert_failed",
Some("/rustc/abc123/library/std/src/time.rs")
),
Some(PanicCause::AssertFailed)
);
assert_eq!(
detect_panic_cause("assert_failed", Some("/library/core/src/num/mod.rs")),
Some(PanicCause::AssertFailed)
);
assert_eq!(
detect_panic_cause("assert_failed", Some("src/main.rs")),
Some(PanicCause::AssertFailed)
);
}
#[test]
fn test_assert_in_user_path_with_library() {
assert_eq!(
detect_panic_cause("assert_failed", Some("/home/me/library/app/src/main.rs")),
Some(PanicCause::AssertFailed)
);
assert_eq!(
detect_panic_cause("assert_failed", Some("/projects/library/core/lib.rs")),
Some(PanicCause::AssertFailed)
);
}
#[test]
fn test_detect_panic_cause_panic_display() {
assert_eq!(
detect_panic_cause("panic_display", None),
Some(PanicCause::ExplicitPanic)
);
}
#[test]
fn test_detect_panic_cause_panic_in_cleanup() {
assert_eq!(
detect_panic_cause("panic_in_cleanup", None),
Some(PanicCause::PanicInDrop)
);
}
#[test]
fn test_detect_panic_cause_panic_cannot_unwind() {
assert_eq!(
detect_panic_cause("panic_cannot_unwind", None),
Some(PanicCause::CannotUnwind)
);
assert_eq!(
detect_panic_cause("panic_nounwind", None),
Some(PanicCause::CannotUnwind)
);
}
#[test]
fn test_detect_panic_cause_formatting() {
assert_eq!(
detect_panic_cause("core::fmt::write", None),
Some(PanicCause::FormattingError)
);
assert_eq!(
detect_panic_cause("write_fmt", None),
Some(PanicCause::FormattingError)
);
}
#[test]
fn test_detect_panic_cause_capacity_overflow() {
assert_eq!(
detect_panic_cause("capacity_overflow", None),
Some(PanicCause::CapacityOverflow)
);
}
#[test]
fn test_detect_panic_cause_out_of_memory() {
assert_eq!(
detect_panic_cause("handle_alloc_error", None),
Some(PanicCause::OutOfMemory)
);
}
#[test]
fn test_detect_panic_cause_string_slice_error() {
assert_eq!(
detect_panic_cause("slice_error_fail", None),
Some(PanicCause::StringSliceError)
);
assert_eq!(
detect_panic_cause("str_index_overflow_fail", None),
Some(PanicCause::StringSliceError)
);
}
#[test]
fn test_detect_panic_cause_index_bounds() {
assert_eq!(
detect_panic_cause("index<T, usize>", None),
Some(PanicCause::BoundsCheck)
);
assert_eq!(
detect_panic_cause("Index::index", None),
Some(PanicCause::BoundsCheck)
);
}
#[test]
fn test_detect_panic_cause_index_string() {
assert_eq!(
detect_panic_cause("index<Range>", Some("/library/core/src/str/mod.rs")),
Some(PanicCause::StringSliceError)
);
assert_eq!(
detect_panic_cause("str::index<Range>", None),
Some(PanicCause::StringSliceError)
);
}
#[test]
fn test_detect_panic_cause_invalid_enum() {
assert_eq!(
detect_panic_cause("panic_invalid_enum_construction", None),
Some(PanicCause::InvalidEnum)
);
}
#[test]
fn test_detect_panic_cause_misaligned_pointer() {
assert_eq!(
detect_panic_cause("panic_misaligned_pointer_dereference", None),
Some(PanicCause::MisalignedPointer)
);
}
#[test]
fn test_detect_panic_cause_hashbrown_raw() {
assert_eq!(
detect_panic_cause("hashbrown::raw::TableLayout::calculate_layout_for", None),
Some(PanicCause::CapacityOverflow)
);
assert_eq!(
detect_panic_cause(
"hashbrown::raw::RawTableInner::fallible_with_capacity",
None
),
Some(PanicCause::CapacityOverflow)
);
assert_eq!(
detect_panic_cause("hashbrown::raw::RawTableInner::new_uninitialized", None),
Some(PanicCause::CapacityOverflow)
);
}
#[test]
fn test_detect_panic_cause_std_collections_hash() {
assert_eq!(
detect_panic_cause(
"std::collections::hash::map::HashMap<K,V>::new",
Some("/rustc/abc/library/std/src/collections/hash/map.rs")
),
Some(PanicCause::CapacityOverflow)
);
assert_eq!(
detect_panic_cause(
"std::collections::hash::set::HashSet<T>::with_capacity",
Some("/rustc/abc/library/std/src/collections/hash/set.rs")
),
Some(PanicCause::CapacityOverflow)
);
}
#[test]
fn test_detect_hashbrown_specific_causes_take_priority() {
assert_eq!(
detect_panic_cause("hashbrown::raw::Fallibility::capacity_overflow", None),
Some(PanicCause::CapacityOverflow)
);
assert_eq!(
detect_panic_cause("hashbrown::raw::Fallibility::alloc_err", None),
Some(PanicCause::OutOfMemory)
);
}
#[test]
fn test_detect_panic_cause_unknown() {
assert_eq!(detect_panic_cause("some_random_function", None), None);
}
#[test]
fn test_detect_panic_cause_unreachable() {
assert_eq!(
detect_panic_cause("unreachable_panic_handler", None),
Some(PanicCause::Unreachable)
);
}
#[test]
fn test_detect_panic_cause_raw_vec_grow() {
assert_eq!(
detect_panic_cause("raw_vec::grow", None),
Some(PanicCause::CapacityOverflow)
);
}
#[test]
fn test_detect_panic_cause_display_fmt() {
assert_eq!(
detect_panic_cause("Display::fmt", None),
Some(PanicCause::FormattingError)
);
assert_eq!(
detect_panic_cause("Debug::fmt", None),
Some(PanicCause::FormattingError)
);
}
}