diff --git a/Cargo.toml b/Cargo.toml index 99d9402f..2ed14399 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ tree-sitter-inference = "0.0.38" anyhow = "1.0.101" thiserror = "2.0.18" serde = { version = "1.0.228", features = ["derive", "rc"] } +cov-mark = { version = "2.2.0", default-features = false } leb128 = "0.2.5" rustc-hash = "2.1.1" diff --git a/core/type-checker/Cargo.toml b/core/type-checker/Cargo.toml index dc41e173..9321f743 100644 --- a/core/type-checker/Cargo.toml +++ b/core/type-checker/Cargo.toml @@ -11,3 +11,4 @@ inference-ast.workspace = true anyhow.workspace = true thiserror.workspace = true rustc-hash.workspace = true +cov-mark.workspace = true diff --git a/core/type-checker/src/type_checker.rs b/core/type-checker/src/type_checker.rs index 6c171663..0e00b52c 100644 --- a/core/type-checker/src/type_checker.rs +++ b/core/type-checker/src/type_checker.rs @@ -2092,8 +2092,10 @@ impl TypeChecker { }; if let Some(key) = key { if self.reported_error_keys.contains(&key) { + cov_mark::hit!(type_checker_error_dedup_skips_duplicate); return; } + cov_mark::hit!(type_checker_error_dedup_first_occurrence); self.reported_error_keys.insert(key); } self.errors.push(error); diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 47e3b0a2..0f0c8cb5 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -9,7 +9,7 @@ repository = { workspace = true } [dependencies] anyhow.workspace = true serde_json = "1.0.99" -wasmtime="40.0.0" +wasmtime="41.0.3" inference-ast.workspace = true inference-wasm-codegen.workspace = true @@ -19,3 +19,6 @@ inf-wasmparser.workspace = true tree-sitter.workspace = true tree-sitter-inference.workspace = true rustc-hash.workspace = true + +[dev-dependencies] +cov-mark = { workspace = true, features = ["enable"] } diff --git a/tests/src/type_checker/error_recovery.rs b/tests/src/type_checker/error_recovery.rs index 512cf478..8ec6279d 100644 --- a/tests/src/type_checker/error_recovery.rs +++ b/tests/src/type_checker/error_recovery.rs @@ -125,12 +125,8 @@ mod error_recovery_tests { #[test] fn test_error_deduplication() { let source = r#" - struct Container { - value: UnknownType; - } - fn test(c: Container) -> UnknownType { - let x: UnknownType = c.value; - return x; + fn test(a: UnknownType, b: UnknownType) -> i32 { + return 0; } "#; let result = try_type_check(source); @@ -142,15 +138,47 @@ mod error_recovery_tests { if let Err(error) = result { let error_msg = error.to_string(); let unknown_type_count = error_msg.matches("unknown type `UnknownType`").count(); - assert!( - unknown_type_count <= 3, - "UnknownType error should not be excessively duplicated (found {} occurrences), got: {}", - unknown_type_count, + assert_eq!( + unknown_type_count, 1, + "UnknownType error should appear exactly once due to deduplication, got: {}", error_msg ); } } + /// Structural coverage: verifies that the first occurrence of a deduplicated + /// error type hits the `type_checker_error_dedup_first_occurrence` branch in + /// `push_error_dedup`. Uses a source with exactly one `UnknownType` so the + /// mark fires exactly once. + #[test] + fn test_error_dedup_first_occurrence() { + let source = r#"fn test(x: UnknownType) -> i32 { return 0; }"#; + cov_mark::check_count!(type_checker_error_dedup_first_occurrence, 1); + let result = try_type_check(source); + assert!( + result.is_err(), + "Type checker should report unknown type error" + ); + } + + /// Structural coverage: verifies that the second occurrence of the same + /// deduplicated error type hits the `type_checker_error_dedup_skips_duplicate` + /// branch in `push_error_dedup`. Uses a source where `UnknownType` appears + /// exactly twice so that the first occurrence hits + /// `type_checker_error_dedup_first_occurrence` and the second occurrence + /// hits `type_checker_error_dedup_skips_duplicate`; this test specifically + /// asserts only on the skips-duplicate path. + #[test] + fn test_error_dedup_skips_duplicate() { + let source = r#"fn test(a: UnknownType, b: UnknownType) -> i32 { return 0; }"#; + cov_mark::check_count!(type_checker_error_dedup_skips_duplicate, 1); + let result = try_type_check(source); + assert!( + result.is_err(), + "Type checker should report unknown type error" + ); + } + #[test] fn test_method_call_on_non_struct_infers_arguments() { let source = r#"