Add pattern match exhaustiveness checking
Implements the exhaustiveness algorithm to detect non-exhaustive pattern matches: - Detects missing Bool patterns (true/false) - Detects missing Option patterns (Some/None) - Detects missing Result patterns (Ok/Err) - Recognizes wildcards and variable patterns as catch-alls - Warns about redundant patterns after catch-all patterns - Integrates with the type checker to report errors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
111
src/main.rs
111
src/main.rs
@@ -2,6 +2,7 @@
|
||||
|
||||
mod ast;
|
||||
mod diagnostics;
|
||||
mod exhaustiveness;
|
||||
mod interpreter;
|
||||
mod lexer;
|
||||
mod modules;
|
||||
@@ -931,4 +932,114 @@ c")"#;
|
||||
assert_eq!(diag.title, "Purity Violation");
|
||||
}
|
||||
}
|
||||
|
||||
// Exhaustiveness checking tests
|
||||
mod exhaustiveness_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_exhaustive_bool_match() {
|
||||
let source = r#"
|
||||
fn check(b: Bool): Int = match b {
|
||||
true => 1,
|
||||
false => 0
|
||||
}
|
||||
let result = check(true)
|
||||
"#;
|
||||
let result = eval(source);
|
||||
assert!(result.is_ok(), "Expected success but got: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_exhaustive_bool_match() {
|
||||
let source = r#"
|
||||
fn check(b: Bool): Int = match b {
|
||||
true => 1
|
||||
}
|
||||
let result = check(true)
|
||||
"#;
|
||||
let result = eval(source);
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("Non-exhaustive"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exhaustive_option_match() {
|
||||
let source = r#"
|
||||
fn unwrap_or(opt: Option<Int>, default: Int): Int = match opt {
|
||||
Some(x) => x,
|
||||
None => default
|
||||
}
|
||||
let result = unwrap_or(Some(42), 0)
|
||||
"#;
|
||||
let result = eval(source);
|
||||
assert!(result.is_ok(), "Expected success but got: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_exhaustive_option_missing_none() {
|
||||
let source = r#"
|
||||
fn get_value(opt: Option<Int>): Int = match opt {
|
||||
Some(x) => x
|
||||
}
|
||||
let result = get_value(Some(1))
|
||||
"#;
|
||||
let result = eval(source);
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("Non-exhaustive"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wildcard_is_exhaustive() {
|
||||
let source = r#"
|
||||
fn classify(n: Int): String = match n {
|
||||
0 => "zero",
|
||||
1 => "one",
|
||||
_ => "many"
|
||||
}
|
||||
let result = classify(5)
|
||||
"#;
|
||||
let result = eval(source);
|
||||
assert!(result.is_ok(), "Expected success but got: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variable_pattern_is_exhaustive() {
|
||||
let source = r#"
|
||||
fn identity(n: Int): Int = match n {
|
||||
x => x
|
||||
}
|
||||
let result = identity(42)
|
||||
"#;
|
||||
let result = eval(source);
|
||||
assert!(result.is_ok(), "Expected success but got: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_redundant_arm_warning() {
|
||||
let source = r#"
|
||||
fn test_fn(n: Int): Int = match n {
|
||||
_ => 1,
|
||||
0 => 2
|
||||
}
|
||||
let result = test_fn(0)
|
||||
"#;
|
||||
let result = eval(source);
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("Redundant"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exhaustive_result_match() {
|
||||
let source = r#"
|
||||
fn handle_result(r: Result<Int, String>): Int = match r {
|
||||
Ok(n) => n,
|
||||
Err(_) => 0
|
||||
}
|
||||
let result = handle_result(Ok(42))
|
||||
"#;
|
||||
let result = eval(source);
|
||||
assert!(result.is_ok(), "Expected success but got: {:?}", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user