Skip to content

Commit 742b1ba

Browse files
authored
Call elimination: requires violation diagnostic should point to call site (#741)
Fixes #531 ## Problem During call elimination, the generated asserts for `requires` preconditions used only the call site metadata (`md`), discarding the requires clause's own metadata. This meant origin-based labels and the requires clause's file range were lost, producing generic diagnostic names like `callElimAssert_requires_0_4` and no link back to the original requires clause location. ## Fix In `createAsserts` and `createAssumes`, use `check.md.setCallSiteFileRange md` instead of `md` alone. This: - Preserves the requires/ensures clause's metadata (including origin labels) as the base - Sets the call site as the primary file range (which was already the reported location) - Adds the original clause file range as a related file range for richer SARIF diagnostics ## Testing All existing tests pass. The correctness proofs (`CallElimCorrect`) compile without changes. A SARIF output test verifies that related locations are emitted correctly.
1 parent 9bba297 commit 742b1ba

13 files changed

Lines changed: 73 additions & 67 deletions

Strata/Transform/CoreTransform.lean

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,10 @@ def createAsserts
254254
:= conds.mapM (fun (l, check) => do
255255
let newLabel ← genIdent l (fun s => s!"{labelPrefix}{s}")
256256
-- Non-lifting: the replacement expressions must be closed (no dangling bvars).
257-
return Statement.assert newLabel.toPretty (Lambda.LExpr.substFvars check.expr subst) md)
257+
-- Use the call site as the primary file range, preserving the requires
258+
-- clause location as a related file range for richer diagnostics.
259+
let assertMd := check.md.setCallSiteFileRange md
260+
return Statement.assert newLabel.toPretty (Lambda.LExpr.substFvars check.expr subst) assertMd)
258261

259262
/-- turns a list of preconditions into assumes with substitution -/
260263
def createAssumes
@@ -267,7 +270,10 @@ def createAssumes
267270
conds.mapM (fun (l, check) => do
268271
let newLabel ← genIdent l (fun s => s!"{labelPrefix}{s}")
269272
-- Non-lifting: the replacement expressions must be closed (no dangling bvars).
270-
return Statement.assume newLabel.toPretty (Lambda.LExpr.substFvars check.expr subst) md)
273+
-- Use the call site as the primary file range, preserving the ensures
274+
-- clause location as a related file range for richer diagnostics.
275+
let assumeMd := check.md.setCallSiteFileRange md
276+
return Statement.assume newLabel.toPretty (Lambda.LExpr.substFvars check.expr subst) assumeMd)
271277

272278
/--
273279
Generate the substitution pairs needed for the body of the procedure

StrataTest/Languages/Python/expected_laurel/test_class_methods.expected

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ test_class_methods.py(24, 4): ✅ pass - assert_assert(539)_calls_Any_to_bool_0
55
test_class_methods.py(24, 4): ❓ unknown - assert(539)
66
test_class_methods.py(28, 4): ✅ pass - assert_assert(654)_calls_Any_to_bool_0
77
test_class_methods.py(28, 4): ❓ unknown - assert(654)
8-
test_class_methods.py(30, 4): ✅ pass - callElimAssert_requires_0_4
9-
test_class_methods.py(30, 4): ✅ pass - callElimAssert_requires_1_5
10-
test_class_methods.py(30, 4): ✅ pass - callElimAssert_requires_2_6
8+
test_class_methods.py(30, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_name_is_foo
9+
test_class_methods.py(30, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_str
10+
test_class_methods.py(30, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_bar
1111
test_class_methods.py(17, 0): ✅ pass - postcondition
1212
test_class_methods.py(32, 0): ✅ pass - ite_cond_calls_Any_to_bool_0
1313
DETAIL: 9 passed, 0 failed, 3 inconclusive

StrataTest/Languages/Python/expected_laurel/test_class_with_methods.expected

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ test_class_with_methods.py(23, 4): ✅ pass - assert_assert(459)_calls_Any_to_bo
33
test_class_with_methods.py(23, 4): ❓ unknown - assert(459)
44
test_class_with_methods.py(26, 4): ✅ pass - assert_assert(544)_calls_Any_to_bool_0
55
test_class_with_methods.py(26, 4): ❓ unknown - assert(544)
6-
test_class_with_methods.py(28, 4): ✅ pass - callElimAssert_requires_0_4
7-
test_class_with_methods.py(28, 4): ✅ pass - callElimAssert_requires_1_5
8-
test_class_with_methods.py(28, 4): ✅ pass - callElimAssert_requires_2_6
6+
test_class_with_methods.py(28, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_name_is_foo
7+
test_class_with_methods.py(28, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_str
8+
test_class_with_methods.py(28, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_bar
99
test_class_with_methods.py(17, 0): ✅ pass - postcondition
1010
test_class_with_methods.py(30, 0): ✅ pass - ite_cond_calls_Any_to_bool_0
1111
DETAIL: 8 passed, 0 failed, 2 inconclusive

StrataTest/Languages/Python/expected_laurel/test_datetime.expected

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
test_datetime.py(13, 0): ✅ pass - callElimAssert_requires_12
2-
test_datetime.py(14, 0): ✅ pass - callElimAssert_requires_0_4
3-
test_datetime.py(14, 0): ✅ pass - callElimAssert_requires_1_5
4-
test_datetime.py(14, 0): ✅ pass - callElimAssert_requires_2_6
5-
test_datetime.py(14, 0): ✅ pass - callElimAssert_requires_3_7
1+
test_datetime.py(13, 0): ✅ pass - (Origin_datetime_date_Requires)d_type
2+
test_datetime.py(14, 0): ✅ pass - (Origin_timedelta_Requires)
3+
test_datetime.py(14, 0): ✅ pass - (Origin_timedelta_Requires)hours_type
4+
test_datetime.py(14, 0): ✅ pass - (Origin_timedelta_Requires)days_pos
5+
test_datetime.py(14, 0): ✅ pass - (Origin_timedelta_Requires)hours_pos
66
test_datetime.py(21, 0): ✅ pass - assert_assert(554)_calls_Any_to_bool_0
77
test_datetime.py(21, 0): ✅ pass - assert(554)
88
test_datetime.py(25, 0): ✅ pass - assert_assert(673)_calls_Any_to_bool_0

StrataTest/Languages/Python/expected_laurel/test_default_params.expected

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@ unknown location: ✅ pass - (greet ensures) Return type constraint
22
test_default_params.py(8, 4): ✅ pass - loop_guard_calls_Any_to_bool_0
33
test_default_params.py(8, 4): ✅ pass - loop_guard_end_calls_Any_to_bool_0
44
unknown location: ❓ unknown - (power ensures) Return type constraint
5-
test_default_params.py(14, 4): ✅ pass - callElimAssert_requires_0_21
6-
test_default_params.py(14, 4): ✅ pass - callElimAssert_requires_1_22
5+
test_default_params.py(14, 4): ✅ pass - (greet requires) Type constraint of name
6+
test_default_params.py(14, 4): ✅ pass - (greet requires) Type constraint of greeting
77
test_default_params.py(15, 4): ✅ pass - assert_assert(325)_calls_Any_to_bool_0
88
test_default_params.py(15, 4): ❓ unknown - assert(325)
9-
test_default_params.py(17, 4): ✅ pass - callElimAssert_requires_0_15
10-
test_default_params.py(17, 4): ✅ pass - callElimAssert_requires_1_16
9+
test_default_params.py(17, 4): ✅ pass - (greet requires) Type constraint of name
10+
test_default_params.py(17, 4): ✅ pass - (greet requires) Type constraint of greeting
1111
test_default_params.py(18, 4): ✅ pass - assert_assert(421)_calls_Any_to_bool_0
1212
test_default_params.py(18, 4): ❓ unknown - assert(421)
13-
test_default_params.py(20, 4): ✅ pass - callElimAssert_requires_0_9
14-
test_default_params.py(20, 4): ✅ pass - callElimAssert_requires_1_10
13+
test_default_params.py(20, 4): ✅ pass - (power requires) Type constraint of base
14+
test_default_params.py(20, 4): ✅ pass - (power requires) Type constraint of exp
1515
test_default_params.py(21, 4): ✅ pass - assert_assert(501)_calls_Any_to_bool_0
1616
test_default_params.py(21, 4): ❓ unknown - assert(501)
17-
test_default_params.py(23, 4): ✅ pass - callElimAssert_requires_0_3
18-
test_default_params.py(23, 4): ✅ pass - callElimAssert_requires_1_4
17+
test_default_params.py(23, 4): ✅ pass - (power requires) Type constraint of base
18+
test_default_params.py(23, 4): ✅ pass - (power requires) Type constraint of exp
1919
test_default_params.py(24, 4): ✅ pass - assert_assert(571)_calls_Any_to_bool_0
2020
test_default_params.py(24, 4): ❓ unknown - assert(571)
2121
test_default_params.py(26, 0): ✅ pass - ite_cond_calls_Any_to_bool_0

StrataTest/Languages/Python/expected_laurel/test_func_input_type_constraints.expected

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ unknown location: ✅ pass - (Sum ensures) Return type constraint
55
unknown location: ❓ unknown - set_LaurelResult_calls_Any_get_0
66
unknown location: ❓ unknown - set_LaurelResult_calls_Any_get_1
77
unknown location: ❓ unknown - (List_Dict_index ensures) Return type constraint
8-
test_func_input_type_constraints.py(15, 0): ✅ pass - callElimAssert_requires_0_17
9-
test_func_input_type_constraints.py(15, 0): ✅ pass - callElimAssert_requires_1_18
10-
test_func_input_type_constraints.py(16, 0): ✅ pass - callElimAssert_requires_0_11
11-
test_func_input_type_constraints.py(16, 0): ✅ pass - callElimAssert_requires_1_12
12-
test_func_input_type_constraints.py(17, 0): ✅ pass - callElimAssert_requires_0_4
13-
test_func_input_type_constraints.py(17, 0): ✅ pass - callElimAssert_requires_1_5
14-
test_func_input_type_constraints.py(17, 0): ✅ pass - callElimAssert_requires_2_6
8+
test_func_input_type_constraints.py(15, 0): ✅ pass - (Mul requires) Type constraint of x
9+
test_func_input_type_constraints.py(15, 0): ✅ pass - (Mul requires) Type constraint of y
10+
test_func_input_type_constraints.py(16, 0): ✅ pass - (Sum requires) Type constraint of x
11+
test_func_input_type_constraints.py(16, 0): ✅ pass - (Sum requires) Type constraint of y
12+
test_func_input_type_constraints.py(17, 0): ✅ pass - (List_Dict_index requires) Type constraint of l
13+
test_func_input_type_constraints.py(17, 0): ✅ pass - (List_Dict_index requires) Type constraint of i
14+
test_func_input_type_constraints.py(17, 0): ✅ pass - (List_Dict_index requires) Type constraint of s
1515
DETAIL: 11 passed, 0 failed, 3 inconclusive
1616
RESULT: Inconclusive
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
test_function_def_calls.py(6, 4): ❓ unknown - callElimAssert_requires_0_4
2-
test_function_def_calls.py(6, 4): ✅ pass - callElimAssert_requires_1_5
3-
test_function_def_calls.py(6, 4): ✅ pass - callElimAssert_requires_2_6
4-
test_function_def_calls.py(9, 4): ✅ pass - callElimAssert_requires_10
1+
test_function_def_calls.py(6, 4): ❓ unknown - (Origin_test_helper_procedure_Requires)req_name_is_foo
2+
test_function_def_calls.py(6, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_str
3+
test_function_def_calls.py(6, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_bar
4+
test_function_def_calls.py(9, 4): ✅ pass - (my_f requires) Type constraint of s
55
test_function_def_calls.py(11, 0): ✅ pass - ite_cond_calls_Any_to_bool_0
66
DETAIL: 4 passed, 0 failed, 1 inconclusive
77
RESULT: Inconclusive

StrataTest/Languages/Python/expected_laurel/test_if_elif.expected

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ unknown location: ✅ pass - (classify ensures) Return type constraint
55
test_if_elif.py(6, 4): ✅ pass - ite_cond_calls_Any_to_bool_0
66
unknown location: ✅ pass - (classify ensures) Return type constraint
77
unknown location: ✅ pass - (classify ensures) Return type constraint
8-
test_if_elif.py(12, 4): ✅ pass - callElimAssert_requires_14
8+
test_if_elif.py(12, 4): ✅ pass - (classify requires) Type constraint of x
99
test_if_elif.py(13, 4): ✅ pass - assert_assert(225)_calls_Any_to_bool_0
1010
test_if_elif.py(13, 4): ❓ unknown - assert(225)
11-
test_if_elif.py(15, 4): ✅ pass - callElimAssert_requires_10
11+
test_if_elif.py(15, 4): ✅ pass - (classify requires) Type constraint of x
1212
test_if_elif.py(16, 4): ✅ pass - assert_assert(302)_calls_Any_to_bool_0
1313
test_if_elif.py(16, 4): ❓ unknown - assert(302)
14-
test_if_elif.py(18, 4): ✅ pass - callElimAssert_requires_6
14+
test_if_elif.py(18, 4): ✅ pass - (classify requires) Type constraint of x
1515
test_if_elif.py(19, 4): ✅ pass - assert_assert(371)_calls_Any_to_bool_0
1616
test_if_elif.py(19, 4): ❓ unknown - assert(371)
17-
test_if_elif.py(21, 4): ✅ pass - callElimAssert_requires_2
17+
test_if_elif.py(21, 4): ✅ pass - (classify requires) Type constraint of x
1818
test_if_elif.py(22, 4): ✅ pass - assert_assert(444)_calls_Any_to_bool_0
1919
test_if_elif.py(22, 4): ❓ unknown - assert(444)
2020
test_if_elif.py(24, 0): ✅ pass - ite_cond_calls_Any_to_bool_0
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
test_missing_models.py(8, 4): ❓ unknown - init_calls_Any_get_0
22
test_missing_models.py(8, 4): ❓ unknown - init_calls_Any_get_1
3-
test_missing_models.py(9, 4): ❓ unknown - callElimAssert_requires_0_22
4-
test_missing_models.py(9, 4): ✅ pass - callElimAssert_requires_1_23
5-
test_missing_models.py(9, 4): ✅ pass - callElimAssert_requires_2_24
3+
test_missing_models.py(9, 4): ❓ unknown - (Origin_test_helper_procedure_Requires)req_name_is_foo
4+
test_missing_models.py(9, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_str
5+
test_missing_models.py(9, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_bar
66
DETAIL: 2 passed, 0 failed, 3 inconclusive
77
RESULT: Inconclusive

StrataTest/Languages/Python/expected_laurel/test_multi_function.expected

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@ test_multi_function.py(11, 4): ✅ pass - ite_cond_calls_PNotIn_0
66
test_multi_function.py(11, 4): ✅ pass - ite_cond_calls_Any_to_bool_1
77
unknown location: ✅ pass - (validate_config ensures) Return type constraint
88
unknown location: ✅ pass - (validate_config ensures) Return type constraint
9-
test_multi_function.py(16, 4): ✅ pass - callElimAssert_requires_0_7
10-
test_multi_function.py(16, 4): ✅ pass - callElimAssert_requires_1_8
11-
test_multi_function.py(17, 4): ✅ pass - callElimAssert_requires_2
9+
test_multi_function.py(16, 4): ✅ pass - (create_config requires) Type constraint of name
10+
test_multi_function.py(16, 4): ✅ pass - (create_config requires) Type constraint of value
11+
test_multi_function.py(17, 4): ✅ pass - (validate_config requires) Type constraint of config
1212
test_multi_function.py(18, 4): ✅ pass - ite_cond_calls_Any_to_bool_0
1313
unknown location: ❓ unknown - set_LaurelResult_calls_Any_get_0
14-
test_multi_function.py(23, 4): ✅ pass - callElimAssert_requires_0_21
15-
test_multi_function.py(23, 4): ✅ pass - callElimAssert_requires_1_22
14+
test_multi_function.py(23, 4): ✅ pass - (process_config requires) Type constraint of name
15+
test_multi_function.py(23, 4): ✅ pass - (process_config requires) Type constraint of value
1616
test_multi_function.py(24, 4): ✅ pass - assert_assert(651)_calls_Any_to_bool_0
1717
test_multi_function.py(24, 4): ❓ unknown - assert(651)
18-
test_multi_function.py(26, 4): ✅ pass - callElimAssert_requires_0_14
19-
test_multi_function.py(26, 4): ✅ pass - callElimAssert_requires_1_15
20-
test_multi_function.py(26, 4): ✅ pass - callElimAssert_requires_2_16
18+
test_multi_function.py(26, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_name_is_foo
19+
test_multi_function.py(26, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_str
20+
test_multi_function.py(26, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_bar
2121
test_multi_function.py(28, 0): ✅ pass - ite_cond_calls_Any_to_bool_0
2222
DETAIL: 19 passed, 0 failed, 2 inconclusive
2323
RESULT: Inconclusive

0 commit comments

Comments
 (0)