refactor: expose checked function fallthrough obligations

This commit is contained in:
2026-10-25 01:59:57 +02:00
parent 2761c77182
commit 2c11772931
4 changed files with 74 additions and 28 deletions
+41 -12
View File
@@ -34,6 +34,24 @@ expect_error proc(
return actual.diagnostic
}
@hide:file
expect_fallthrough proc(
result @Result,
diagnostic_store @diagnostics.Store,
function ast.NodeId,
span ast.Span,
) diagnostics.DiagnosticId ! testing.Error {
if (fallthrough_diagnostic(result, function)) |id| {
if (diagnostics.get(diagnostic_store, id)) |diagnostic| {
try testing.expect_equal(diagnostics.Code.missing_return, diagnostic.code)
try testing.expect_equal(span.start, diagnostic.span.start)
try testing.expect_equal(span.end, diagnostic.span.end)
return id
}
}
return .expectation_failed
}
@hide:file
code_count proc(diagnostic_store @diagnostics.Store, code diagnostics.Code) usize {
count := usize(0)
@@ -376,14 +394,16 @@ reports_nonvoid_fallthrough_without_poisoning_signatures_and_checks_unused_bodie
try expect_kind(&result, function, .function)
try testing.expect_equal(function, type_of(&result, function).function)
try expect_kind(&result, view.return_type, returns[index])
_ = try expect_error(&result, &diagnostic_store, view.body, .missing_return, ast.node_span(&tree, view.body))
_ = try expect_fallthrough(&result, &diagnostic_store, function, ast.node_span(&tree, view.body))
}
broken :: ast.package_decl(&tree, 4)
try expect_kind(&result, broken, .function)
try testing.expect_equal(null, fallthrough_diagnostic(&result, broken))
broken_body :: ast.function_decl(&tree, broken).body
bad_operand :: ast.statement_expr(&tree, ast.block_statement(&tree, broken_body, 0))
_ = try expect_error(&result, &diagnostic_store, bad_operand, .invalid_operand, ast.node_span(&tree, bad_operand))
main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body
try testing.expect_equal(null, fallthrough_diagnostic(&result, ast.package_decl(&tree, 5)))
call :: ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 0))
try expect_kind(&result, call, .int)
try testing.expect_equal(2, code_count(&diagnostic_store, .missing_return))
@@ -721,8 +741,9 @@ return_errors_preserve_identity_check_unreachable_code_and_suppress_missing_retu
try expect_kind(&result, ast.block_statement(&tree, unreachable_body, 0), .void)
bad :: ast.statement_expr(&tree, ast.block_statement(&tree, unreachable_body, 1))
_ = try expect_error(&result, &diagnostic_store, bad, .invalid_operand, ast.node_span(&tree, bad))
missing_body :: ast.function_decl(&tree, ast.package_decl(&tree, 6)).body
_ = try expect_error(&result, &diagnostic_store, missing_body, .missing_return, ast.node_span(&tree, missing_body))
missing :: ast.package_decl(&tree, 6)
missing_body :: ast.function_decl(&tree, missing).body
_ = try expect_fallthrough(&result, &diagnostic_store, missing, ast.node_span(&tree, missing_body))
recovered_body :: ast.function_decl(&tree, ast.package_decl(&tree, 7)).body
try expect_kind(&result, ast.block_statement(&tree, recovered_body, 1), .int)
try testing.expect_equal(3, code_count(&diagnostic_store, .return_type_mismatch))
@@ -928,8 +949,9 @@ analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test {
for (0..usize(11)) |i| try expect_kind(&result, ast.package_decl(&tree, i), .function)
for (usize(1)..usize(3)) |i| {
body :: ast.function_decl(&tree, ast.package_decl(&tree, i)).body
_ = try expect_error(&result, &diagnostic_store, body, .missing_return, ast.node_span(&tree, body))
function :: ast.package_decl(&tree, i)
body :: ast.function_decl(&tree, function).body
_ = try expect_fallthrough(&result, &diagnostic_store, function, ast.node_span(&tree, body))
}
bad_body :: ast.function_decl(&tree, ast.package_decl(&tree, 2)).body
conditional :: ast.block_statement(&tree, bad_body, 0)
@@ -938,7 +960,7 @@ analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test {
operand :: ast.statement_expr(&tree, ast.block_statement(&tree, arm, 0))
original :: try expect_error(&result, &diagnostic_store, operand, .invalid_operand, ast.node_span(&tree, operand))
try testing.expect_equal(original, type_of(&result, arm).diagnostic)
try testing.expect(type_of(&result, bad_body).diagnostic != original)
try testing.expect(fallthrough_diagnostic(&result, ast.package_decl(&tree, 2))? != original)
condition_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body
condition :: ast.if_stmt(&tree, ast.block_statement(&tree, condition_body, 0)).condition
condition_diag :: try expect_error(&result, &diagnostic_store, condition, .condition_not_bool, ast.node_span(&tree, condition))
@@ -964,6 +986,11 @@ analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test {
} else {
return .expectation_failed
}
for 0..ast.package_decl_count(&tree) |i| {
function :: ast.package_decl(&tree, i)
if (i == 1 or i == 2 or ast.decl_kind(&tree, function) != .function) continue
try testing.expect_equal(null, fallthrough_diagnostic(&result, function))
}
try testing.expect_equal(1, code_count(&diagnostic_store, .return_type_mismatch))
try testing.expect_equal(2, code_count(&diagnostic_store, .missing_return))
try testing.expect_equal(1, code_count(&diagnostic_store, .condition_not_bool))
@@ -1212,8 +1239,9 @@ while_checks_nested_bodies_without_claiming_a_zero_iteration_return test {
defer deinit(&result)
for usize(0)..usize(2) |index| {
body :: ast.function_decl(&tree, ast.package_decl(&tree, index)).body
_ = try expect_error(&result, &diagnostic_store, body, .missing_return, ast.node_span(&tree, body))
function :: ast.package_decl(&tree, index)
body :: ast.function_decl(&tree, function).body
_ = try expect_fallthrough(&result, &diagnostic_store, function, ast.node_span(&tree, body))
try expect_kind(&result, ast.block_statement(&tree, body, 0), .void)
}
continues_body :: ast.function_decl(&tree, ast.package_decl(&tree, 2)).body
@@ -1526,7 +1554,7 @@ short_circuit_skip_paths_do_not_guarantee_a_return test {
function :: ast.package_decl(&tree, index)
try expect_kind(&result, function, .function)
body :: ast.function_decl(&tree, function).body
_ = try expect_error(&result, &diagnostic_store, body, .missing_return, ast.node_span(&tree, body))
_ = try expect_fallthrough(&result, &diagnostic_store, function, ast.node_span(&tree, body))
}
expression_body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body
expression :: ast.statement_expr(&tree, ast.block_statement(&tree, expression_body, 0))
@@ -1670,8 +1698,9 @@ loop_controls_do_not_guarantee_function_returns test {
defer deinit(&result)
for (usize(0)..usize(3)) |index| {
body :: ast.function_decl(&tree, ast.package_decl(&tree, index)).body
_ = try expect_error(&result, &diagnostic_store, body, .missing_return, ast.node_span(&tree, body))
function :: ast.package_decl(&tree, index)
body :: ast.function_decl(&tree, function).body
_ = try expect_fallthrough(&result, &diagnostic_store, function, ast.node_span(&tree, body))
try expect_kind(&result, ast.block_statement(&tree, body, 0), .void)
}
closed_body :: ast.function_decl(&tree, ast.package_decl(&tree, 3)).body
@@ -2037,7 +2066,7 @@ range_loop_return_flow_and_controls_are_lexical_and_body_errors_are_isolated tes
function :: ast.package_decl(&tree, index)
body :: ast.function_decl(&tree, function).body
try expect_kind(&result, function, .function)
_ = try expect_error(&result, &diagnostic_store, body, .missing_return, ast.node_span(&tree, body))
_ = try expect_fallthrough(&result, &diagnostic_store, function, ast.node_span(&tree, body))
try expect_kind(&result, ast.block_statement(&tree, body, 0), .void)
}
closed_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body