From 03f4733828c4e5cd4158372f0f49ad13bb6f8b14 Mon Sep 17 00:00:00 2001 From: PhysShell <45852143+PhysShell@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:35:39 +0500 Subject: [PATCH 1/7] =?UTF-8?q?feat(ownir)!:=20OwnIR=20v1=20=E2=80=94=20`m?= =?UTF-8?q?ove`=20and=20the=20exclusive=20`borrow=5Fmut`=20region?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two flow ops join the vocabulary, and an incompatible vocabulary change bumps the version (spec/OwnIR.md §2): OWNIR_VERSION 0 -> 1, LOWERED_VERSION 1 -> 2. A v0 document is refused at the door by both engines; nothing accepts both. move src -> var `var` takes the obligation, `src` is dead borrow_mut owner as binding { .. } a BLOCK-scoped exclusive loan `borrow_mut` is compound on purpose: the core's loans are block-scoped, so the vocabulary must not be able to say "open a loan" without saying where it closes. Inference reads a region as transparent, so a region never changes a summary. Together the two ops are enough for a frontend to express a state protocol: an affine token over an exclusively borrowed entity. The bridge REFUSES four shapes instead of lowering them (spec/Bridge.md, BR-L12/BR-L13), because each would otherwise reach the verdict as "the core raised, the bridge filtered it, clean": a region whose owner is not tracked; a call inside a region to an unresolvable callee that passes a tracked local; a tracked local on a plain position inside a region; and any may/unknown position in a function that opens a region. The loan diagnostics the regions exercise (OWN005/007/008/011/012/013) get flow-local wordings and a `subject`, on both engines. Python is the reference and Rust replays it: own-ir, own-lowered, own-bridge and own-analysis carry the same ops, the same refusals and the same wordings, and the Layer 2/3, summary, CLI, repro and validation ledgers are regenerated at v1. The handwritten `typestate_*` documents and the `typestate_cs_*` / `typestate_ef_*` documents (the frozen output of the Roslyn lowering that lands in the next commit) are byte-identical on both public CLIs: exit code, stdout and stderr, 37 of 37. BREAKING CHANGE: `ownir_version` is 1. Facts stamped 0 are refused; build the extractor and the core from the same commit. Co-Authored-By: Claude Fable 5.1 --- audit/static/run_static.py | 4 +- audit/static/tools/xaml_join.py | 4 +- docs/generated/p022-coord-census.md | 25 +- docs/generated/p022-cp4-census.md | 14 +- docs/generated/p022-cp5-inventory.md | 28 +- docs/generated/p022-shadow-census.md | 8 +- frontend/ownts/ownts.py | 2 +- frontend/roslyn/OwnSharp.Extractor/Program.cs | 4 +- ownlang/analysis.py | 12 +- ownlang/lowered.py | 15 +- ownlang/ownir.py | 277 +++++++- rust/crates/own-analysis/src/ownership.rs | 16 +- rust/crates/own-analysis/tests/subject.rs | 9 +- rust/crates/own-bridge/src/ast.rs | 31 +- rust/crates/own-bridge/src/dump.rs | 6 +- rust/crates/own-bridge/src/lower.rs | 300 ++++++++- rust/crates/own-bridge/src/render.rs | 2 +- rust/crates/own-bridge/src/verdict.rs | 28 +- rust/crates/own-bridge/tests/mechanisms.rs | 20 +- rust/crates/own-bridge/tests/verdicts.rs | 3 +- rust/crates/own-ir/src/lib.rs | 2 +- rust/crates/own-ir/tests/roundtrip.rs | 4 +- rust/crates/own-ir/tests/validation_replay.rs | 2 +- .../own-ir/tests/version_repr_census.rs | 8 +- rust/crates/own-lowered/src/model.rs | 18 +- rust/crates/own-lowered/tests/strictness.rs | 8 +- rust/crates/own-shadow/tests/repro.rs | 2 +- spec/Bridge.md | 26 + spec/OwnIR.md | 49 +- spec/ownir.schema.json | 27 +- tests/fake_shadow_engine.py | 2 +- .../bands-sarif-error-normal.case.json | 2 +- .../bands-sarif-error-quiet.case.json | 2 +- .../bands-sarif-error-verbose.case.json | 2 +- .../bands-sarif-warning-normal.case.json | 2 +- .../bands-sarif-warning-quiet.case.json | 2 +- .../bands-sarif-warning-verbose.case.json | 2 +- .../fixtures/cli_ownir/clean-sarif.case.json | 2 +- .../inputs/empty_ignore_reason.facts.json | 2 +- .../cli_ownir/inputs/nonascii_file.facts.json | 2 +- .../facts.json" | 2 +- .../inputs/suppressed_only.facts.json | 2 +- .../cli_ownir/leaky-sarif-error.case.json | 2 +- .../cli_ownir/leaky-sarif-warning.case.json | 2 +- .../path-backslash-file-sarif.case.json | 2 +- .../path-nonascii-file-sarif.case.json | 2 +- .../refuse-version-mismatch.case.json | 2 +- .../sarif-ascii-escape-em-dash.case.json | 2 +- .../cli_ownir/suppressed-only-sarif.case.json | 2 +- .../usage-duplicate-flag-last-wins.case.json | 2 +- .../usage-equals-spelling-accepted.case.json | 2 +- .../lowered/alias_join_cases.facts.json | 2 +- .../lowered/alias_join_cases.golden.json | 2 +- .../lowered/flow_kill_on_rebind.facts.json | 2 +- .../lowered/flow_kill_on_rebind.golden.json | 2 +- .../lowered/flow_unmapped_refs.facts.json | 2 +- .../lowered/flow_unmapped_refs.golden.json | 2 +- .../lowered/fn_params_ordering.facts.json | 2 +- .../lowered/fn_params_ordering.golden.json | 2 +- .../handles_global_counters.facts.json | 2 +- .../handles_global_counters.golden.json | 2 +- .../lowered/handles_null_metadata.facts.json | 2 +- .../lowered/handles_null_metadata.golden.json | 2 +- .../lowered/hoist_neg_early_return.facts.json | 2 +- .../hoist_neg_early_return.golden.json | 2 +- .../lowered/hoist_neg_nested_depth.facts.json | 2 +- .../hoist_neg_nested_depth.golden.json | 2 +- .../lowered/hoist_neg_while_body.facts.json | 2 +- .../lowered/hoist_neg_while_body.golden.json | 2 +- .../lowered/hoist_pool_kind.facts.json | 2 +- .../lowered/hoist_pool_kind.golden.json | 2 +- .../lowered/hoist_positive_release.facts.json | 2 +- .../hoist_positive_release.golden.json | 2 +- .../hoist_positive_use_only.facts.json | 2 +- .../hoist_positive_use_only.golden.json | 2 +- .../lowered/lines_preserved.facts.json | 2 +- .../lowered/lines_preserved.golden.json | 2 +- tests/fixtures/lowered/manifest.json | 267 +++++++- .../mos_call_channel_overload_sig.facts.json | 2 +- .../mos_call_channel_overload_sig.golden.json | 2 +- .../mos_call_direct_consume.facts.json | 2 +- .../mos_call_direct_consume.golden.json | 2 +- .../lowered/mos_call_unknown_drop.facts.json | 2 +- .../lowered/mos_call_unknown_drop.golden.json | 2 +- .../lowered/mos_fresh_mint.facts.json | 2 +- .../lowered/mos_fresh_mint.golden.json | 2 +- .../lowered/mos_killsite_toplevel.facts.json | 2 +- .../lowered/mos_killsite_toplevel.golden.json | 2 +- .../lowered/mos_untrack_inbranch.facts.json | 2 +- .../lowered/mos_untrack_inbranch.golden.json | 2 +- .../lowered/routing_r1_unresolved.facts.json | 2 +- .../lowered/routing_r1_unresolved.golden.json | 2 +- .../routing_r2_subscribe_self.facts.json | 2 +- .../routing_r2_subscribe_self.golden.json | 2 +- .../routing_r3_capture_static.facts.json | 2 +- .../routing_r3_capture_static.golden.json | 2 +- .../routing_r4_returned_fresh.facts.json | 2 +- .../routing_r4_returned_fresh.golden.json | 2 +- .../lowered/routing_r5_di_capture.facts.json | 2 +- .../lowered/routing_r5_di_capture.golden.json | 2 +- .../lowered/routing_r6_token_kinds.facts.json | 2 +- .../routing_r6_token_kinds.golden.json | 2 +- .../lowered/tolerant_unknown_kind.facts.json | 2 +- .../lowered/tolerant_unknown_kind.golden.json | 2 +- ...typestate_cs_a1_alias_of_entity.facts.json | 79 +++ ...ypestate_cs_a1_alias_of_entity.golden.json | 215 +++++++ ...te_cs_a2_entity_through_indexer.facts.json | 79 +++ ...e_cs_a2_entity_through_indexer.golden.json | 215 +++++++ ...tate_cs_g6_capability_not_spent.facts.json | 42 ++ ...ate_cs_g6_capability_not_spent.golden.json | 149 +++++ ...estate_cs_l10_region_inside_try.facts.json | 74 +++ ...state_cs_l10_region_inside_try.golden.json | 210 ++++++ ...cs_l11_region_beside_disposable.facts.json | 98 +++ ...s_l11_region_beside_disposable.golden.json | 244 +++++++ ...region_beside_leaked_disposable.facts.json | 93 +++ ...egion_beside_leaked_disposable.golden.json | 239 +++++++ ...l13_values_captured_into_region.facts.json | 96 +++ ...13_values_captured_into_region.golden.json | 233 +++++++ ...ypestate_cs_l1_stale_capability.facts.json | 143 +++++ ...pestate_cs_l1_stale_capability.golden.json | 348 ++++++++++ ...pestate_cs_l2_copied_capability.facts.json | 89 +++ ...estate_cs_l2_copied_capability.golden.json | 235 +++++++ .../typestate_cs_l3_raw_mutation.facts.json | 79 +++ .../typestate_cs_l3_raw_mutation.golden.json | 215 +++++++ .../typestate_cs_l4_raw_escape.facts.json | 79 +++ .../typestate_cs_l4_raw_escape.golden.json | 215 +++++++ ...pestate_cs_l5_normal_transition.facts.json | 74 +++ ...estate_cs_l5_normal_transition.golden.json | 210 ++++++ ...typestate_cs_l6_two_transitions.facts.json | 111 ++++ ...ypestate_cs_l6_two_transitions.golden.json | 287 +++++++++ ...tate_cs_l6b_chained_transitions.facts.json | 111 ++++ ...ate_cs_l6b_chained_transitions.golden.json | 287 +++++++++ ...pestate_cs_l7_read_after_region.facts.json | 74 +++ ...estate_cs_l7_read_after_region.golden.json | 210 ++++++ ...tate_cs_l8_branch_inside_region.facts.json | 95 +++ ...ate_cs_l8_branch_inside_region.golden.json | 232 +++++++ ...s_l9_opaque_calls_around_region.facts.json | 74 +++ ..._l9_opaque_calls_around_region.golden.json | 210 ++++++ ...cs_x1_nested_region_same_entity.facts.json | 119 ++++ ...s_x1_nested_region_same_entity.golden.json | 295 +++++++++ .../typestate_ef_orderbackend.facts.json | 312 +++++++++ .../typestate_ef_orderbackend.golden.json | 605 ++++++++++++++++++ ...pestate_g6_capability_not_spent.facts.json | 169 +++++ ...estate_g6_capability_not_spent.golden.json | 444 +++++++++++++ .../typestate_l1_stale_capability.facts.json | 187 ++++++ .../typestate_l1_stale_capability.golden.json | 462 +++++++++++++ .../typestate_l2_copied_capability.facts.json | 193 ++++++ ...typestate_l2_copied_capability.golden.json | 478 ++++++++++++++ ...ypestate_l3_raw_entity_mutation.facts.json | 186 ++++++ ...pestate_l3_raw_entity_mutation.golden.json | 461 +++++++++++++ .../typestate_l3b_second_region.facts.json | 194 ++++++ .../typestate_l3b_second_region.golden.json | 469 ++++++++++++++ ...tate_l4_raw_entity_unknown_call.facts.json | 186 ++++++ ...ate_l4_raw_entity_unknown_call.golden.json | 4 + ...tate_l4b_raw_entity_shared_call.facts.json | 186 ++++++ ...ate_l4b_raw_entity_shared_call.golden.json | 461 +++++++++++++ ...pestate_l4c_raw_entity_consumed.facts.json | 181 ++++++ ...estate_l4c_raw_entity_consumed.golden.json | 456 +++++++++++++ ...ypestate_l4d_token_unknown_call.facts.json | 186 ++++++ ...pestate_l4d_token_unknown_call.golden.json | 4 + .../typestate_l5_normal_transition.facts.json | 178 ++++++ ...typestate_l5_normal_transition.golden.json | 453 +++++++++++++ .../typestate_l5b_two_transitions.facts.json | 192 ++++++ .../typestate_l5b_two_transitions.golden.json | 478 ++++++++++++++ ...typestate_l5c_read_after_region.facts.json | 186 ++++++ ...ypestate_l5c_read_after_region.golden.json | 461 +++++++++++++ ..._n1_token_spent_on_other_entity.facts.json | 196 ++++++ ...n1_token_spent_on_other_entity.golden.json | 482 ++++++++++++++ ...estate_n2_two_tokens_one_region.facts.json | 192 ++++++ ...state_n2_two_tokens_one_region.golden.json | 478 ++++++++++++++ ...n3_raw_mutation_inferred_effect.facts.json | 197 ++++++ ...3_raw_mutation_inferred_effect.golden.json | 4 + ...estate_n4_token_outlives_region.facts.json | 186 ++++++ ...state_n4_token_outlives_region.golden.json | 461 +++++++++++++ .../typestate_w1_loan_wordings.facts.json | 219 +++++++ .../typestate_w1_loan_wordings.golden.json | 442 +++++++++++++ ...pestate_w2_loan_wordings_pooled.facts.json | 225 +++++++ ...estate_w2_loan_wordings_pooled.golden.json | 442 +++++++++++++ .../lowered/vocab_unknown_op.facts.json | 2 +- .../lowered/vocab_unknown_op.golden.json | 2 +- tests/fixtures/ownir/capture.facts.json | 2 +- .../ownir/contract_inference.facts.json | 2 +- tests/fixtures/ownir/di.facts.json | 2 +- tests/fixtures/ownir/di_capture.facts.json | 2 +- tests/fixtures/ownir/disposable.facts.json | 2 +- .../ownir/flow_column_anchors.facts.json | 2 +- .../ownir/flow_finally_switch.facts.json | 2 +- .../ownir/flow_leak_on_else.facts.json | 2 +- .../ownir/flow_leak_two_exits.facts.json | 2 +- .../ownir/flow_nested_throw.facts.json | 2 +- .../ownir/flow_pool_partial.facts.json | 2 +- tests/fixtures/ownir/flow_while.facts.json | 2 +- .../ownir/handoff_contract.facts.json | 2 +- .../ownir/local_disposable.facts.json | 2 +- tests/fixtures/ownir/pool.facts.json | 2 +- .../ownir/protocol_isloaded_clean.facts.json | 2 +- .../protocol_isloaded_violation.facts.json | 2 +- tests/fixtures/ownir/sample.facts.json | 2 +- tests/fixtures/ownir/subscribe.facts.json | 2 +- tests/fixtures/ownir/timer.facts.json | 2 +- .../fixtures/ownir/unitofwork_flow.facts.json | 2 +- tests/fixtures/ownir/unresolved.facts.json | 2 +- tests/fixtures/ownir_validation.json | 566 ++++++++-------- .../repro/canonical_key_order.facts.json | 2 +- .../repro/canonical_key_order.repro.json | 30 +- .../repro/canonical_key_order.trace.json | 12 +- .../repro/canonical_minimal.facts.json | 2 +- .../repro/canonical_minimal.repro.json | 30 +- .../repro/canonical_minimal.trace.json | 12 +- .../repro/canonical_torture.facts.json | 2 +- .../repro/canonical_torture.repro.json | 30 +- .../repro/canonical_torture.trace.json | 12 +- tests/fixtures/repro/di.repro.json | 30 +- tests/fixtures/repro/di.trace.json | 12 +- tests/fixtures/repro/digests.json | 418 +++++++++--- .../fixtures/repro/domain_exponent.facts.json | 2 +- tests/fixtures/repro/domain_float.facts.json | 2 +- .../repro/domain_int_above_i64.facts.json | 2 +- .../repro/domain_int_below_i64.facts.json | 2 +- .../repro/domain_negative_zero.facts.json | 2 +- .../repro/domain_non_finite.facts.json | 2 +- .../repro/hoist_neg_while_body.repro.json | 26 +- .../repro/hoist_neg_while_body.trace.json | 12 +- .../mosdump_degraded_duplicate_key.repro.json | 30 +- .../mosdump_degraded_duplicate_key.trace.json | 12 +- .../protocol_isloaded_violation.repro.json | 30 +- .../protocol_isloaded_violation.trace.json | 12 +- tests/fixtures/repro/variants/bom.bin | 2 +- .../repro/variants/byte_variants.json | 48 +- tests/fixtures/repro/variants/crlf.bin | 2 +- .../repro/variants/extra_trailing_newline.bin | 2 +- .../fixtures/repro/variants/invalid_utf8.bin | 2 +- .../repro/variants/key_order_reversed.bin | 2 +- .../repro/variants/malformed_json.bin | 2 +- .../repro/variants/no_trailing_newline.bin | 2 +- tests/fixtures/repro/variants/plain.bin | 2 +- .../repro/variants/whitespace_compact.bin | 2 +- .../repro/variants/whitespace_expanded.bin | 2 +- ...ct_door_effect_deps_not_strings.repro.json | 22 +- ...ct_door_effect_deps_not_strings.trace.json | 8 +- ...t_door_service_unknown_lifetime.repro.json | 22 +- ...t_door_service_unknown_lifetime.trace.json | 8 +- .../repro/vocab_unknown_op.repro.json | 22 +- .../repro/vocab_unknown_op.trace.json | 8 +- .../summaries/alias_join_cases.summaries.json | 2 +- .../flow_kill_on_rebind.summaries.json | 2 +- .../flow_unmapped_refs.summaries.json | 2 +- .../fn_params_ordering.summaries.json | 2 +- .../handles_global_counters.summaries.json | 2 +- .../handles_null_metadata.summaries.json | 2 +- .../hoist_neg_early_return.summaries.json | 2 +- .../hoist_neg_nested_depth.summaries.json | 2 +- .../hoist_neg_while_body.summaries.json | 2 +- .../summaries/hoist_pool_kind.summaries.json | 2 +- .../hoist_positive_release.summaries.json | 2 +- .../hoist_positive_use_only.summaries.json | 2 +- .../summaries/lines_preserved.summaries.json | 2 +- tests/fixtures/summaries/manifest.json | 2 +- ...s_call_channel_overload_sig.summaries.json | 2 +- .../mos_call_direct_consume.summaries.json | 2 +- .../mos_call_unknown_drop.summaries.json | 2 +- .../summaries/mos_fresh_mint.summaries.json | 2 +- .../mos_killsite_toplevel.summaries.json | 2 +- .../mos_untrack_inbranch.summaries.json | 2 +- .../mosdump_degraded_duplicate_key.facts.json | 2 +- ...dump_degraded_duplicate_key.summaries.json | 2 +- .../mosdump_explicit_effects.facts.json | 2 +- .../mosdump_explicit_effects.summaries.json | 2 +- .../mosdump_nonascii_escaping.facts.json | 2 +- .../mosdump_nonascii_escaping.summaries.json | 2 +- ...osdump_overload_merge_tiebreaks.facts.json | 2 +- ...mp_overload_merge_tiebreaks.summaries.json | 2 +- .../mosdump_partial_and_guarded.facts.json | 2 +- ...mosdump_partial_and_guarded.summaries.json | 2 +- .../mosdump_return_chains.facts.json | 2 +- .../mosdump_return_chains.summaries.json | 2 +- .../summaries/mosdump_scc_cycles.facts.json | 2 +- .../mosdump_scc_cycles.summaries.json | 2 +- .../mosdump_sig_vocabulary.facts.json | 2 +- .../mosdump_sig_vocabulary.summaries.json | 2 +- .../mosdump_sink_channels.facts.json | 2 +- .../mosdump_sink_channels.summaries.json | 2 +- .../routing_r1_unresolved.summaries.json | 2 +- .../routing_r2_subscribe_self.summaries.json | 2 +- .../routing_r3_capture_static.summaries.json | 2 +- .../routing_r4_returned_fresh.summaries.json | 2 +- .../routing_r5_di_capture.summaries.json | 2 +- .../routing_r6_token_kinds.summaries.json | 2 +- ...state_cs_a1_alias_of_entity.summaries.json | 41 ++ ...s_a2_entity_through_indexer.summaries.json | 41 ++ ..._cs_g6_capability_not_spent.summaries.json | 18 + ...te_cs_l10_region_inside_try.summaries.json | 41 ++ ...11_region_beside_disposable.summaries.json | 51 ++ ...on_beside_leaked_disposable.summaries.json | 51 ++ ...values_captured_into_region.summaries.json | 41 ++ ...tate_cs_l1_stale_capability.summaries.json | 87 +++ ...ate_cs_l2_copied_capability.summaries.json | 41 ++ ...ypestate_cs_l3_raw_mutation.summaries.json | 41 ++ .../typestate_cs_l4_raw_escape.summaries.json | 41 ++ ...ate_cs_l5_normal_transition.summaries.json | 41 ++ ...state_cs_l6_two_transitions.summaries.json | 64 ++ ..._cs_l6b_chained_transitions.summaries.json | 64 ++ ...ate_cs_l7_read_after_region.summaries.json | 41 ++ ..._cs_l8_branch_inside_region.summaries.json | 41 ++ ..._opaque_calls_around_region.summaries.json | 41 ++ ...1_nested_region_same_entity.summaries.json | 64 ++ .../typestate_ef_orderbackend.summaries.json | 165 +++++ ...ate_g6_capability_not_spent.summaries.json | 161 +++++ ...pestate_l1_stale_capability.summaries.json | 161 +++++ ...estate_l2_copied_capability.summaries.json | 161 +++++ ...tate_l3_raw_entity_mutation.summaries.json | 161 +++++ ...typestate_l3b_second_region.summaries.json | 161 +++++ ..._l4_raw_entity_unknown_call.summaries.json | 161 +++++ ..._l4b_raw_entity_shared_call.summaries.json | 161 +++++ ...ate_l4c_raw_entity_consumed.summaries.json | 161 +++++ ...tate_l4d_token_unknown_call.summaries.json | 161 +++++ ...estate_l5_normal_transition.summaries.json | 161 +++++ ...pestate_l5b_two_transitions.summaries.json | 161 +++++ ...state_l5c_read_after_region.summaries.json | 161 +++++ ...token_spent_on_other_entity.summaries.json | 161 +++++ ...te_n2_two_tokens_one_region.summaries.json | 161 +++++ ...aw_mutation_inferred_effect.summaries.json | 178 ++++++ ...te_n4_token_outlives_region.summaries.json | 161 +++++ .../typestate_w1_loan_wordings.summaries.json | 119 ++++ ...ate_w2_loan_wordings_pooled.summaries.json | 119 ++++ .../summaries/vocab_unknown_op.summaries.json | 2 +- .../render_anchorless.facts.json | 2 +- .../render_anchorless.renders.json | 4 +- .../verdict_renders/render_columns.facts.json | 2 +- .../render_columns.renders.json | 4 +- .../verdict_renders/render_empty.facts.json | 2 +- .../verdict_renders/render_empty.renders.json | 4 +- .../render_escaping.facts.json | 2 +- .../render_escaping.renders.json | 4 +- .../render_evidence_slices.facts.json | 2 +- .../render_evidence_slices.renders.json | 4 +- .../render_protocol_family.facts.json | 2 +- .../render_protocol_family.renders.json | 4 +- .../verdict_renders/render_refusal.facts.json | 2 +- .../render_rule_catalogue.facts.json | 2 +- .../render_rule_catalogue.renders.json | 4 +- .../render_tiers_and_levels.facts.json | 2 +- .../render_tiers_and_levels.renders.json | 4 +- ...estate_cs_a1_alias_of_entity.verdicts.json | 21 + ...cs_a2_entity_through_indexer.verdicts.json | 21 + ...e_cs_g6_capability_not_spent.verdicts.json | 21 + ...ate_cs_l10_region_inside_try.verdicts.json | 4 + ...l11_region_beside_disposable.verdicts.json | 4 + ...ion_beside_leaked_disposable.verdicts.json | 21 + ..._values_captured_into_region.verdicts.json | 4 + ...state_cs_l1_stale_capability.verdicts.json | 32 + ...tate_cs_l2_copied_capability.verdicts.json | 21 + ...typestate_cs_l3_raw_mutation.verdicts.json | 21 + .../typestate_cs_l4_raw_escape.verdicts.json | 21 + ...tate_cs_l5_normal_transition.verdicts.json | 4 + ...estate_cs_l6_two_transitions.verdicts.json | 4 + ...e_cs_l6b_chained_transitions.verdicts.json | 4 + ...tate_cs_l7_read_after_region.verdicts.json | 4 + ...e_cs_l8_branch_inside_region.verdicts.json | 32 + ...9_opaque_calls_around_region.verdicts.json | 4 + ...x1_nested_region_same_entity.verdicts.json | 21 + .../typestate_ef_orderbackend.verdicts.json | 4 + ...tate_g6_capability_not_spent.verdicts.json | 21 + ...ypestate_l1_stale_capability.verdicts.json | 32 + ...pestate_l2_copied_capability.verdicts.json | 21 + ...state_l3_raw_entity_mutation.verdicts.json | 21 + .../typestate_l3b_second_region.verdicts.json | 21 + ...e_l4_raw_entity_unknown_call.verdicts.json | 4 + ...e_l4b_raw_entity_shared_call.verdicts.json | 21 + ...tate_l4c_raw_entity_consumed.verdicts.json | 21 + ...state_l4d_token_unknown_call.verdicts.json | 4 + ...pestate_l5_normal_transition.verdicts.json | 4 + ...ypestate_l5b_two_transitions.verdicts.json | 4 + ...estate_l5c_read_after_region.verdicts.json | 4 + ..._token_spent_on_other_entity.verdicts.json | 4 + ...ate_n2_two_tokens_one_region.verdicts.json | 4 + ...raw_mutation_inferred_effect.verdicts.json | 4 + ...ate_n4_token_outlives_region.verdicts.json | 4 + .../typestate_w1_loan_wordings.verdicts.json | 101 +++ ...tate_w2_loan_wordings_pooled.verdicts.json | 101 +++ ...t_boundary_effect_line_negative.facts.json | 2 +- ...verdict_boundary_line_above_u32.facts.json | 2 +- .../verdict_boundary_line_negative.facts.json | 2 +- ..._boundary_service_line_negative.facts.json | 2 +- .../verdict_column_sort_order.facts.json | 2 +- .../verdict_dedup_key_members.facts.json | 2 +- ...verdict_dedup_needs_the_message.facts.json | 2 +- .../verdict_degenerate_metadata.facts.json | 2 +- .../verdict_di004_call_site_anchor.facts.json | 2 +- ...verdict_di005_cache_site_anchor.facts.json | 2 +- ...ct_di_duplicate_sites_last_wins.facts.json | 2 +- ...erdict_di_evidence_degradations.facts.json | 2 +- .../verdict_di_graph_families.facts.json | 2 +- ...ict_di_tolerant_typed_coercions.facts.json | 2 +- ...verdict_domain_tolerant_readers.facts.json | 2 +- ...ct_door_effect_deps_not_strings.facts.json | 2 +- ...t_door_service_unknown_lifetime.facts.json | 2 +- .../verdict_eff001_slice_dropped.facts.json | 2 +- .../verdict_eff001_storm_and_memo.facts.json | 2 +- .../verdict_flow_local_wordings.facts.json | 2 +- .../verdict_multi_file_ordering.facts.json | 2 +- ...ict_own014_escape_slice_dropped.facts.json | 2 +- .../verdict_own014_wordings.facts.json | 2 +- .../verdicts/verdict_own051_gates.facts.json | 2 +- .../verdict_pool_view_anchor.facts.json | 2 +- ...rotocol_absent_rules_are_silent.facts.json | 2 +- ...ol_barrier_maybe_two_step_slice.facts.json | 2 +- ..._protocol_beside_other_families.facts.json | 2 +- ...rotocol_exit_codes_and_wordings.facts.json | 2 +- ...otocol_identity_and_line_guards.facts.json | 2 +- ...rdict_protocol_obl005_dead_rule.facts.json | 2 +- ...ct_protocol_tolerant_door_rules.facts.json | 2 +- ...rdict_same_site_distinct_events.facts.json | 2 +- ..._severity_tiers_and_suppression.facts.json | 2 +- .../verdict_skip_list_artifacts.facts.json | 2 +- ...erdict_subscription_lambda_note.facts.json | 2 +- .../verdict_unknown_line_guards.facts.json | 2 +- tests/goldens/fix_candidates_off.golden.json | 2 +- tests/test_effects.py | 6 +- tests/test_fix_candidates.py | 2 +- tests/test_obligations.py | 14 +- tests/test_ownir.py | 23 +- tests/test_ownir_column.py | 13 +- tests/test_ownir_defensive_limits.py | 20 +- tests/test_ownir_input_boundary.py | 12 +- tests/test_repro_fixtures.py | 2 +- tests/test_stage3_cancellation.py | 2 +- tests/test_unreachable_branch_probe.py | 2 +- tests/test_verify_delta.py | 2 +- tests/verdict_surface_inventory.py | 19 +- 430 files changed, 23885 insertions(+), 977 deletions(-) create mode 100644 tests/fixtures/lowered/typestate_cs_a1_alias_of_entity.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_a1_alias_of_entity.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_a2_entity_through_indexer.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_a2_entity_through_indexer.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_g6_capability_not_spent.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_g6_capability_not_spent.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l10_region_inside_try.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l10_region_inside_try.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l11_region_beside_disposable.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l11_region_beside_disposable.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l12_region_beside_leaked_disposable.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l12_region_beside_leaked_disposable.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l13_values_captured_into_region.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l13_values_captured_into_region.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l1_stale_capability.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l1_stale_capability.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l2_copied_capability.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l2_copied_capability.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l3_raw_mutation.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l3_raw_mutation.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l4_raw_escape.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l4_raw_escape.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l5_normal_transition.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l5_normal_transition.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l6_two_transitions.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l6_two_transitions.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l6b_chained_transitions.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l6b_chained_transitions.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l7_read_after_region.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l7_read_after_region.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l8_branch_inside_region.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l8_branch_inside_region.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_l9_opaque_calls_around_region.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_l9_opaque_calls_around_region.golden.json create mode 100644 tests/fixtures/lowered/typestate_cs_x1_nested_region_same_entity.facts.json create mode 100644 tests/fixtures/lowered/typestate_cs_x1_nested_region_same_entity.golden.json create mode 100644 tests/fixtures/lowered/typestate_ef_orderbackend.facts.json create mode 100644 tests/fixtures/lowered/typestate_ef_orderbackend.golden.json create mode 100644 tests/fixtures/lowered/typestate_g6_capability_not_spent.facts.json create mode 100644 tests/fixtures/lowered/typestate_g6_capability_not_spent.golden.json create mode 100644 tests/fixtures/lowered/typestate_l1_stale_capability.facts.json create mode 100644 tests/fixtures/lowered/typestate_l1_stale_capability.golden.json create mode 100644 tests/fixtures/lowered/typestate_l2_copied_capability.facts.json create mode 100644 tests/fixtures/lowered/typestate_l2_copied_capability.golden.json create mode 100644 tests/fixtures/lowered/typestate_l3_raw_entity_mutation.facts.json create mode 100644 tests/fixtures/lowered/typestate_l3_raw_entity_mutation.golden.json create mode 100644 tests/fixtures/lowered/typestate_l3b_second_region.facts.json create mode 100644 tests/fixtures/lowered/typestate_l3b_second_region.golden.json create mode 100644 tests/fixtures/lowered/typestate_l4_raw_entity_unknown_call.facts.json create mode 100644 tests/fixtures/lowered/typestate_l4_raw_entity_unknown_call.golden.json create mode 100644 tests/fixtures/lowered/typestate_l4b_raw_entity_shared_call.facts.json create mode 100644 tests/fixtures/lowered/typestate_l4b_raw_entity_shared_call.golden.json create mode 100644 tests/fixtures/lowered/typestate_l4c_raw_entity_consumed.facts.json create mode 100644 tests/fixtures/lowered/typestate_l4c_raw_entity_consumed.golden.json create mode 100644 tests/fixtures/lowered/typestate_l4d_token_unknown_call.facts.json create mode 100644 tests/fixtures/lowered/typestate_l4d_token_unknown_call.golden.json create mode 100644 tests/fixtures/lowered/typestate_l5_normal_transition.facts.json create mode 100644 tests/fixtures/lowered/typestate_l5_normal_transition.golden.json create mode 100644 tests/fixtures/lowered/typestate_l5b_two_transitions.facts.json create mode 100644 tests/fixtures/lowered/typestate_l5b_two_transitions.golden.json create mode 100644 tests/fixtures/lowered/typestate_l5c_read_after_region.facts.json create mode 100644 tests/fixtures/lowered/typestate_l5c_read_after_region.golden.json create mode 100644 tests/fixtures/lowered/typestate_n1_token_spent_on_other_entity.facts.json create mode 100644 tests/fixtures/lowered/typestate_n1_token_spent_on_other_entity.golden.json create mode 100644 tests/fixtures/lowered/typestate_n2_two_tokens_one_region.facts.json create mode 100644 tests/fixtures/lowered/typestate_n2_two_tokens_one_region.golden.json create mode 100644 tests/fixtures/lowered/typestate_n3_raw_mutation_inferred_effect.facts.json create mode 100644 tests/fixtures/lowered/typestate_n3_raw_mutation_inferred_effect.golden.json create mode 100644 tests/fixtures/lowered/typestate_n4_token_outlives_region.facts.json create mode 100644 tests/fixtures/lowered/typestate_n4_token_outlives_region.golden.json create mode 100644 tests/fixtures/lowered/typestate_w1_loan_wordings.facts.json create mode 100644 tests/fixtures/lowered/typestate_w1_loan_wordings.golden.json create mode 100644 tests/fixtures/lowered/typestate_w2_loan_wordings_pooled.facts.json create mode 100644 tests/fixtures/lowered/typestate_w2_loan_wordings_pooled.golden.json create mode 100644 tests/fixtures/summaries/typestate_cs_a1_alias_of_entity.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_a2_entity_through_indexer.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_g6_capability_not_spent.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l10_region_inside_try.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l11_region_beside_disposable.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l12_region_beside_leaked_disposable.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l13_values_captured_into_region.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l1_stale_capability.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l2_copied_capability.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l3_raw_mutation.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l4_raw_escape.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l5_normal_transition.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l6_two_transitions.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l6b_chained_transitions.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l7_read_after_region.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l8_branch_inside_region.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_l9_opaque_calls_around_region.summaries.json create mode 100644 tests/fixtures/summaries/typestate_cs_x1_nested_region_same_entity.summaries.json create mode 100644 tests/fixtures/summaries/typestate_ef_orderbackend.summaries.json create mode 100644 tests/fixtures/summaries/typestate_g6_capability_not_spent.summaries.json create mode 100644 tests/fixtures/summaries/typestate_l1_stale_capability.summaries.json create mode 100644 tests/fixtures/summaries/typestate_l2_copied_capability.summaries.json create mode 100644 tests/fixtures/summaries/typestate_l3_raw_entity_mutation.summaries.json create mode 100644 tests/fixtures/summaries/typestate_l3b_second_region.summaries.json create mode 100644 tests/fixtures/summaries/typestate_l4_raw_entity_unknown_call.summaries.json create mode 100644 tests/fixtures/summaries/typestate_l4b_raw_entity_shared_call.summaries.json create mode 100644 tests/fixtures/summaries/typestate_l4c_raw_entity_consumed.summaries.json create mode 100644 tests/fixtures/summaries/typestate_l4d_token_unknown_call.summaries.json create mode 100644 tests/fixtures/summaries/typestate_l5_normal_transition.summaries.json create mode 100644 tests/fixtures/summaries/typestate_l5b_two_transitions.summaries.json create mode 100644 tests/fixtures/summaries/typestate_l5c_read_after_region.summaries.json create mode 100644 tests/fixtures/summaries/typestate_n1_token_spent_on_other_entity.summaries.json create mode 100644 tests/fixtures/summaries/typestate_n2_two_tokens_one_region.summaries.json create mode 100644 tests/fixtures/summaries/typestate_n3_raw_mutation_inferred_effect.summaries.json create mode 100644 tests/fixtures/summaries/typestate_n4_token_outlives_region.summaries.json create mode 100644 tests/fixtures/summaries/typestate_w1_loan_wordings.summaries.json create mode 100644 tests/fixtures/summaries/typestate_w2_loan_wordings_pooled.summaries.json create mode 100644 tests/fixtures/verdicts/typestate_cs_a1_alias_of_entity.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_a2_entity_through_indexer.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_g6_capability_not_spent.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l10_region_inside_try.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l11_region_beside_disposable.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l12_region_beside_leaked_disposable.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l13_values_captured_into_region.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l1_stale_capability.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l2_copied_capability.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l3_raw_mutation.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l4_raw_escape.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l5_normal_transition.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l6_two_transitions.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l6b_chained_transitions.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l7_read_after_region.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l8_branch_inside_region.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_l9_opaque_calls_around_region.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_cs_x1_nested_region_same_entity.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_ef_orderbackend.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_g6_capability_not_spent.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_l1_stale_capability.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_l2_copied_capability.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_l3_raw_entity_mutation.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_l3b_second_region.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_l4_raw_entity_unknown_call.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_l4b_raw_entity_shared_call.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_l4c_raw_entity_consumed.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_l4d_token_unknown_call.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_l5_normal_transition.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_l5b_two_transitions.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_l5c_read_after_region.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_n1_token_spent_on_other_entity.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_n2_two_tokens_one_region.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_n3_raw_mutation_inferred_effect.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_n4_token_outlives_region.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_w1_loan_wordings.verdicts.json create mode 100644 tests/fixtures/verdicts/typestate_w2_loan_wordings_pooled.verdicts.json diff --git a/audit/static/run_static.py b/audit/static/run_static.py index 2a63eff6..b884675c 100755 --- a/audit/static/run_static.py +++ b/audit/static/run_static.py @@ -363,7 +363,7 @@ def check(ok: bool, msg: str) -> None: # total derives from the call count def _fake_own_check(target_, out_dir_, severity_, root=None): facts = Path(out_dir_) / "own-check.facts.json" - facts.write_text(json.dumps({"ownir_version": 0, "module": "App", "components": [ + facts.write_text(json.dumps({"ownir_version": 1, "module": "App", "components": [ {"name": "CustomerView", "file": "Views/CustomerView.xaml.cs", "subscriptions": [ {"event": "_bus.Changed", "handler": "OnChanged", "line": 21, "released": False}]}]}), encoding="utf-8") @@ -419,7 +419,7 @@ def _fake_own_check(target_, out_dir_, severity_, root=None): ' x:Class="App.Views.CustomerView" Loaded="OnLoaded" />\n', encoding="utf-8") (out5 / "own-check.facts.json").write_text(json.dumps({ - "ownir_version": 0, "module": "Stale", "components": [ + "ownir_version": 1, "module": "Stale", "components": [ {"name": "CustomerView", "file": "Views/CustomerView.xaml.cs", "subscriptions": [{"event": "_bus.Changed", "handler": "OnChanged", "line": 21, "released": False}]}]}), encoding="utf-8") diff --git a/audit/static/tools/xaml_join.py b/audit/static/tools/xaml_join.py index 98a36edf..82e91691 100644 --- a/audit/static/tools/xaml_join.py +++ b/audit/static/tools/xaml_join.py @@ -303,7 +303,7 @@ def check(ok: bool, msg: str) -> None: "event_handlers": [{"event": "Loaded", "handler": "OnLoaded", "line": 4}], "bindings": [], "named_elements": []}, ]} - ownir = {"ownir_version": 0, "module": "App", "components": [ + ownir = {"ownir_version": 1, "module": "App", "components": [ {"name": "CustomerView", "file": "Views/CustomerView.xaml.cs", "subscriptions": [ {"event": "_bus.Changed", "handler": "OnChanged", "line": 21, "released": False}]}, {"name": "CleanView", "file": "Views/CleanView.xaml.cs", "subscriptions": [ @@ -363,7 +363,7 @@ def check(ok: bool, msg: str) -> None: wrong_ns = {"documents": [{"file": "Features/Billing/CustomerView.xaml", "x_class": "Billing.CustomerView", "event_handlers": [{"event": "Loaded", "handler": "OnLoaded", "line": 4}]}]} - other = {"ownir_version": 0, "components": [ + other = {"ownir_version": 1, "components": [ {"name": "CustomerView", "file": "Legacy/CustomerView.cs", "subscriptions": [ {"event": "_bus.Changed", "handler": "OnChanged", "line": 9, "released": False}]}]} check(join(wrong_ns, other) == [], diff --git a/docs/generated/p022-coord-census.md b/docs/generated/p022-coord-census.md index 3f4a526a..add32e71 100644 --- a/docs/generated/p022-coord-census.md +++ b/docs/generated/p022-coord-census.md @@ -10,8 +10,8 @@ Value classes follow the cp1 taxonomy's axis rather than blurring it: `outside-i | measure | value | |------------------------------------|------:| -| JSON files scanned | 438 | -| coordinate slots found | 2197 | +| JSON files scanned | 586 | +| coordinate slots found | 3943 | ## By value class @@ -21,12 +21,12 @@ Value classes follow the cp1 taxonomy's axis rather than blurring it: `outside-i | `below-1` | 19 | 19 | | `bool` | 14 | 14 | | `float` | 2 | 2 | -| `in-domain` | 1752 | 957 | +| `in-domain` | 3272 | 2152 | | `negative` | 23 | 23 | -| `null` | 227 | 7 | +| `null` | 255 | 7 | | `outside-int64` | 14 | 14 | | `string` | 17 | 17 | -| `zero` | 108 | 25 | +| `zero` | 306 | 25 | ## By family and slot @@ -142,10 +142,11 @@ Value classes follow the cp1 taxonomy's axis rather than blurring it: `outside-i | `cli_ownir/inputs` | `components[].subscriptions[].line` | `in-domain` | yes | 3 | 3 | — | | `cli_ownir/inputs/pa th ünïcødé` | `components[].subscriptions[].line` | `in-domain` | yes | 1 | 1 | — | | `lowered` | `components[].subscriptions[].line` | `in-domain` | yes | 24 | 10 | — | -| `lowered` | `functions[].[].line` | `in-domain` | yes | 150 | 41 | — | -| `lowered` | `functions[].params[].line` | `in-domain` | yes | 20 | 10 | — | +| `lowered` | `functions[].[].column` | `in-domain` | yes | 2 | 2 | — | +| `lowered` | `functions[].[].line` | `in-domain` | yes | 903 | 112 | — | +| `lowered` | `functions[].params[].line` | `in-domain` | yes | 460 | 79 | — | | `lowered` | `functions[].params[].line` | `zero` | yes | 4 | 3 | — | -| `lowered` | `handles[].line` | `in-domain` | — | 48 | 22 | — | +| `lowered` | `handles[].line` | `in-domain` | — | 343 | 56 | — | | `lowered` | `services[].line` | `in-domain` | yes | 2 | 1 | — | | `ownir` | `components[].subscriptions[].column` | `in-domain` | yes | 2 | 1 | — | | `ownir` | `components[].subscriptions[].line` | `in-domain` | yes | 20 | 10 | — | @@ -181,7 +182,7 @@ Value classes follow the cp1 taxonomy's axis rather than blurring it: `outside-i | `repro` | `traces[].layers[].steps[].value.params[].line` | `in-domain` | — | 4 | 1 | — | | `summaries` | `functions[].[].line` | `in-domain` | yes | 37 | 9 | — | | `summaries` | `functions[].params[].line` | `in-domain` | yes | 25 | 8 | — | -| `summaries` | `summaries[].line` | `zero` | — | 54 | 26 | — | +| `summaries` | `summaries[].line` | `zero` | — | 252 | 63 | — | | `verdict_renders` | `components[].subscriptions[].column` | `in-domain` | yes | 1 | 1 | — | | `verdict_renders` | `components[].subscriptions[].line` | `in-domain` | yes | 11 | 5 | — | | `verdict_renders` | `functions[].[].line` | `in-domain` | yes | 3 | 2 | — | @@ -199,9 +200,9 @@ Value classes follow the cp1 taxonomy's axis rather than blurring it: `outside-i | `verdicts` | `effects[].line` | `in-domain` | yes | 9 | 5 | — | | `verdicts` | `effects[].line` | `negative` | yes | 1 | 1 | `-3` | | `verdicts` | `effects[].line` | `zero` | yes | 1 | 1 | — | -| `verdicts` | `findings[].column` | `in-domain` | — | 14 | 5 | — | -| `verdicts` | `findings[].column` | `null` | — | 168 | 74 | — | -| `verdicts` | `findings[].line` | `in-domain` | — | 168 | 69 | — | +| `verdicts` | `findings[].column` | `in-domain` | — | 15 | 6 | — | +| `verdicts` | `findings[].column` | `null` | — | 196 | 92 | — | +| `verdicts` | `findings[].line` | `in-domain` | — | 197 | 88 | — | | `verdicts` | `findings[].line` | `zero` | — | 14 | 11 | — | | `verdicts` | `functions[].[].column` | `below-1` | yes | 1 | 1 | `0` | | `verdicts` | `functions[].[].column` | `in-domain` | yes | 7 | 2 | — | diff --git a/docs/generated/p022-cp4-census.md b/docs/generated/p022-cp4-census.md index 26ced391..76f1cbb1 100644 --- a/docs/generated/p022-cp4-census.md +++ b/docs/generated/p022-cp4-census.md @@ -8,18 +8,18 @@ Computed by `tests/verdict_census.py` and `tests/verdict_render_census.py` (the | measure | value | |-------------------------------------------------------------------------|------:| -| goldens — Python's complete truth, one per planned case | 95 | +| goldens — Python's complete truth, one per planned case | 132 | | … swept from `tests/fixtures/ownir` | 22 | -| … swept from `tests/fixtures/lowered` | 27 | +| … swept from `tests/fixtures/lowered` | 64 | | … swept from `tests/fixtures/summaries` | 9 | | … synthetic controls (`manifest.json` cases) | 37 | -| reference refusals over all goldens | 5 | -| reference findings over all goldens | 182 | +| reference refusals over all goldens | 9 | +| reference findings over all goldens | 211 | | declared Rust exclusions — the executable ledger `rust_replay_excluded` | 2 | | … refused at the typed `OwnIr` door (#294 OD-1) | 2 | -| replayed by Rust (goldens minus exclusions) | 93 | -| … reference refusals among them (compared in full) | 5 | -| … findings among them (compared on every `Finding` member) | 180 | +| replayed by Rust (goldens minus exclusions) | 130 | +| … reference refusals among them (compared in full) | 9 | +| … findings among them (compared on every `Finding` member) | 209 | The differential counts over the replayed set — Python-only, Rust-only, changed, ordering-only, unexplained — are asserted, not measured here: the Rust replay compares every replayed case's full ordered verdict list (or its refusal text) against the golden on every member, collects every divergence without fail-fast, and fails if one exists. A green `cargo test -p own-bridge --test verdicts` is 0 / 0 / 0 / 0 / 0 by construction; a non-zero count is a red build. diff --git a/docs/generated/p022-cp5-inventory.md b/docs/generated/p022-cp5-inventory.md index 4e461122..42d13c0c 100644 --- a/docs/generated/p022-cp5-inventory.md +++ b/docs/generated/p022-cp5-inventory.md @@ -12,17 +12,29 @@ Checkpoint 4 proved identity, anchor, kind and tiering over the replayed set ([c | ledger row | surface | what it is | all goldens | replayed | |---|---|---|---:|---:| -| `flowlocal_own001_never` | bridge | flow-local OWN001, never released | 27 | 27 | +| `flowlocal_own001_never` | bridge | flow-local OWN001, never released | 30 | 30 | | `flowlocal_own001_partial` | bridge | flow-local OWN001, released on some path | 10 | 10 | | `flowlocal_own001_pool_partial` | bridge | flow-local OWN001 on a pooled buffer, returned on some path | 2 | 2 | -| `flowlocal_own002` | bridge | flow-local use-after-dispose | 6 | 6 | +| `flowlocal_own002` | bridge | flow-local use-after-dispose | 8 | 8 | | `flowlocal_own003` | bridge | flow-local double dispose | 1 | 1 | -| `flowlocal_own009` | bridge | flow-local maybe-use-after-dispose | 1 | 1 | +| `flowlocal_own009` | bridge | flow-local maybe-use-after-dispose | 2 | 2 | | `flowlocal_own002_pool` | bridge | flow-local use-after-return | 1 | 1 | | `flowlocal_own003_pool` | bridge | flow-local double return | 1 | 1 | | `flowlocal_own009_pool` | bridge | flow-local maybe-use-after-return | 1 | 1 | -| `flowlocal_fallback` | core-diagnostic | flow-local fallback: the core message, verbatim — **not replayed**: unreachable from any facts document (the nine-op flow vocabulary raises only codes that HAVE a wording); pinned by a `verdict::tests` control driven through `map_core`, whose expected text is read from the recorded probe `tests/fixtures/unreachable_branches.json`. The core message it interpolates is checkpoint 5.2's | 0 | 0 | -| `flowlocal_fallback_pool` | core-diagnostic | flow-local pooled fallback: the core message, verbatim — **not replayed**: unreachable from any facts document (the nine-op flow vocabulary raises only codes that HAVE a wording); pinned by a `verdict::tests` control driven through `map_core`, whose expected text is read from the recorded probe `tests/fixtures/unreachable_branches.json`. The core message it interpolates is checkpoint 5.2's | 0 | 0 | +| `flowlocal_own005` | bridge | flow-local use after move | 3 | 3 | +| `flowlocal_own007` | bridge | flow-local consume/return while borrowed | 2 | 2 | +| `flowlocal_own008` | bridge | flow-local release while borrowed | 1 | 1 | +| `flowlocal_own011` | bridge | flow-local exclusive re-borrow | 4 | 4 | +| `flowlocal_own012` | bridge | flow-local shared borrow under an exclusive one | 2 | 2 | +| `flowlocal_own013` | bridge | flow-local direct use under an exclusive borrow | 5 | 5 | +| `flowlocal_own005_pool` | bridge | flow-local use after move on a pooled buffer | 1 | 1 | +| `flowlocal_own007_pool` | bridge | flow-local consume/return while borrowed on a pooled buffer | 1 | 1 | +| `flowlocal_own008_pool` | bridge | flow-local release while borrowed on a pooled buffer | 1 | 1 | +| `flowlocal_own011_pool` | bridge | flow-local exclusive re-borrow on a pooled buffer | 1 | 1 | +| `flowlocal_own012_pool` | bridge | flow-local shared borrow under an exclusive one on a pooled buffer | 1 | 1 | +| `flowlocal_own013_pool` | bridge | flow-local direct use under an exclusive borrow on a pooled buffer | 1 | 1 | +| `flowlocal_fallback` | core-diagnostic | flow-local fallback: the core message, verbatim — **not replayed**: unreachable from any facts document (the eleven-op flow vocabulary raises only codes that HAVE a wording); pinned by a `verdict::tests` control driven through `map_core`, whose expected text is read from the recorded probe `tests/fixtures/unreachable_branches.json`. The core message it interpolates is checkpoint 5.2's | 0 | 0 | +| `flowlocal_fallback_pool` | core-diagnostic | flow-local pooled fallback: the core message, verbatim — **not replayed**: unreachable from any facts document (the eleven-op flow vocabulary raises only codes that HAVE a wording); pinned by a `verdict::tests` control driven through `map_core`, whose expected text is read from the recorded probe `tests/fixtures/unreachable_branches.json`. The core message it interpolates is checkpoint 5.2's | 0 | 0 | | `own025_view` | bridge | OWN025 pooled-view wording | 2 | 2 | | `own014_di_singleton` | bridge | OWN014 captive, source registered singleton | 6 | 6 | | `own014_di_scoped` | bridge | OWN014 captive, source registered scoped | 1 | 1 | @@ -88,9 +100,9 @@ One row per `related`/`flow` family; a slice matching no family (or two) fails t | `di005_registration_related` | related | DI005 registration beside the store site | 4 | 4 | | `capture_escape_flow` | flow | OWN014 subscribe site → source registration site | 5 | 5 | | `effect_flow` | flow | EFF001 re-run site → identity-mint site | 5 | 4 | -| `flowlocal_flow_own002` | flow | flow-local OWN002 origin → violation | 4 | 4 | +| `flowlocal_flow_own002` | flow | flow-local OWN002 origin → violation | 6 | 6 | | `flowlocal_flow_own003` | flow | flow-local OWN003 origin → violation | 1 | 1 | -| `flowlocal_flow_own009` | flow | flow-local OWN009 origin → violation | 1 | 1 | +| `flowlocal_flow_own009` | flow | flow-local OWN009 origin → violation | 2 | 2 | | `flowlocal_flow_own002_pool` | flow | flow-local OWN002 origin → violation (pooled) | 1 | 1 | | `flowlocal_flow_own003_pool` | flow | flow-local OWN003 origin → violation (pooled) | 1 | 1 | | `flowlocal_flow_own009_pool` | flow | flow-local OWN009 origin → violation (pooled) | 1 | 1 | @@ -113,7 +125,7 @@ The rules that produce an EMPTY slice: a step whose line is unknown is omitted, | `capture_escape_flow_dropped` | flow | DI-sourced OWN014 with no escape slice (source registration unknown → < 2 steps) | 3 | 3 | | `capture_flow_absent` | flow | OWN014 from the capture route: no escape slice by design (only the DI-sourced branch builds one) | 6 | 6 | | `effect_flow_dropped` | flow | EFF001 with no slice (a re-run or mint line < 1) | 3 | 3 | -| `flowlocal_flow_absent` | flow | OWN001 on a local/pooled record: a single-point finding, no slice by design | 45 | 45 | +| `flowlocal_flow_absent` | flow | OWN001 on a local/pooled record: a single-point finding, no slice by design | 48 | 48 | | `protocol_flow_absent` | flow | an OBL finding with no slice at all: the anchorless OBL005 by design, and a leak whose only step has an unknown line | 4 | 4 | ## BR-V9 — rendered surfaces diff --git a/docs/generated/p022-shadow-census.md b/docs/generated/p022-shadow-census.md index d6d34f20..0fbd656e 100644 --- a/docs/generated/p022-shadow-census.md +++ b/docs/generated/p022-shadow-census.md @@ -24,12 +24,12 @@ acceptance work. | corpus | documents | |---|---| -| `tests/fixtures/lowered` | 27 | +| `tests/fixtures/lowered` | 64 | | `tests/fixtures/ownir` | 22 | | `tests/fixtures/repro` | 3 | | `tests/fixtures/summaries` | 9 | | `tests/fixtures/verdicts` | 37 | -| **total** | **98** | +| **total** | **135** | Every one of those documents is canonicalized and hashed by the reference (`ownlang/repro.py`) and re-hashed from the same file by the port @@ -43,8 +43,8 @@ refuses to carry a foreign entry that has none rather than filling one in. | surface | count | |---|---| -| documents captured and digest-pinned | 98 | -| tamper controls (one changed character per document, refusal required) | 98 | +| documents captured and digest-pinned | 135 | +| tamper controls (one changed character per document, refusal required) | 135 | | documents both engines must REFUSE to name (`domain_refusals`) | 6 | | reproduction artifacts committed and replayed byte-for-byte | 10 | | structural negative controls on `verify` (each side) | 34 | diff --git a/frontend/ownts/ownts.py b/frontend/ownts/ownts.py index a93a92cd..06ee6b70 100644 --- a/frontend/ownts/ownts.py +++ b/frontend/ownts/ownts.py @@ -577,7 +577,7 @@ def extract(path: str) -> list[Component]: def to_ownir(comps: list[Component], module: str, effects: list[dict] | None = None) -> dict: facts = { - "ownir_version": 0, + "ownir_version": 1, "module": module, "components": [ { diff --git a/frontend/roslyn/OwnSharp.Extractor/Program.cs b/frontend/roslyn/OwnSharp.Extractor/Program.cs index 3d64e9c2..18f91faf 100644 --- a/frontend/roslyn/OwnSharp.Extractor/Program.cs +++ b/frontend/roslyn/OwnSharp.Extractor/Program.cs @@ -7219,7 +7219,7 @@ or ImplicitObjectCreationExpressionSyntax } init object facts = emitFixCandidates ? new { - ownir_version = 0, + ownir_version = 1, fix_candidates_version = 1, module = "Extracted", components, @@ -7229,7 +7229,7 @@ or ImplicitObjectCreationExpressionSyntax } init } : new { - ownir_version = 0, + ownir_version = 1, module = "Extracted", components, services = factServices, diff --git a/ownlang/analysis.py b/ownlang/analysis.py index 17c6f78e..ae1634f0 100644 --- a/ownlang/analysis.py +++ b/ownlang/analysis.py @@ -534,7 +534,8 @@ def step(self, ins: Instr, st: State) -> None: if mut: self.err("OWN013", f"cannot use '{ins.sym.name}' directly while it " - f"is mutably borrowed", ins.line) + f"is mutably borrowed", ins.line, + subject=ins.sym.origin) elif ins.sym.kind == Kind.BORROW: if not self.binding_live(st, ins.sym): self.err("OWN004", @@ -641,7 +642,8 @@ def _consume_like(self, st: State, sym: Symbol, verb: str, line: int, shared, mut = self.loans_on(st, sym) if shared or mut: self.err(code_borrowed, - f"cannot {verb} '{sym.name}' while it is borrowed", line) + f"cannot {verb} '{sym.name}' while it is borrowed", line, + subject=sym.origin) def _check_mut_borrowable(self, st: State, owner: Symbol, line: int) -> None: if self._state_problem(st, owner, "mutably borrow", line): @@ -650,11 +652,11 @@ def _check_mut_borrowable(self, st: State, owner: Symbol, line: int) -> None: if shared: self.err("OWN006", f"cannot mutably borrow '{owner.name}': a shared borrow is " - f"live", line) + f"live", line, subject=owner.origin) elif mut: self.err("OWN011", f"cannot mutably borrow '{owner.name}': it is already " - f"mutably borrowed", line) + f"mutably borrowed", line, subject=owner.origin) def _check_shared_borrowable(self, st: State, owner: Symbol, line: int) -> None: if self._state_problem(st, owner, "borrow", line): @@ -663,7 +665,7 @@ def _check_shared_borrowable(self, st: State, owner: Symbol, line: int) -> None: if mut: self.err("OWN012", f"cannot share-borrow '{owner.name}': it is mutably " - f"borrowed", line) + f"borrowed", line, subject=owner.origin) def _apply_effect(self, st: State, sym: Symbol, eff: Effect, callee: str, line: int) -> None: diff --git a/ownlang/lowered.py b/ownlang/lowered.py index c3f31c84..dfb7e2e9 100644 --- a/ownlang/lowered.py +++ b/ownlang/lowered.py @@ -67,6 +67,8 @@ from .ast_nodes import ( Acquire, AliasJoin, + BorrowBlock, + BorrowKind, Call, ExternDecl, FnDecl, @@ -74,6 +76,7 @@ Let, LifetimeDecl, Module, + Move, Overspan, Param, Release, @@ -90,7 +93,7 @@ # The Layer 2 surface version. Bump on ANY normalization change above — the # committed goldens and the Rust replay are both keyed to it. -LOWERED_VERSION = 1 +LOWERED_VERSION = 2 # The handle-map key allowlist, in serialization order (see the docstring). _HANDLE_KEYS = ( @@ -114,6 +117,16 @@ def _arg(a: Any) -> str: def _stmt(s: Stmt) -> dict[str, Any]: + if isinstance(s, Let) and isinstance(s.rhs, Move): + return {"stmt": "move", "handle": s.name, "src": s.rhs.var, + "line": s.line} + if isinstance(s, BorrowBlock): + if s.kind is not BorrowKind.MUT: + raise ValueError( + "unprojectable shared BorrowBlock — the bridge lowers only " + "`borrow_mut`; a new shape must extend the Layer 2 contract") + return {"stmt": "borrow_mut", "owner": s.owner, "binding": s.binding, + "body": [_stmt(x) for x in s.body], "line": s.line} if isinstance(s, Let): if not isinstance(s.rhs, Acquire): raise ValueError( diff --git a/ownlang/ownir.py b/ownlang/ownir.py index 78ffd55f..fabf1a3e 100644 --- a/ownlang/ownir.py +++ b/ownlang/ownir.py @@ -9,7 +9,7 @@ OwnIR schema (JSON):: { - "ownir_version": 0, + "ownir_version": 1, "module": "WpfApp", "components": [ { @@ -110,6 +110,8 @@ from .ast_nodes import ( Acquire, AliasJoin, + BorrowBlock, + BorrowKind, Call, Effect, EffectParam, @@ -120,6 +122,7 @@ Let, LifetimeDecl, Module, + Move, Overspan, Param, Release, @@ -178,7 +181,7 @@ # vocabulary changes incompatibly; the extractor stamps the same number so a # mismatched extractor/core pair fails loudly (see load()) instead of silently # mis-reading facts. -OWNIR_VERSION = 0 +OWNIR_VERSION = 1 class OwnIRError(ValueError): @@ -337,6 +340,7 @@ def _route_resource(rkind: str) -> tuple[str, str]: _FLOW_OPS = frozenset({ "acquire", "release", "use", "overspan", "return", "alias_join", "call", "if", "while", + "move", "borrow_mut", }) # The parameter ownership-effect vocabulary (P-006/2b): a method contract's @@ -1319,7 +1323,7 @@ def to_module(facts: dict[str, Any], # silent (honest-skip; TZ D5): record the reason for the caller to surface # as an advisory OWN052. try: - mos: dict[str, Any] = solve(_build_skeletons(raw_fns)) + mos: dict[str, Any] = solve(_build_skeletons(_inference_view(raw_fns))) except Exception as exc: mos = {} if notes is not None: @@ -1339,13 +1343,22 @@ def to_module(facts: dict[str, Any], # to an overloaded method still matches (CodeRabbit). overloaded = frozenset( n for n, c in Counter(_canonical_callee_name(x) for x in fp_names).items() if c > 1) + region_effects = (_region_param_effects(raw_fns, mos) + if any(isinstance(fn, dict) and _has_region(fn.get("body")) + for fn in raw_fns) else None) for fn in raw_fns: if not isinstance(fn, dict): continue fname = str(fn.get("name", f"Fn{loc[0]}")) ffile = str(fn.get("file", "?")) - nodes = fn.get("body", []) - nodes = nodes if isinstance(nodes, list) else [] + flow_nodes = fn.get("body", []) + flow_nodes = flow_nodes if isinstance(flow_nodes, list) else [] + # `nodes` is the INFERENCE view (regions spliced, see `_inline_regions`); + # `flow_nodes` keeps the structure and is read by `_lower_flow` alone. + # With no region the two are the same object. + has_region = _has_region(flow_nodes) + nodes = _inline_regions(flow_nodes) if has_region else flow_nodes + fn_view = {**fn, "body": nodes} if has_region else fn # which locals have ANY release in the body (any branch) — lets the OWN001 # wording distinguish "never disposed" (no release at all) from "not # disposed on every path" (released on some branch, leaked on another). @@ -1353,7 +1366,7 @@ def to_module(facts: dict[str, Any], # ownership contract params first (they seed `localmap` so the body's # uses/releases and call arguments resolve to them), then the flow body. localmap: dict[str, str] = {} - fparams = _lower_fn_params(fn, ffile, fname, handles, loc, localmap, + fparams = _lower_fn_params(fn_view, ffile, fname, handles, loc, localmap, released, mos) # The optimistic default (d5 §5), made real: an argument at a call # position whose contract resolved `may`/`unknown` means "we cannot @@ -1373,6 +1386,19 @@ def to_module(facts: dict[str, Any], unverified = _unverified_transfer_calls(nodes, mos) kill_sites = _kill_sites_for_unverified(nodes, mos) untracked = frozenset(a for a, _c, _t, _l in unverified) - kill_sites.keys() + if has_region and unverified: + # A function that opens an exclusive region must be verified end + # to end. The optimistic default ("ownership probably left the + # caller", advisory OWN051) would turn an unverifiable handoff + # into silence, and silence inside a protocol region reads as a + # PASS. Refuse instead — on both engines, identically. + arg, callee, transfer, cline = unverified[0] + raise OwnIRError( + f"OwnIR function {fname!r} opens a borrow_mut region but hands " + f"{arg!r} to {callee!r} at an unverified position (inferred " + f"contract: {transfer}) ({ffile}:{cline}) — refused: a function " + f"with an exclusive region must not rely on the optimistic " + f"ownership default") if advisories is not None and unverified: # OWN051, gated on args that actually carry an obligation here (an # acquired local or a fresh factory result) — a plain value passed @@ -1412,9 +1438,9 @@ def to_module(facts: dict[str, Any], "ever_released": hname in released, "pool": hpool} hoisted_lets.append(Let(hh, Acquire("Disposable", [], hline), hline)) fbody = [*hoisted_lets, - *_lower_flow(nodes, ffile, fname, handles, loc, localmap, + *_lower_flow(flow_nodes, ffile, fname, handles, loc, localmap, released, mos, set(hoist), first_party, overloaded, - untracked, kill_sites)] + untracked, kill_sites, False, region_effects)] # A body that returns a value gets an owned return type, so the core # models `return s` as a valid ESCAPE (the value is discharged to the # caller) instead of a void-return mismatch that would leave `s` looking @@ -1432,6 +1458,75 @@ def to_module(facts: dict[str, Any], handles) +def _has_region(nodes: Any) -> bool: + """Whether a flow body contains a `borrow_mut` region at any depth.""" + if not isinstance(nodes, list): + return False + for n in nodes: + if not isinstance(n, dict): + continue + op = n.get("op") + if op == "borrow_mut": + return True + if op == "if" and (_has_region(n.get("then")) or _has_region(n.get("else"))): + return True + if op == "while" and _has_region(n.get("body")): + return True + return False + + +def _inline_regions(nodes: Any) -> Any: + """The INFERENCE view of a flow body: every `borrow_mut` region spliced in + place of its block. + + A region is a straight-line sub-body that runs exactly once, so for every + question the contract-inference layer asks (is this param released on all + paths, which locals are acquired, does the body return a value, which + calls are unverified) the region is transparent. Only the flow lowering + (`_lower_flow`) needs the structure, because only it emits the loan. + + Identity is load-bearing twice over: a body with no region is returned AS + IS (so every pre-existing document takes the pre-existing path, byte for + byte), and the leaf op dicts are shared, never copied.""" + if not isinstance(nodes, list) or not _has_region(nodes): + return nodes + out: list[Any] = [] + for n in nodes: + if not isinstance(n, dict): + out.append(n) + continue + op = n.get("op") + if op == "borrow_mut": + sub = _inline_regions(n.get("body")) + if isinstance(sub, list): + out.extend(sub) + elif op == "if" and (_has_region(n.get("then")) or _has_region(n.get("else"))): + m = dict(n) + m["then"] = _inline_regions(n.get("then")) + m["else"] = _inline_regions(n.get("else")) + out.append(m) + elif op == "while" and _has_region(n.get("body")): + m = dict(n) + m["body"] = _inline_regions(n.get("body")) + out.append(m) + else: + out.append(n) + return out + + +def _inference_view(raw_fns: list[Any]) -> list[Any]: + """`functions[]` as the inference layer reads it (see `_inline_regions`). + A function with no region is the SAME object, so the common case is free + and unchanged.""" + out: list[Any] = [] + for fn in raw_fns: + if isinstance(fn, dict) and _has_region(fn.get("body")): + out.append({**fn, "body": _inline_regions(fn.get("body"))}) + else: + out.append(fn) + return out + + def _released_vars(nodes: list[Any]) -> set[str]: """The set of local names with at least one `release` op anywhere in a flow body (recursing into `if` branches and `while` bodies). Used to word an OWN001 as @@ -2346,6 +2441,45 @@ def _infer_param_effect(pname: str, nodes: Any, return None +def _region_param_effects(raw_fns: list[Any], + mos: dict[str, Any] | None) -> dict[str, list[str | None]]: + """For each function NAME, the effect each parameter resolves to — the + explicit `effect` when the fact carries one, the inferred one otherwise, + `None` when it resolves to a plain value. Exactly the resolution + `_lower_fn_params` applies, over the same inference view. + + Read by one rule only: inside a `borrow_mut` region a tracked local must + not land on a parameter that resolved PLAIN. The core does report that + shape (OWN041), but OWN041 is filtered as a synthesis artifact, so without + this the path is "core raised, bridge filtered, clean" — a mutation the + facts cannot see (the callee touches a plain entity, so its flow body is + empty and nothing is inferred) passing as verified.""" + out: dict[str, list[str | None]] = {} + for fn in _inference_view(raw_fns): + if not isinstance(fn, dict): + continue + fname = str(fn.get("name", "")) + raw = fn.get("params", []) + effs: list[str | None] = [] + if isinstance(raw, list): + summ = _mos_lookup(mos, fname, _call_sig(fn)) + for i, p in enumerate(raw): + if not isinstance(p, dict): + continue + eff = p.get("effect") + if not isinstance(eff, str): + ftrans = None + if summ is not None: + ps = next((q for q in summ.params if q.index == i), None) + if ps is not None: + ftrans = ps.transfer + eff = _infer_param_effect(str(p.get("name", "?")), + fn.get("body", []), ftrans) + effs.append(eff if isinstance(eff, str) and eff != "plain" else None) + out[fname] = effs + return out + + def _lower_fn_params(fn: dict[str, Any], ffile: str, fname: str, handles: dict[str, dict[str, Any]], loc: list[int], localmap: dict[str, str], @@ -2549,7 +2683,10 @@ def _lower_flow(nodes: list[Any], ffile: str, fname: str, first_party: frozenset[str] = frozenset(), overloaded: frozenset[str] = frozenset(), untracked: frozenset[str] = frozenset(), - kill_sites: dict[str, int] | None = None) -> list[Stmt]: + kill_sites: dict[str, int] | None = None, + in_region: bool = False, + param_effects: dict[str, list[str | None]] | None = None, + ) -> list[Stmt]: """Lower one OwnIR flow body (B0b/B2) into core statements. acquire/use/release/ return reference a C# local by name (`var`); `if` carries `then`/`else` sub-bodies; `while` carries a `body` (a back-edge — the core's worklist fixpoint @@ -2662,17 +2799,80 @@ def _lower_flow(nodes: list[Any], ffile: str, fname: str, en = n.get("else", []) then_b = _lower_flow(tn if isinstance(tn, list) else [], ffile, fname, handles, loc, localmap, released_vars, mos, hoisted, - first_party, overloaded, untracked, kill_sites) + first_party, overloaded, untracked, kill_sites, + in_region, param_effects) else_b = _lower_flow(en if isinstance(en, list) else [], ffile, fname, handles, loc, localmap, released_vars, mos, hoisted, - first_party, overloaded, untracked, kill_sites) + first_party, overloaded, untracked, kill_sites, + in_region, param_effects) body.append(If("?", then_b, else_b, line)) elif op == "while": bn = n.get("body", []) body_b = _lower_flow(bn if isinstance(bn, list) else [], ffile, fname, handles, loc, localmap, released_vars, mos, hoisted, - first_party, overloaded, untracked, kill_sites) + first_party, overloaded, untracked, kill_sites, + in_region, param_effects) body.append(While("?", body_b, line)) + elif op == "move": + # `var` takes OWNERSHIP of `src`'s obligation; `src` is dead after it + # (the core's `Let(Move)` — OWN005 on a later use of `src`). Unlike + # `alias_join` the source does not stay owning. Shaped exactly like + # the alias branch above: the destination's OLD binding dies first, + # and an untracked `src` makes no claim. + name = str(n.get("var", "?")) + src_h = localmap.get(str(n.get("src", ""))) + if name not in hoisted: + localmap.pop(name, None) + if src_h is not None and name not in hoisted and name not in untracked: + handle = f"loc_{loc[0]}" + loc[0] += 1 + localmap[name] = handle + handles[handle] = {"file": ffile, "line": line, + "column": _as_col(n.get("column")), "event": name, + "component": fname, "resource": "flow-local", + "ever_released": name in released_vars, + "pool": False} + body.append(Let(handle, Move(src_h, line), line)) + elif op == "borrow_mut": + # A BLOCK-scoped exclusive loan: `owner` is unavailable (and cannot + # be re-borrowed, consumed or released) until the block closes. The + # op is compound on purpose — the core's loans are block-scoped and + # the merge asserts that every predecessor carries the same loan + # set, so the fact vocabulary must not be able to say "open a loan" + # without also saying where it closes. + oname = n.get("owner") + bname = n.get("binding") + bn = n.get("body") + if not (isinstance(oname, str) and oname + and isinstance(bname, str) and bname + and isinstance(bn, list)): + raise OwnIRError( + f"OwnIR flow op 'borrow_mut' needs a non-empty string 'owner', " + f"a non-empty string 'binding' and an array 'body' " + f"({ffile}:{line})") + owner_h = localmap.get(oname) + if owner_h is None: + # no owner the core can see => no loan the core can enforce. + # Lowering the body anyway would analyse an UNPROTECTED region + # and report it clean. + raise OwnIRError( + f"OwnIR 'borrow_mut' owner {oname!r} is not a tracked local " + f"({ffile}:{line}) — refused: an exclusive region needs an " + f"owner the core tracks") + bsym = f"brw_{loc[0]}" + loc[0] += 1 + shadowed = localmap.get(bname) + localmap[bname] = bsym + body_b = _lower_flow(bn, ffile, fname, + handles, loc, localmap, released_vars, mos, hoisted, + first_party, overloaded, untracked, kill_sites, + True, param_effects) + # the binding dies with its block + if shadowed is None: + localmap.pop(bname, None) + else: + localmap[bname] = shadowed + body.append(BorrowBlock(owner_h, bsym, BorrowKind.MUT, body_b, line)) elif op == "call": # A call to a CONTRACTED callee (a function/extern whose signature the # bridge also lowered). `lower_call` resolves each argument's effect @@ -2728,9 +2928,37 @@ def _lower_flow(nodes: list[Any], ffile: str, fname: str, # OWN040. Drop them (no effect, no claim) — precision-safe, never a crash. elif (summ is not None or callee in _SINK_EXTERN_NAMES) \ and callee and isinstance(raw_args, list): + if in_region and summ is not None: + # see `_region_param_effects`: a tracked local on a PLAIN + # position is the core's OWN041, which is filtered below. + effs = (param_effects or {}).get(callee, []) + for j, a in enumerate(raw_args): + if str(a) in localmap and (j >= len(effs) or effs[j] is None): + raise OwnIRError( + f"OwnIR call to {callee!r} inside a borrow_mut " + f"region passes tracked local {str(a)!r} at " + f"position {j}, which carries no ownership effect " + f"({ffile}:{line}) — refused: inside an exclusive " + f"region every position that receives the entity " + f"or a token must state what it does with it") arg_refs: list[Expr] = [VarRef(localmap.get(str(a), str(a)), line) for a in raw_args] body.append(Call(callee, arg_refs, line)) + elif in_region and isinstance(raw_args, list): + # An UNRESOLVABLE callee is dropped everywhere else ("no effect, + # no claim"), and the core's OWN040 for it is filtered as a + # synthetic artifact. Inside an exclusive region that default is + # a false PASS: the region's entity, its binding or one of its + # tokens just left for code nobody analysed. Refuse — the + # alternative is "core raised OWN040, bridge filtered it, clean". + for a in raw_args: + if str(a) in localmap: + raise OwnIRError( + f"OwnIR call to {callee!r} inside a borrow_mut region " + f"passes tracked local {str(a)!r}, but the callee has " + f"no ownership contract ({ffile}:{line}) — refused: an " + f"exclusive region must not hand its entity or tokens " + f"to unanalysed code") # The kill site of a tracked local (Codex P1): THIS top-level call hands # it to a may/unknown position, so discharge the obligation here — the # optimistic "ownership left the caller" as a real `$consume` on the @@ -2808,6 +3036,22 @@ def _handle_of(diag: object) -> str | None: # the violation-site label per flow-local code, for the 2-step "origin -> manifestation" # reachability slice (P-015): the Rent/acquire site is where the resource came from, the # diagnostic line is where the obligation is violated. +# The move / loan codes a flow-local handle can raise once the flow vocabulary +# carries `move` and `borrow_mut`. Each needs a wording of its OWN: the fallback +# below interpolates the core diagnostic's message, and message text is not at +# parity between the two engines, so a code left to the fallback would agree on +# the verdict and disagree on the bytes. `{noun}` is "pooled buffer" or +# "IDisposable local", as for the codes above. +_FLOW_LOCAL_LOAN_WORDING = { + "OWN005": "{noun} '{name}' is used after its ownership was moved", + "OWN007": "{noun} '{name}' is consumed or returned while it is borrowed", + "OWN008": "{noun} '{name}' is released while it is borrowed", + "OWN011": "{noun} '{name}' is exclusively borrowed while an exclusive borrow of it is live", + "OWN012": "{noun} '{name}' is borrowed while an exclusive borrow of it is live", + "OWN013": "{noun} '{name}' is used directly while an exclusive borrow of it is live", +} + + _FLOW_LOCAL_VIOLATION = { "OWN002": "used here after it was released/returned", "OWN003": "released/returned here a second time", @@ -2864,7 +3108,8 @@ def dump_summaries(facts: dict[str, Any]) -> dict[str, Any]: summaries: dict[str, Any] = {} unresolved: list[str] = [] try: - summaries, unresolved = solve_with_log(_build_skeletons(raw_fns)) + summaries, unresolved = solve_with_log( + _build_skeletons(_inference_view(raw_fns))) except Exception as exc: degraded = f"{type(exc).__name__}: {exc}" return { @@ -2992,14 +3237,16 @@ def check_facts(facts: dict[str, Any]) -> list[Finding]: if sub.get("ever_released") else f"IDisposable local '{name}' is never disposed (leak)") elif pool: - msg = { + loan = _FLOW_LOCAL_LOAN_WORDING.get(d.code) + msg = loan.format(noun="pooled buffer", name=name) if loan else { "OWN002": f"pooled buffer '{name}' is used after it is returned to the pool", "OWN003": f"pooled buffer '{name}' is returned to the pool more than once", "OWN009": (f"pooled buffer '{name}' may be used after " f"being returned on some path"), }.get(d.code, f"pooled buffer '{name}': {d.message}") else: - msg = { + loan = _FLOW_LOCAL_LOAN_WORDING.get(d.code) + msg = loan.format(noun="IDisposable local", name=name) if loan else { "OWN002": f"IDisposable local '{name}' is used after it is disposed", "OWN003": f"IDisposable local '{name}' is disposed more than once", "OWN009": f"IDisposable local '{name}' may be used after disposal on some path", diff --git a/rust/crates/own-analysis/src/ownership.rs b/rust/crates/own-analysis/src/ownership.rs index 9a15de49..7c45bc6a 100644 --- a/rust/crates/own-analysis/src/ownership.rs +++ b/rust/crates/own-analysis/src/ownership.rs @@ -213,8 +213,10 @@ enum Emit<'a> { impl Emit<'_> { /// Emit `code` at `line` with **no** subject — the Python `err(...)` calls - /// that pass no `subject=` (the loan/permission codes OWN004/006/007-on- - /// consume/011/012/013/034/041). + /// that pass no `subject=` (OWN004/034/041). The loan/permission codes on + /// an OWNER (OWN006/007/011/012/013) carry one since the `OwnIR` flow + /// vocabulary gained `move`/`borrow_mut`: without it the bridge cannot map + /// the verdict back to a fact handle (IR5). fn push(&mut self, code: &'static str, line: u32) { self.push_at(code, line, None); } @@ -302,7 +304,7 @@ fn consume_like( } let (shared, mutable) = loans_on(st, sym); if shared > 0 || mutable { - emit.push(code_borrowed, line); + emit.push_at(code_borrowed, line, Some(sym)); } } @@ -312,9 +314,9 @@ fn check_mut_borrowable(st: &State, owner: SymId, emit: &mut Emit<'_>, line: u32 } let (shared, mutable) = loans_on(st, owner); if shared > 0 { - emit.push("OWN006", line); + emit.push_at("OWN006", line, Some(owner)); } else if mutable { - emit.push("OWN011", line); + emit.push_at("OWN011", line, Some(owner)); } } @@ -324,7 +326,7 @@ fn check_shared_borrowable(st: &State, owner: SymId, emit: &mut Emit<'_>, line: } let (_shared, mutable) = loans_on(st, owner); if mutable { - emit.push("OWN012", line); + emit.push_at("OWN012", line, Some(owner)); } } @@ -481,7 +483,7 @@ impl<'a> Ownership<'a> { if !state_problem(st, *sym, emit, *line) { let (_shared, mutable) = loans_on(st, *sym); if mutable { - emit.push("OWN013", *line); + emit.push_at("OWN013", *line, Some(*sym)); } } } diff --git a/rust/crates/own-analysis/tests/subject.rs b/rust/crates/own-analysis/tests/subject.rs index 16502447..8517e9ca 100644 --- a/rust/crates/own-analysis/tests/subject.rs +++ b/rust/crates/own-analysis/tests/subject.rs @@ -86,13 +86,16 @@ fn origin_is_inherited_across_a_move() { } #[test] -fn loan_permission_codes_stay_subject_less_like_python() { +fn loan_permission_codes_on_an_owner_carry_its_origin_like_python() { // `borrow b as r { borrow_mut b as m { } }`: a mutable borrow while a shared - // one is live is OWN006 — Python's `err(...)` passes no subject there. + // one is live is OWN006. It USED to be subject-less on both sides; since the + // `OwnIR` flow vocabulary carries `move`/`borrow_mut` the bridge must map a + // loan verdict back to a fact handle (IR5), so Python's `err(...)` passes + // `subject=owner.origin` there and this core stamps the same. let got = subjects( "fn f() {\n let b = acquire Conn(1);\n borrow b as r {\n borrow_mut b as m {\n }\n }\n release b;\n}\n", ); - assert_eq!(got, vec![(8, "OWN006".to_owned(), None)]); + assert_eq!(got, vec![(8, "OWN006".to_owned(), s("b#6"))]); } #[test] diff --git a/rust/crates/own-bridge/src/ast.rs b/rust/crates/own-bridge/src/ast.rs index f57a168a..a21ae663 100644 --- a/rust/crates/own-bridge/src/ast.rs +++ b/rust/crates/own-bridge/src/ast.rs @@ -34,9 +34,9 @@ use crate::BridgeError; use own_lowered::{Function, LoweredDocument, Param, Stmt, TypeShape}; use own_syntax::ast::{ - Acquire, AliasJoin, Call, Effect, EffectParam, Expr, ExternDecl, FnDecl, If, Let, LifetimeDecl, - MemberRole, Module, Overspan, Release, ResourceDecl, ResourceMember, Return, Stmt as AstStmt, - Subscribe, TypeRef, Use, VarRef, While, + Acquire, AliasJoin, BorrowBlock, BorrowKind, Call, Effect, EffectParam, Expr, ExternDecl, + FnDecl, If, Let, LifetimeDecl, MemberRole, Module, Move, Overspan, Release, ResourceDecl, + ResourceMember, Return, Stmt as AstStmt, Subscribe, TypeRef, Use, VarRef, While, }; /// A fact coordinate as a core line: in the §4.2 domain, or degraded to `0`. @@ -153,6 +153,31 @@ fn stmt(s: &Stmt) -> Result { src: src.clone(), line: core_line(*line), }), + Stmt::Move { handle, src, line } => { + // Python: `Let(handle, Move(src_h, line), line)` — the move + // expression and the `let` carry the same line. + let line = core_line(*line); + AstStmt::Let(Let { + name: handle.clone(), + rhs: Expr::Move(Move { + var: src.clone(), + line, + }), + line, + }) + } + Stmt::BorrowMut { + owner, + binding, + body, + line, + } => AstStmt::BorrowBlock(BorrowBlock { + owner: owner.clone(), + binding: binding.clone(), + kind: BorrowKind::Mut, + body: stmts(body)?, + line: core_line(*line), + }), Stmt::Call { callee, args, line } => { // Python: `VarRef(localmap.get(a, a), line)` — every argument is a // name reference carrying the CALL's line. diff --git a/rust/crates/own-bridge/src/dump.rs b/rust/crates/own-bridge/src/dump.rs index 3461426a..299c3c70 100644 --- a/rust/crates/own-bridge/src/dump.rs +++ b/rust/crates/own-bridge/src/dump.rs @@ -23,7 +23,7 @@ // visibility here (same stance as `mos.rs`). #![allow(clippy::redundant_pub_crate)] -use crate::lower::{as_list, build_skeletons, py_str}; +use crate::lower::{as_list, build_skeletons, inference_view, py_str}; use crate::mos; use crate::BridgeError; use own_ir::{OwnIr, OWNIR_VERSION}; @@ -62,7 +62,9 @@ pub(crate) fn dump_summaries(facts: &OwnIr) -> Result { let mut summaries: Vec = Vec::new(); let mut unresolved: Vec = Vec::new(); let mut degraded = Value::Null; - match mos::solve_with_log(build_skeletons(raw_fns)) { + let view_store = inference_view(raw_fns); + let view_fns: &[Value] = view_store.as_deref().unwrap_or(raw_fns); + match mos::solve_with_log(build_skeletons(view_fns)) { Ok((mos, log)) => { unresolved = log; // `sorted(summaries)` — the map key IS `MethodSummary.key` in the diff --git a/rust/crates/own-bridge/src/lower.rs b/rust/crates/own-bridge/src/lower.rs index 93c07207..b3de0fc6 100644 --- a/rust/crates/own-bridge/src/lower.rs +++ b/rust/crates/own-bridge/src/lower.rs @@ -365,6 +365,133 @@ fn callee_returns_fresh( // --- flow-body walks (the `_*` helpers of ownir.py) -------------------------- /// `_released_vars`. +/// Python `{s!r}` for a plain name — the only strings the region refusals +/// interpolate. +fn repr_str(s: &str) -> String { + format!("'{s}'") +} + +// --- the inference view (`_has_region` / `_inline_regions`) ------------------- + +/// `_has_region`: whether a flow body contains a `borrow_mut` region at any +/// depth. +pub(crate) fn has_region(nodes: &[Value]) -> bool { + nodes + .iter() + .filter_map(Value::as_object) + .any(|n| match get_str(n, "op") { + Some("borrow_mut") => true, + Some("if") => has_region(as_list(n.get("then"))) || has_region(as_list(n.get("else"))), + Some("while") => has_region(as_list(n.get("body"))), + _ => false, + }) +} + +/// `_inline_regions`: the INFERENCE view of a flow body — every `borrow_mut` +/// region spliced in place of its block. +/// +/// A region is a straight-line sub-body that runs exactly once, so it is +/// transparent to every question the contract-inference layer asks. Only +/// [`lower_flow`] needs the structure, because only it emits the loan. Callers +/// take this path ONLY for a body [`has_region`] accepts, so a document +/// without regions never sees a copy. +fn inline_regions(nodes: &[Value]) -> Vec { + let mut out: Vec = Vec::new(); + for n_v in nodes { + let Some(n) = n_v.as_object() else { + out.push(n_v.clone()); + continue; + }; + let splice = |key: &str, m: &mut Obj| { + if let Some(Value::Array(sub)) = n.get(key) { + m.insert(key.to_owned(), Value::Array(inline_regions(sub))); + } + }; + match get_str(n, "op") { + Some("borrow_mut") => { + if let Some(Value::Array(sub)) = n.get("body") { + out.extend(inline_regions(sub)); + } + } + Some("if") + if has_region(as_list(n.get("then"))) || has_region(as_list(n.get("else"))) => + { + let mut m = n.clone(); + splice("then", &mut m); + splice("else", &mut m); + out.push(Value::Object(m)); + } + Some("while") if has_region(as_list(n.get("body"))) => { + let mut m = n.clone(); + splice("body", &mut m); + out.push(Value::Object(m)); + } + _ => out.push(n_v.clone()), + } + } + out +} + +/// `_inference_view`: `functions[]` as the inference layer reads it, or `None` +/// when no function opens a region (the common case: nothing is copied and +/// the caller keeps reading the original slice). +pub(crate) fn inference_view(raw_fns: &[Value]) -> Option> { + let regional = |f: &Obj| has_region(as_list(f.get("body"))); + if !raw_fns.iter().filter_map(Value::as_object).any(regional) { + return None; + } + Some( + raw_fns + .iter() + .map(|fv| match fv.as_object() { + Some(f) if regional(f) => { + let mut m = f.clone(); + m.insert( + "body".to_owned(), + Value::Array(inline_regions(as_list(f.get("body")))), + ); + Value::Object(m) + } + _ => fv.clone(), + }) + .collect(), + ) +} + +/// `_region_param_effects`: for each function NAME, whether each parameter +/// resolves to an ownership effect (`true`) or to a plain value (`false`) — +/// the resolution [`lower_fn_params`] applies, over the same inference view. +/// +/// Read by one rule: inside a `borrow_mut` region a tracked local must not +/// land on a PLAIN position. The core reports that shape as OWN041, which the +/// verdict layer filters as a synthesis artifact — so without the rule the +/// path is "core raised, bridge filtered, clean". +fn region_param_effects(view_fns: &[Value], mos: &Mos) -> HashMap> { + let mut out: HashMap> = HashMap::new(); + for f in view_fns.iter().filter_map(Value::as_object) { + let fname = str_or(f, "name", ""); + let mut effs: Vec = Vec::new(); + if let Some(raw) = f.get("params").and_then(Value::as_array) { + let summ = mos_lookup(mos, &fname, call_sig(f)); + for (i, p) in raw.iter().enumerate() { + let Some(p) = p.as_object() else { continue }; + let eff: Option<&str> = p.get("effect").and_then(Value::as_str).or_else(|| { + let ftrans = summ.and_then(|s| { + s.params + .iter() + .find(|q| q.index == i64::try_from(i).unwrap_or(i64::MAX)) + .map(|q| q.transfer) + }); + infer_param_effect(&str_or(p, "name", "?"), as_list(f.get("body")), ftrans) + }); + effs.push(eff.is_some_and(|e| e != "plain")); + } + } + out.insert(fname, effs); + } + out +} + fn released_vars(nodes: &[Value]) -> HashSet { let mut out = HashSet::new(); fn walk(nodes: &[Value], out: &mut HashSet) { @@ -1477,6 +1604,11 @@ struct FnCtx<'v, 'a> { overloaded: &'a HashSet, untracked: &'a HashSet, kill_sites: &'a HashMap, + /// Inside a `borrow_mut` region: an unverifiable handoff refuses instead + /// of dropping to "no effect, no claim". + in_region: bool, + /// `_region_param_effects`, present iff some function opens a region. + param_effects: Option<&'a HashMap>>, } fn lower_flow<'v>(ctx: &mut FnCtx<'v, '_>, nodes: &'v [Value]) -> Result, BridgeError> { @@ -1599,6 +1731,87 @@ fn lower_flow<'v>(ctx: &mut FnCtx<'v, '_>, nodes: &'v [Value]) -> Result { + // `var` takes OWNERSHIP of `src`'s obligation; `src` is dead + // after it. Shaped like `alias_join`: the destination's old + // binding dies first, and an untracked `src` makes no claim. + let name = str_or(n, "var", "?"); + let src_h = ctx.localmap.get(&str_or(n, "src", "")).cloned(); + if !ctx.hoisted.contains(&name) { + ctx.localmap.remove(&name); + } + if let Some(src_h) = src_h { + if !ctx.hoisted.contains(&name) && !ctx.untracked.contains(&name) { + let handle = format!("loc_{}", ctx.loc); + *ctx.loc = ctx.loc.saturating_add(1); + ctx.localmap.insert(name.clone(), handle.clone()); + ctx.handles.push( + &handle, + flow_local_record( + ctx.ffile, + line, + n.get("column"), + &name, + ctx.fname, + ctx.released.contains(&name), + Some(false), + ), + )?; + body.push(Stmt::Move { + handle, + src: src_h, + line, + }); + } + } + } + Some("borrow_mut") => { + // A BLOCK-scoped exclusive loan. Compound on purpose: the + // core's loans are block-scoped and its merge requires every + // predecessor to carry the same loan set. + let oname = get_str(n, "owner").filter(|s| !s.is_empty()); + let bname = get_str(n, "binding").filter(|s| !s.is_empty()); + let bn = n.get("body").and_then(Value::as_array); + let (Some(oname), Some(bname), Some(bn)) = (oname, bname, bn) else { + return Err(BridgeError(format!( + "OwnIR flow op 'borrow_mut' needs a non-empty string 'owner', \ + a non-empty string 'binding' and an array 'body' ({}:{line})", + ctx.ffile, + ))); + }; + // no owner the core can see => no loan the core can enforce. + let Some(owner_h) = ctx.localmap.get(oname).cloned() else { + return Err(BridgeError(format!( + "OwnIR 'borrow_mut' owner {} is not a tracked local \ + ({}:{line}) — refused: an exclusive region needs an \ + owner the core tracks", + repr_str(oname), + ctx.ffile, + ))); + }; + let bsym = format!("brw_{}", ctx.loc); + *ctx.loc = ctx.loc.saturating_add(1); + let shadowed = ctx.localmap.insert(bname.to_owned(), bsym.clone()); + let outer = ctx.in_region; + ctx.in_region = true; + let body_b = lower_flow(ctx, bn)?; + ctx.in_region = outer; + // the binding dies with its block + match shadowed { + Some(prev) => { + ctx.localmap.insert(bname.to_owned(), prev); + } + None => { + ctx.localmap.remove(bname); + } + } + body.push(Stmt::BorrowMut { + owner: owner_h, + binding: bsym, + body: body_b, + line, + }); + } Some("call") => { let callee = str_or(n, "callee", ""); let identity = canonical(&callee); @@ -1648,6 +1861,29 @@ fn lower_flow<'v>(ctx: &mut FnCtx<'v, '_>, nodes: &'v [Value]) -> Result(ctx: &mut FnCtx<'v, '_>, nodes: &'v [Value]) -> Result Result { // notes.append(f"{type(exc).__name__}: {exc}")`; the only failure a facts // document can reach is the duplicate-key guard, a `ValueError` there. let mut mos_notes: Vec = Vec::new(); - let mos_map: Mos = match mos::solve(build_skeletons(raw_fns)) { + // the INFERENCE view (regions spliced); `None` when no function opens one. + let view_store = inference_view(raw_fns); + let view_fns: &[Value] = view_store.as_deref().unwrap_or(raw_fns); + let mos_map: Mos = match mos::solve(build_skeletons(view_fns)) { Ok(m) => m, Err(e) => { mos_notes.push(format!("ValueError: {e}")); @@ -1944,15 +2204,28 @@ pub(crate) fn lower_full(facts: &OwnIr) -> Result { .map(|(n, _)| n.to_owned()) .collect() }; + let region_effects: Option>> = view_store + .is_some() + .then(|| region_param_effects(view_fns, &mos_map)); for fn_v in raw_fns { let Some(f) = fn_v.as_object() else { continue }; let fname = f.get("name").map_or_else(|| format!("Fn{loc}"), py_str); let ffile = str_or(f, "file", "?"); - let nodes: &[Value] = as_list(f.get("body")); + // `flow_nodes` keeps the structure and is read by `lower_flow` alone; + // `nodes` is the inference view. With no region they are one slice. + let flow_nodes: &[Value] = as_list(f.get("body")); + let f_view_store: Option = has_region(flow_nodes).then(|| { + let mut m = f.clone(); + m.insert("body".to_owned(), Value::Array(inline_regions(flow_nodes))); + m + }); + let regional = f_view_store.is_some(); + let f_view: &Obj = f_view_store.as_ref().unwrap_or(f); + let nodes: &[Value] = as_list(f_view.get("body")); let released = released_vars(nodes); let mut localmap: HashMap = HashMap::new(); let fparams = lower_fn_params( - f, + f_view, &ffile, &fname, &mut handles, @@ -1970,6 +2243,23 @@ pub(crate) fn lower_full(facts: &OwnIr) -> Result { .map(|(a, _, _, _)| a.clone()) .filter(|a| !kill_sites.contains_key(a)) .collect(); + if regional { + // A function that opens an exclusive region must be verified end + // to end: the optimistic default (advisory OWN051) would turn an + // unverifiable handoff into silence, and silence inside a + // protocol region reads as a PASS. + if let Some((arg, callee, transfer, cline)) = unverified.first() { + return Err(BridgeError(format!( + "OwnIR function {} opens a borrow_mut region but hands {} to {} \ + at an unverified position (inferred contract: {transfer}) \ + ({ffile}:{cline}) — refused: a function with an exclusive \ + region must not rely on the optimistic ownership default", + repr_str(&fname), + repr_str(arg), + repr_str(callee), + ))); + } + } // OWN051, gated on args that actually carry an obligation here (an // acquired local or a fresh factory result) — a plain value passed to // a may-position is not a gap worth a note (BR-L8). @@ -2040,8 +2330,10 @@ pub(crate) fn lower_full(facts: &OwnIr) -> Result { overloaded: &overloaded, untracked: &untracked, kill_sites: &kill_sites, + in_region: false, + param_effects: region_effects.as_ref(), }; - fbody.extend(lower_flow(&mut ctx, nodes)?); + fbody.extend(lower_flow(&mut ctx, flow_nodes)?); // a value-returning body gets an owned return type so `return s` // models a valid escape (discharge), not a void-return mismatch. let fret = returns_value(nodes).then(|| TypeShape { diff --git a/rust/crates/own-bridge/src/render.rs b/rust/crates/own-bridge/src/render.rs index e3fcfd8a..d91733d1 100644 --- a/rust/crates/own-bridge/src/render.rs +++ b/rust/crates/own-bridge/src/render.rs @@ -38,7 +38,7 @@ const SARIF_VERSION: &str = "2.1.0"; const TOOL_NAME: &str = "Owen"; /// `OWNIR_VERSION` — the schema stamp the driver carries so a consumer can tell /// which fact vocabulary produced the log. -const OWNIR_VERSION: u32 = 0; +const OWNIR_VERSION: u32 = 1; /// `_esc_data`: a GitHub workflow-command MESSAGE escapes only `%`, CR and LF. fn esc_data(s: &str) -> String { diff --git a/rust/crates/own-bridge/src/verdict.rs b/rust/crates/own-bridge/src/verdict.rs index a315a086..90f548c7 100644 --- a/rust/crates/own-bridge/src/verdict.rs +++ b/rust/crates/own-bridge/src/verdict.rs @@ -166,6 +166,27 @@ fn flow_local_message( (false, false) => format!("IDisposable local '{name}' is never disposed (leak)"), }; } + // `_FLOW_LOCAL_LOAN_WORDING`: the move / loan codes reachable since the + // flow vocabulary gained `move` and `borrow_mut`. They need wordings of + // their own — the fallback interpolates the core message, which is not at + // parity between the engines. + let noun = if pool { + "pooled buffer" + } else { + "IDisposable local" + }; + let loan = match code { + "OWN005" => Some("is used after its ownership was moved"), + "OWN007" => Some("is consumed or returned while it is borrowed"), + "OWN008" => Some("is released while it is borrowed"), + "OWN011" => Some("is exclusively borrowed while an exclusive borrow of it is live"), + "OWN012" => Some("is borrowed while an exclusive borrow of it is live"), + "OWN013" => Some("is used directly while an exclusive borrow of it is live"), + _ => None, + }; + if let Some(tail) = loan { + return format!("{noun} '{name}' {tail}"); + } if pool { return match code { "OWN002" => format!("pooled buffer '{name}' is used after it is returned to the pool"), @@ -1345,9 +1366,10 @@ mod tests { /// BR-V4's flow-local FALLBACK: a code with no wording of its own keeps the /// core diagnostic's message verbatim after a colon, on both sides of the /// pool split. Driven through `map_core` — the production branch — because - /// no facts document can reach it: the `OwnIR` flow vocabulary is nine ops, + /// no facts document can reach it: the `OwnIR` flow vocabulary is eleven ops, /// and the only codes they raise on a flow-local handle are OWN001/002/003/ - /// 009/025, every one of which HAS a wording. Same shape as the BR-V1 + /// 009/025 plus the move/loan codes OWN005/007/008/011/012/013, every one + /// of which HAS a wording. Same shape as the BR-V1 /// severity control above: a rule the corpus cannot exercise is proven at /// the unit level rather than left unproven or quietly dropped. /// @@ -1374,7 +1396,7 @@ mod tests { let diags: Vec = ["loc_0", "loc_1"] .iter() .map(|h| { - Diagnostic::new("OWN005", "moved 's' at A.cs:9", 9) + Diagnostic::new("OWN015", "moved 's' at A.cs:9", 9) .unwrap() .with_subject(format!("{h}#4")) }) diff --git a/rust/crates/own-bridge/tests/mechanisms.rs b/rust/crates/own-bridge/tests/mechanisms.rs index cca00d72..714a23f7 100644 --- a/rust/crates/own-bridge/tests/mechanisms.rs +++ b/rust/crates/own-bridge/tests/mechanisms.rs @@ -34,12 +34,12 @@ fn record_order_changes_global_mint_order_predictably() { ) }; let doc_ab = lower(&format!( - r#"{{"ownir_version": 0, "module": "M", "components": [{}, {}]}}"#, + r#"{{"ownir_version": 1, "module": "M", "components": [{}, {}]}}"#, comp("A"), comp("B") )); let doc_ba = lower(&format!( - r#"{{"ownir_version": 0, "module": "M", "components": [{}, {}]}}"#, + r#"{{"ownir_version": 1, "module": "M", "components": [{}, {}]}}"#, comp("B"), comp("A") )); @@ -62,7 +62,7 @@ fn record_order_changes_global_mint_order_predictably() { #[test] fn kill_on_rebind_removes_the_old_mapping() { let doc = lower( - r#"{"ownir_version": 0, "module": "M", "functions": [ + r#"{"ownir_version": 1, "module": "M", "functions": [ {"name": "F", "file": "F.cs", "body": [ {"op": "acquire", "var": "x", "line": 2}, {"op": "call", "callee": "Unknown.Make", "args": [], "result": "x", "line": 3}, @@ -82,7 +82,7 @@ fn kill_on_rebind_removes_the_old_mapping() { #[test] fn precise_overload_channel_does_not_unmap() { let doc = lower( - r#"{"ownir_version": 0, "module": "M", "functions": [ + r#"{"ownir_version": 1, "module": "M", "functions": [ {"name": "Take", "file": "F.cs", "sig": "System.IO.Stream", "params": [{"name": "p", "line": 1, "effect": "consume"}], "body": []}, {"name": "Take", "file": "F.cs", "sig": "System.String", @@ -115,7 +115,7 @@ fn precise_overload_channel_does_not_unmap() { #[test] fn merged_may_consume_applies_the_kill_site_unmap() { let doc = lower( - r#"{"ownir_version": 0, "module": "M", "functions": [ + r#"{"ownir_version": 1, "module": "M", "functions": [ {"name": "Take", "file": "F.cs", "sig": "System.IO.Stream", "params": [{"name": "p", "line": 1, "effect": "consume"}], "body": []}, {"name": "Take", "file": "F.cs", "sig": "System.String", @@ -148,7 +148,7 @@ mod hoist_gates { #[test] fn positive_control_hoists() { let doc = lower( - r#"{"ownir_version": 0, "module": "M", "functions": [ + r#"{"ownir_version": 1, "module": "M", "functions": [ {"name": "F", "file": "F.cs", "body": [ {"op": "if", "line": 2, "then": [{"op": "acquire", "var": "r", "line": 3}], @@ -175,7 +175,7 @@ mod hoist_gates { #[test] fn nested_depth_reference_blocks_the_hoist() { let doc = lower( - r#"{"ownir_version": 0, "module": "M", "functions": [ + r#"{"ownir_version": 1, "module": "M", "functions": [ {"name": "F", "file": "F.cs", "body": [ {"op": "if", "line": 2, "then": [ {"op": "if", "line": 3, @@ -195,7 +195,7 @@ mod hoist_gates { #[test] fn while_body_acquire_blocks_the_hoist() { let doc = lower( - r#"{"ownir_version": 0, "module": "M", "functions": [ + r#"{"ownir_version": 1, "module": "M", "functions": [ {"name": "F", "file": "F.cs", "body": [ {"op": "while", "line": 2, "body": [{"op": "acquire", "var": "r", "line": 3}]}, @@ -214,7 +214,7 @@ mod hoist_gates { #[test] fn early_return_blocks_the_hoist() { let doc = lower( - r#"{"ownir_version": 0, "module": "M", "functions": [ + r#"{"ownir_version": 1, "module": "M", "functions": [ {"name": "F", "file": "F.cs", "body": [ {"op": "if", "line": 2, "then": [{"op": "acquire", "var": "r", "line": 3}], @@ -236,7 +236,7 @@ mod hoist_gates { #[test] fn unknown_flow_op_fails_loud_with_python_text() { let facts = own_ir::OwnIr::from_json( - r#"{"ownir_version": 0, "module": "M", "functions": [ + r#"{"ownir_version": 1, "module": "M", "functions": [ {"name": "F", "file": "X.cs", "body": [{"op": "goto", "line": 7}]}]}"#, ) .expect("facts parse"); diff --git a/rust/crates/own-bridge/tests/verdicts.rs b/rust/crates/own-bridge/tests/verdicts.rs index 6a34e734..ee5503bc 100644 --- a/rust/crates/own-bridge/tests/verdicts.rs +++ b/rust/crates/own-bridge/tests/verdicts.rs @@ -366,6 +366,7 @@ fn lowered_lines(doc: &own_lowered::LoweredDocument) -> Vec { | Stmt::Overspan { line, .. } | Stmt::Return { line, .. } | Stmt::AliasJoin { line, .. } + | Stmt::Move { line, .. } | Stmt::Call { line, .. } | Stmt::Subscribe { line, .. } => out.push(*line), Stmt::If { @@ -375,7 +376,7 @@ fn lowered_lines(doc: &own_lowered::LoweredDocument) -> Vec { walk(then, out); walk(r#else, out); } - Stmt::While { body, line, .. } => { + Stmt::While { body, line, .. } | Stmt::BorrowMut { body, line, .. } => { out.push(*line); walk(body, out); } diff --git a/rust/crates/own-ir/src/lib.rs b/rust/crates/own-ir/src/lib.rs index 91dbee96..b250f647 100644 --- a/rust/crates/own-ir/src/lib.rs +++ b/rust/crates/own-ir/src/lib.rs @@ -59,7 +59,7 @@ use serde_json::{Map, Value}; /// The schema version this crate understands. Bump only on an incompatible /// vocabulary change — additive optional fields are NOT a version bump. -pub const OWNIR_VERSION: i64 = 0; +pub const OWNIR_VERSION: i64 = 1; /// Why a document was rejected. /// diff --git a/rust/crates/own-ir/tests/roundtrip.rs b/rust/crates/own-ir/tests/roundtrip.rs index abcb2ccb..26281dbc 100644 --- a/rust/crates/own-ir/tests/roundtrip.rs +++ b/rust/crates/own-ir/tests/roundtrip.rs @@ -45,9 +45,9 @@ fn round_trips_every_python_fixture() { #[test] fn version_gate_rejects_future_schema() { - let err = OwnIr::from_json(r#"{"ownir_version": 1}"#).expect_err("v1 must be rejected"); + let err = OwnIr::from_json(r#"{"ownir_version": 2}"#).expect_err("v2 must be rejected"); assert!( - err.message.contains("schema v1") && err.message.contains(&format!("v{OWNIR_VERSION}")), + err.message.contains("schema v2") && err.message.contains(&format!("v{OWNIR_VERSION}")), "gate message must name both versions: {err}" ); } diff --git a/rust/crates/own-ir/tests/validation_replay.rs b/rust/crates/own-ir/tests/validation_replay.rs index 2b603185..0bfaeb9d 100644 --- a/rust/crates/own-ir/tests/validation_replay.rs +++ b/rust/crates/own-ir/tests/validation_replay.rs @@ -370,7 +370,7 @@ fn the_depth_guard_never_fires_on_a_document_from_json_accepts() { node = serde_json::json!({"nested": [node]}); } serde_json::to_string(&serde_json::json!({ - "ownir_version": 0, + "ownir_version": 1, "future_section": node })) .expect("serializes") diff --git a/rust/crates/own-ir/tests/version_repr_census.rs b/rust/crates/own-ir/tests/version_repr_census.rs index 2109f087..cbf3a4df 100644 --- a/rust/crates/own-ir/tests/version_repr_census.rs +++ b/rust/crates/own-ir/tests/version_repr_census.rs @@ -61,10 +61,10 @@ const CENSUS: &[(&str, &str, &str)] = &[ ("null", "{\"ownir_version\": null}", "OwnIR 'ownir_version' must be an integer, got None"), ("array of one", "{\"ownir_version\": [\"a\"]}", "OwnIR 'ownir_version' must be an integer, got ['a']"), ("array empty", "{\"ownir_version\": []}", "OwnIR 'ownir_version' must be an integer, got []"), - ("int oversized +", "{\"ownir_version\": 10000000000000000000000000000000}", "OwnIR facts are schema v10000000000000000000000000000000, but this core understands v0. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it."), - ("int oversized -", "{\"ownir_version\": -10000000000000000000000000000000}", "OwnIR facts are schema v-10000000000000000000000000000000, but this core understands v0. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it."), - ("int i64 max + 1", "{\"ownir_version\": 9223372036854775808}", "OwnIR facts are schema v9223372036854775808, but this core understands v0. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it."), - ("int mismatch 7", "{\"ownir_version\": 7}", "OwnIR facts are schema v7, but this core understands v0. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it."), + ("int oversized +", "{\"ownir_version\": 10000000000000000000000000000000}", "OwnIR facts are schema v10000000000000000000000000000000, but this core understands v1. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it."), + ("int oversized -", "{\"ownir_version\": -10000000000000000000000000000000}", "OwnIR facts are schema v-10000000000000000000000000000000, but this core understands v1. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it."), + ("int i64 max + 1", "{\"ownir_version\": 9223372036854775808}", "OwnIR facts are schema v9223372036854775808, but this core understands v1. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it."), + ("int mismatch 7", "{\"ownir_version\": 7}", "OwnIR facts are schema v7, but this core understands v1. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it."), ]; /// Every value class the reference rejects, spelled byte for byte as the diff --git a/rust/crates/own-lowered/src/model.rs b/rust/crates/own-lowered/src/model.rs index e88a2e68..0b149a51 100644 --- a/rust/crates/own-lowered/src/model.rs +++ b/rust/crates/own-lowered/src/model.rs @@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize}; /// The Layer 2 surface version — must equal `ownlang/lowered.py`'s /// `LOWERED_VERSION` and `manifest.json`'s `lowered_version`. -pub const LOWERED_VERSION: u32 = 1; +pub const LOWERED_VERSION: u32 = 2; /// Three-state presence for the schema-nullable handle keys. /// @@ -208,6 +208,13 @@ pub enum Stmt { src: String, line: i64, }, + /// `handle` takes ownership of `src`'s obligation; `src` is dead after it + /// (unlike `alias_join`, where the source stays owning). + Move { + handle: String, + src: String, + line: i64, + }, Call { callee: String, args: Vec, @@ -229,6 +236,15 @@ pub enum Stmt { body: Vec, line: i64, }, + /// A block-scoped exclusive loan of `owner`, visible inside as `binding`. + /// Compound on purpose: the core's loans are block-scoped, so the surface + /// cannot open a loan without also saying where it closes. + BorrowMut { + owner: String, + binding: String, + body: Vec, + line: i64, + }, } /// One normalized handle-map entry: `handle` first, then the allowlist keys in diff --git a/rust/crates/own-lowered/tests/strictness.rs b/rust/crates/own-lowered/tests/strictness.rs index 61290902..9b114e79 100644 --- a/rust/crates/own-lowered/tests/strictness.rs +++ b/rust/crates/own-lowered/tests/strictness.rs @@ -56,7 +56,7 @@ fn rejects_wrong_version_on_a_rejection_document() { #[test] fn accepts_the_current_version_on_a_rejection_document() { - let text = "{\n \"lowered_version\": 1,\n \"error\": \"boom\"\n}\n"; + let text = "{\n \"lowered_version\": 2,\n \"error\": \"boom\"\n}\n"; let surface = parse_document(text).expect("a current-version rejection parses"); let emitted = to_canonical_json(&surface).expect("canonical emit"); assert_eq!( @@ -71,7 +71,7 @@ fn rejects_a_null_parameter_type() { // never write `"type": null` on a parameter, so the typed model must not // accept it either. let text = r#"{ - "lowered_version": 1, + "lowered_version": 2, "module": "m", "resources": [], "externs": [], @@ -102,7 +102,7 @@ fn rejects_a_null_parameter_type() { fn rejects_explicit_null_on_a_non_nullable_handle_key() { // `released` is optional-but-boolean on the record; an explicit null must // fail the parse, not decay to "missing" and vanish on re-emit. - let text = doc(1, r#"[{"handle": "sub_0", "released": null}]"#); + let text = doc(2, r#"[{"handle": "sub_0", "released": null}]"#); parse_document(&text).expect_err("\"released\": null must be rejected, not deleted"); } @@ -112,7 +112,7 @@ fn preserves_explicit_null_metadata_through_a_round_trip() { // membership) explicit `null` for `type`, `source`, and `source_type` — // `{}` and `{"source_type": null}` are DIFFERENT Layer 2 documents. let text = doc( - 1, + 2, r#"[ { "handle": "sub_0", diff --git a/rust/crates/own-shadow/tests/repro.rs b/rust/crates/own-shadow/tests/repro.rs index b72644e9..f4cb2074 100644 --- a/rust/crates/own-shadow/tests/repro.rs +++ b/rust/crates/own-shadow/tests/repro.rs @@ -832,7 +832,7 @@ fn verify_refuses_each_structural_violation() { "raw bytes that parse to another document", "does not reproduce input.canonical", with_raw(&|_raw| { - let other = br#"{"ownir_version": 0, "module": "SomethingElse"}"#; + let other = br#"{"ownir_version": 1, "module": "SomethingElse"}"#; let identity = own_shadow::hash_bytes(other); Json::Object(vec![ ("algorithm".to_owned(), Json::Str("sha256".to_owned())), diff --git a/spec/Bridge.md b/spec/Bridge.md index 021152eb..cfefb74b 100644 --- a/spec/Bridge.md +++ b/spec/Bridge.md @@ -213,6 +213,32 @@ through `_as_line` — a non-integer, or one outside the [§4.2](OwnIR.md) coordinate domain, reads as `0`. The bridge never invents lines and never clamps one; the anchor policy for *findings* is BR-V5's. +**BR-L12 (`move`).** `move src -> var` lowers to `Let(var', Move(src_h))`: +`var` takes the obligation, `src` is dead after it (the core's OWN005 on a later +use). Shaped like the alias branch: the destination's old binding dies first +(BR-L6), a hoisted or untracked destination mints nothing, and an untracked +`src` makes no claim. + +**BR-L13 (`borrow_mut` — the exclusive region, and its admission rules).** +`borrow_mut owner as binding { body }` lowers to one `BorrowBlock(owner_h, +brw_, MUT, body')` — the op is compound because the core's loans are +block-scoped; `binding` is in scope for `body` only and restores what it +shadowed. Inference reads the region as transparent (its body spliced in +place), so a region never changes a summary. Four shapes are **refused** +(`OwnIRError`, file:line) rather than lowered, because each would otherwise +reach the verdict as "the core raised, the bridge filtered it, clean": +(a) an `owner` that is not a tracked local — there would be no loan to enforce; +(b) inside a region, a call to an unresolvable callee that passes a tracked +local — the default "drop it: no effect, no claim" (BR-L9b) would hand the +entity or a token to unanalysed code, and the core's OWN040 for it is filtered; +(c) inside a region, a tracked local on a position that resolved **plain** — +the core's OWN041, filtered as a synthesis artifact; +(d) any `may`/`unknown` position (BR-L8) in a function that contains a region — +the optimistic untrack would silently drop the region's owner or a token. +A frontend must therefore not emit a region it cannot prove closed over +contracted calls; what it may put inside one is the frontend's rule, not the +bridge's (the Roslyn lowering is default-deny: transitions and what it reads). + ## 3. Interprocedural MOS Normatively specified in [Inference.md](Inference.md) (INF-L/S/R/M/F/A/P + diff --git a/spec/OwnIR.md b/spec/OwnIR.md index 3dcc538b..9d9a9499 100644 --- a/spec/OwnIR.md +++ b/spec/OwnIR.md @@ -19,7 +19,7 @@ A facts document is a single JSON object: ```json { - "ownir_version": 0, + "ownir_version": 1, "module": "WpfApp", "components": [ /* §4 owned-resource records, grouped by type */ ], "functions": [ /* §5 flow bodies (intra-procedural CFG facts) */ ], @@ -45,7 +45,17 @@ producer**: the Python core, the Roslyn extractor (`OwnSharp.Extractor`), and th OwnTS frontend. A document whose `ownir_version` differs from the core's raises `OwnIRError` at load — a mismatched extractor/core pair fails loudly rather than silently mis-reading facts. A document that omits the field is read as the -current version (legacy v0 producers). +current version (the producers that omit it predate the field). + +The version is currently **1**: + +| Version | What it added | +|---|---| +| 0 | the initial vocabulary | +| 1 | two flow ops: `move` ([§5](#5-flow-bodies-functions)) and the compound `borrow_mut` ([§5.3](#53-exclusive-regions-borrow_mut)) | + +A document stamped with an older version is refused, not migrated: no core +reads two versions, and a producer and a core are built from the same commit. What bumps the version — the rule that keeps the three producers honest: @@ -294,6 +304,8 @@ op vocabulary: | `call` | `callee`, `args`, optional `result`, optional `sig` | a `Call` checked against the callee's contract; a `fresh`-returning callee mints an acquire for `result` (D5.2) | | `if` | `then`, `else` (sub-bodies) | an `If` with both branches lowered | | `while` | `body` (sub-body) | a `While` — a back-edge the core's worklist fixpoint converges over (A1) | +| `move` | `var`, `src` | a `Let`+`Move`: `var` takes ownership of `src`'s obligation and `src` is dead after it (a later use is **OWN005**) | +| `borrow_mut` | `owner`, `binding`, `body` (sub-body) | a `BorrowBlock`: an exclusive loan of the local `owner` for the block, visible inside as `binding` (§5.3) | Anything else is a hard error (§2, fail-loud). Overwriting a tracked local (a re-bound `call` result or `alias_join` target) kills its previous ownership @@ -390,6 +402,39 @@ that registers the sidecar binds them to the shared coordinate domain. The producer validates every record against this vocabulary and refuses to write a facts file at all if one is malformed (exit 2): fail-loud at the source. +### 5.3 Exclusive regions (`borrow_mut`) + +`borrow_mut` is a **compound** op: it carries the loan's whole extent as its +`body`. There is no `borrow_start`/`borrow_end` pair, and there will not be one — +the core's loans are block-scoped and its merge requires every predecessor to +carry the same loan set, so the fact vocabulary must not be able to open a loan +without also stating where it closes. + +While the block is open the owner cannot be re-borrowed (**OWN011**/**OWN012**), +used directly (**OWN013**), released (**OWN008**), or consumed/returned +(**OWN007**); these are the core's verdicts, mapped to the owner's fact handle. +The contract-inference layer reads a region as transparent (its body spliced in +place): a region runs exactly once, so it changes no inferred contract. + +A region is **fail-closed**. Each of the following is refused with an +`OwnIRError`, never lowered: + +- `owner` does not name a tracked local (a missing `owner`/`binding`/`body` is + refused as malformed); +- a `call` inside the block hands a tracked local to a callee that has no + ownership contract (elsewhere such a call is dropped: "no effect, no claim"); +- a `call` inside the block puts a tracked local on a position that carries no + ownership effect (the core's OWN041, which the bridge filters as an artifact); +- the function hands **any** argument to a `may`/`unknown` position: the + optimistic default (§5, advisory OWN051) is not available to a function that + opens a region. + +What a region does **not** check is stated as plainly: the core has no relation +between a token and the region it was minted in. A token acquired outside any +region, two tokens in one region, and a token spent against a different owner +are all accepted by the core; a frontend that uses regions for a state protocol +must make those shapes unrepresentable in what it emits. + ## 6. DI registration graph (`services[]`) An optional array feeding the **DI001** captive-dependency check (P-006), a diff --git a/spec/ownir.schema.json b/spec/ownir.schema.json index 34896ccd..04cbb060 100644 --- a/spec/ownir.schema.json +++ b/spec/ownir.schema.json @@ -8,7 +8,7 @@ "properties": { "ownir_version": { "description": "OwnIR fact-vocabulary version. Every producer stamps the same integer (OWNIR_VERSION in ownlang/ownir.py); a document whose version differs from the core's is rejected at load (spec/OwnIR.md IR1/IR2). An absent field is read as the current version by the core, but this schema requires it.", - "const": 0 + "const": 1 }, "module": { "description": "The extracted module/assembly name.", @@ -293,6 +293,31 @@ "body": { "type": "array", "items": { "$ref": "#/$defs/flowOp" } } }, "required": ["op"] + }, + { + "title": "move", + "description": "`var` takes ownership of `src`'s obligation (Let+Move); `src` is dead after it. Unlike alias_join the source does not stay owning.", + "properties": { + "op": { "const": "move" }, + "line": { "$ref": "#/$defs/sourceLine" }, + "column": { "$ref": "#/$defs/sourceColumn" }, + "var": { "type": "string" }, + "src": { "type": "string" } + }, + "required": ["op", "var", "src"] + }, + { + "title": "borrow_mut", + "description": "A block-scoped exclusive loan of the local `owner`, visible inside `body` as `binding` (a BorrowBlock). Compound on purpose: the vocabulary cannot open a loan without saying where it closes. Inside the block an unverifiable handoff of a tracked local is refused, not dropped.", + "properties": { + "op": { "const": "borrow_mut" }, + "line": { "$ref": "#/$defs/sourceLine" }, + "column": { "$ref": "#/$defs/sourceColumn" }, + "owner": { "type": "string", "minLength": 1 }, + "binding": { "type": "string", "minLength": 1 }, + "body": { "type": "array", "items": { "$ref": "#/$defs/flowOp" } } + }, + "required": ["op", "owner", "binding", "body"] } ] }, diff --git a/tests/fake_shadow_engine.py b/tests/fake_shadow_engine.py index e56ec912..0f5184bf 100755 --- a/tests/fake_shadow_engine.py +++ b/tests/fake_shadow_engine.py @@ -87,7 +87,7 @@ def main() -> int: target = os.environ.get("OWN_FAKE_ENGINE_REWRITE") if target: with open(target, "wb") as f: - f.write(b'{"ownir_version": 0, "module": "RewrittenUnderneath"}\n') + f.write(b'{"ownir_version": 1, "module": "RewrittenUnderneath"}\n') entry = _recorded_entry() entry["consumed"] = hash_bytes(raw) diff --git a/tests/fixtures/cli_ownir/bands-sarif-error-normal.case.json b/tests/fixtures/cli_ownir/bands-sarif-error-normal.case.json index 30b980ba..e464ca44 100644 --- a/tests/fixtures/cli_ownir/bands-sarif-error-normal.case.json +++ b/tests/fixtures/cli_ownir/bands-sarif-error-normal.case.json @@ -14,7 +14,7 @@ "env": {}, "expected": { "exit": 1, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n },\n {\n \"id\": \"OWN050\",\n \"shortDescription\": {\n \"text\": \"declaring type unresolved -- leakage analysis skipped\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN050\",\n \"level\": \"note\",\n \"message\": {\n \"text\": \"cannot verify 'ext.Unknown' \\u2014 its declaring type is an unresolved reference (build the project or pass references); leakage analysis skipped [resource: unresolved reference]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 13\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"unresolved reference\",\n \"component\": \"Vm\",\n \"event\": \"ext.Unknown\",\n \"handler\": \"OnU\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n },\n {\n \"id\": \"OWN050\",\n \"shortDescription\": {\n \"text\": \"declaring type unresolved -- leakage analysis skipped\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN050\",\n \"level\": \"note\",\n \"message\": {\n \"text\": \"cannot verify 'ext.Unknown' \\u2014 its declaring type is an unresolved reference (build the project or pass references); leakage analysis skipped [resource: unresolved reference]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 13\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"unresolved reference\",\n \"component\": \"Vm\",\n \"event\": \"ext.Unknown\",\n \"handler\": \"OnU\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", "stderr": "\n2 findings, 1 advisory (OWN050), 1 suppressed ([OwnIgnore]).\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/bands-sarif-error-quiet.case.json b/tests/fixtures/cli_ownir/bands-sarif-error-quiet.case.json index 36a2efa1..60e9278f 100644 --- a/tests/fixtures/cli_ownir/bands-sarif-error-quiet.case.json +++ b/tests/fixtures/cli_ownir/bands-sarif-error-quiet.case.json @@ -14,7 +14,7 @@ "env": {}, "expected": { "exit": 1, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", "stderr": "\n2 findings (1 advisory hidden), 1 suppressed ([OwnIgnore]).\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/bands-sarif-error-verbose.case.json b/tests/fixtures/cli_ownir/bands-sarif-error-verbose.case.json index 1c9e8fe1..da29f794 100644 --- a/tests/fixtures/cli_ownir/bands-sarif-error-verbose.case.json +++ b/tests/fixtures/cli_ownir/bands-sarif-error-verbose.case.json @@ -14,7 +14,7 @@ "env": {}, "expected": { "exit": 1, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n },\n {\n \"id\": \"OWN050\",\n \"shortDescription\": {\n \"text\": \"declaring type unresolved -- leakage analysis skipped\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN050\",\n \"level\": \"note\",\n \"message\": {\n \"text\": \"cannot verify 'ext.Unknown' \\u2014 its declaring type is an unresolved reference (build the project or pass references); leakage analysis skipped [resource: unresolved reference]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 13\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"unresolved reference\",\n \"component\": \"Vm\",\n \"event\": \"ext.Unknown\",\n \"handler\": \"OnU\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n },\n {\n \"id\": \"OWN050\",\n \"shortDescription\": {\n \"text\": \"declaring type unresolved -- leakage analysis skipped\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN050\",\n \"level\": \"note\",\n \"message\": {\n \"text\": \"cannot verify 'ext.Unknown' \\u2014 its declaring type is an unresolved reference (build the project or pass references); leakage analysis skipped [resource: unresolved reference]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 13\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"unresolved reference\",\n \"component\": \"Vm\",\n \"event\": \"ext.Unknown\",\n \"handler\": \"OnU\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", "stderr": "\n2 findings, 1 advisory (OWN050), 1 suppressed ([OwnIgnore]).\n by code: OWN001=3, OWN050=1\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/bands-sarif-warning-normal.case.json b/tests/fixtures/cli_ownir/bands-sarif-warning-normal.case.json index 72613d4d..d6dc0575 100644 --- a/tests/fixtures/cli_ownir/bands-sarif-warning-normal.case.json +++ b/tests/fixtures/cli_ownir/bands-sarif-warning-normal.case.json @@ -14,7 +14,7 @@ "env": {}, "expected": { "exit": 1, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n },\n {\n \"id\": \"OWN050\",\n \"shortDescription\": {\n \"text\": \"declaring type unresolved -- leakage analysis skipped\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN050\",\n \"level\": \"note\",\n \"message\": {\n \"text\": \"cannot verify 'ext.Unknown' \\u2014 its declaring type is an unresolved reference (build the project or pass references); leakage analysis skipped [resource: unresolved reference]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 13\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"unresolved reference\",\n \"component\": \"Vm\",\n \"event\": \"ext.Unknown\",\n \"handler\": \"OnU\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n },\n {\n \"id\": \"OWN050\",\n \"shortDescription\": {\n \"text\": \"declaring type unresolved -- leakage analysis skipped\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN050\",\n \"level\": \"note\",\n \"message\": {\n \"text\": \"cannot verify 'ext.Unknown' \\u2014 its declaring type is an unresolved reference (build the project or pass references); leakage analysis skipped [resource: unresolved reference]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 13\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"unresolved reference\",\n \"component\": \"Vm\",\n \"event\": \"ext.Unknown\",\n \"handler\": \"OnU\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", "stderr": "\n2 findings, 1 advisory (OWN050), 1 suppressed ([OwnIgnore]).\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/bands-sarif-warning-quiet.case.json b/tests/fixtures/cli_ownir/bands-sarif-warning-quiet.case.json index 696b01fb..19cb988e 100644 --- a/tests/fixtures/cli_ownir/bands-sarif-warning-quiet.case.json +++ b/tests/fixtures/cli_ownir/bands-sarif-warning-quiet.case.json @@ -14,7 +14,7 @@ "env": {}, "expected": { "exit": 1, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", "stderr": "\n2 findings (1 advisory hidden), 1 suppressed ([OwnIgnore]).\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/bands-sarif-warning-verbose.case.json b/tests/fixtures/cli_ownir/bands-sarif-warning-verbose.case.json index 630f612e..e0ba5250 100644 --- a/tests/fixtures/cli_ownir/bands-sarif-warning-verbose.case.json +++ b/tests/fixtures/cli_ownir/bands-sarif-warning-verbose.case.json @@ -14,7 +14,7 @@ "env": {}, "expected": { "exit": 1, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n },\n {\n \"id\": \"OWN050\",\n \"shortDescription\": {\n \"text\": \"declaring type unresolved -- leakage analysis skipped\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN050\",\n \"level\": \"note\",\n \"message\": {\n \"text\": \"cannot verify 'ext.Unknown' \\u2014 its declaring type is an unresolved reference (build the project or pass references); leakage analysis skipped [resource: unresolved reference]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 13\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"unresolved reference\",\n \"component\": \"Vm\",\n \"event\": \"ext.Unknown\",\n \"handler\": \"OnU\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n },\n {\n \"id\": \"OWN050\",\n \"shortDescription\": {\n \"text\": \"declaring type unresolved -- leakage analysis skipped\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Injected' is subscribed (handler 'OnI') but never unsubscribed; its source is an injected dependency whose lifetime is unknown, so it may outlive and keep 'Vm' alive (possible leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Injected\",\n \"handler\": \"OnI\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 11\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n },\n {\n \"ruleId\": \"OWN050\",\n \"level\": \"note\",\n \"message\": {\n \"text\": \"cannot verify 'ext.Unknown' \\u2014 its declaring type is an unresolved reference (build the project or pass references); leakage analysis skipped [resource: unresolved reference]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 13\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"unresolved reference\",\n \"component\": \"Vm\",\n \"event\": \"ext.Unknown\",\n \"handler\": \"OnU\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Ignored' is subscribed (handler 'OnG') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 12\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Ignored\",\n \"handler\": \"OnG\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", "stderr": "\n2 findings, 1 advisory (OWN050), 1 suppressed ([OwnIgnore]).\n by code: OWN001=3, OWN050=1\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/clean-sarif.case.json b/tests/fixtures/cli_ownir/clean-sarif.case.json index bd060f59..a78e26c9 100644 --- a/tests/fixtures/cli_ownir/clean-sarif.case.json +++ b/tests/fixtures/cli_ownir/clean-sarif.case.json @@ -10,7 +10,7 @@ "env": {}, "expected": { "exit": 0, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": []\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": []\n }\n ]\n}\n", "stderr": "../verdict_renders/render_empty.facts.json: ok — no subscription leaks found\n\n0 findings.\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/inputs/empty_ignore_reason.facts.json b/tests/fixtures/cli_ownir/inputs/empty_ignore_reason.facts.json index e09206bc..5ad23da6 100644 --- a/tests/fixtures/cli_ownir/inputs/empty_ignore_reason.facts.json +++ b/tests/fixtures/cli_ownir/inputs/empty_ignore_reason.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "EmptyReason", "_doc": [ "BR-V6: a reason-less [OwnIgnore] never suppresses. `ignore_reason` is", diff --git a/tests/fixtures/cli_ownir/inputs/nonascii_file.facts.json b/tests/fixtures/cli_ownir/inputs/nonascii_file.facts.json index cefc3c7a..6c64c8c1 100644 --- a/tests/fixtures/cli_ownir/inputs/nonascii_file.facts.json +++ b/tests/fixtures/cli_ownir/inputs/nonascii_file.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "NonAscii", "_doc": [ "A component whose FILE carries non-ASCII (and an astral character):", diff --git "a/tests/fixtures/cli_ownir/inputs/pa th \303\274n\303\257c\303\270d\303\251/facts.json" "b/tests/fixtures/cli_ownir/inputs/pa th \303\274n\303\257c\303\270d\303\251/facts.json" index 7f67d236..de27162e 100644 --- "a/tests/fixtures/cli_ownir/inputs/pa th \303\274n\303\257c\303\270d\303\251/facts.json" +++ "b/tests/fixtures/cli_ownir/inputs/pa th \303\274n\303\257c\303\270d\303\251/facts.json" @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "SpacedPath", "_doc": [ "Reached through a directory whose name carries a space and non-ASCII:", diff --git a/tests/fixtures/cli_ownir/inputs/suppressed_only.facts.json b/tests/fixtures/cli_ownir/inputs/suppressed_only.facts.json index 7ea3c91c..a0432bd4 100644 --- a/tests/fixtures/cli_ownir/inputs/suppressed_only.facts.json +++ b/tests/fixtures/cli_ownir/inputs/suppressed_only.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "SuppressedOnly", "_doc": [ "Every finding is [OwnIgnore]-suppressed: the run prints the `ok` line", diff --git a/tests/fixtures/cli_ownir/leaky-sarif-error.case.json b/tests/fixtures/cli_ownir/leaky-sarif-error.case.json index 1bc4fc99..f996a4f2 100644 --- a/tests/fixtures/cli_ownir/leaky-sarif-error.case.json +++ b/tests/fixtures/cli_ownir/leaky-sarif-error.case.json @@ -12,7 +12,7 @@ "env": {}, "expected": { "exit": 1, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Anchored' is subscribed (handler 'OnA') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 9,\n \"startColumn\": 17\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Anchored\",\n \"handler\": \"OnA\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Bare' is subscribed (handler 'OnB') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Bare\",\n \"handler\": \"OnB\"\n }\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Anchored' is subscribed (handler 'OnA') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 9,\n \"startColumn\": 17\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Anchored\",\n \"handler\": \"OnA\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Bare' is subscribed (handler 'OnB') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Bare\",\n \"handler\": \"OnB\"\n }\n }\n ]\n }\n ]\n}\n", "stderr": "\n2 findings.\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/leaky-sarif-warning.case.json b/tests/fixtures/cli_ownir/leaky-sarif-warning.case.json index d4a141c7..1720818c 100644 --- a/tests/fixtures/cli_ownir/leaky-sarif-warning.case.json +++ b/tests/fixtures/cli_ownir/leaky-sarif-warning.case.json @@ -12,7 +12,7 @@ "env": {}, "expected": { "exit": 1, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Anchored' is subscribed (handler 'OnA') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 9,\n \"startColumn\": 17\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Anchored\",\n \"handler\": \"OnA\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Bare' is subscribed (handler 'OnB') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Bare\",\n \"handler\": \"OnB\"\n }\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Anchored' is subscribed (handler 'OnA') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 9,\n \"startColumn\": 17\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Anchored\",\n \"handler\": \"OnA\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"warning\",\n \"message\": {\n \"text\": \"event 'bus.Bare' is subscribed (handler 'OnB') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Bare\",\n \"handler\": \"OnB\"\n }\n }\n ]\n }\n ]\n}\n", "stderr": "\n2 findings.\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/path-backslash-file-sarif.case.json b/tests/fixtures/cli_ownir/path-backslash-file-sarif.case.json index 84a6fcf3..8a0b95d7 100644 --- a/tests/fixtures/cli_ownir/path-backslash-file-sarif.case.json +++ b/tests/fixtures/cli_ownir/path-backslash-file-sarif.case.json @@ -10,7 +10,7 @@ "env": {}, "expected": { "exit": 1, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.50% off\\r\\nnext' is subscribed (handler 'OnE') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"src/gen:a,b.cs\"\n },\n \"region\": {\n \"startLine\": 7\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.50% off\\r\\nnext\",\n \"handler\": \"OnE\"\n }\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.50% off\\r\\nnext' is subscribed (handler 'OnE') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"src/gen:a,b.cs\"\n },\n \"region\": {\n \"startLine\": 7\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.50% off\\r\\nnext\",\n \"handler\": \"OnE\"\n }\n }\n ]\n }\n ]\n}\n", "stderr": "\n1 finding.\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/path-nonascii-file-sarif.case.json b/tests/fixtures/cli_ownir/path-nonascii-file-sarif.case.json index f9b4011a..ad9bd923 100644 --- a/tests/fixtures/cli_ownir/path-nonascii-file-sarif.case.json +++ b/tests/fixtures/cli_ownir/path-nonascii-file-sarif.case.json @@ -10,7 +10,7 @@ "env": {}, "expected": { "exit": 1, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"src/\\u00dcn\\u00efc\\u00f8d\\u00e9/\\u0412\\u044c\\u044e_\\ud83d\\ude00.cs\"\n },\n \"region\": {\n \"startLine\": 4\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"src/\\u00dcn\\u00efc\\u00f8d\\u00e9/\\u0412\\u044c\\u044e_\\ud83d\\ude00.cs\"\n },\n \"region\": {\n \"startLine\": 4\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n }\n }\n ]\n }\n ]\n}\n", "stderr": "\n1 finding.\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/refuse-version-mismatch.case.json b/tests/fixtures/cli_ownir/refuse-version-mismatch.case.json index 69bae7ac..dfce65c6 100644 --- a/tests/fixtures/cli_ownir/refuse-version-mismatch.case.json +++ b/tests/fixtures/cli_ownir/refuse-version-mismatch.case.json @@ -9,7 +9,7 @@ "expected": { "exit": 2, "stdout": "", - "stderr": "inputs/version_mismatch.facts.json: error: OwnIR facts are schema v99, but this core understands v0. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it.\n", + "stderr": "inputs/version_mismatch.facts.json: error: OwnIR facts are schema v99, but this core understands v1. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it.\n", "os_error_tail": null } } diff --git a/tests/fixtures/cli_ownir/sarif-ascii-escape-em-dash.case.json b/tests/fixtures/cli_ownir/sarif-ascii-escape-em-dash.case.json index 5ef6ffe3..20d8683a 100644 --- a/tests/fixtures/cli_ownir/sarif-ascii-escape-em-dash.case.json +++ b/tests/fixtures/cli_ownir/sarif-ascii-escape-em-dash.case.json @@ -10,7 +10,7 @@ "env": {}, "expected": { "exit": 1, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Anchored' is subscribed (handler 'OnA') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 9,\n \"startColumn\": 17\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Anchored\",\n \"handler\": \"OnA\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Bare' is subscribed (handler 'OnB') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Bare\",\n \"handler\": \"OnB\"\n }\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Anchored' is subscribed (handler 'OnA') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 9,\n \"startColumn\": 17\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Anchored\",\n \"handler\": \"OnA\"\n }\n },\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'bus.Bare' is subscribed (handler 'OnB') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 10\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"bus.Bare\",\n \"handler\": \"OnB\"\n }\n }\n ]\n }\n ]\n}\n", "stderr": "\n2 findings.\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/suppressed-only-sarif.case.json b/tests/fixtures/cli_ownir/suppressed-only-sarif.case.json index 03adfc2c..ff8723f4 100644 --- a/tests/fixtures/cli_ownir/suppressed-only-sarif.case.json +++ b/tests/fixtures/cli_ownir/suppressed-only-sarif.case.json @@ -10,7 +10,7 @@ "env": {}, "expected": { "exit": 0, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 7\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [\n {\n \"id\": \"OWN001\",\n \"shortDescription\": {\n \"text\": \"owned resource not released on all paths (possible leak)\"\n }\n }\n ],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": [\n {\n \"ruleId\": \"OWN001\",\n \"level\": \"error\",\n \"message\": {\n \"text\": \"event 'SystemEvents.Static' is subscribed (handler 'OnS') but never unsubscribed \\u2014 the source keeps 'Vm' alive (leak) [resource: subscription token]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"Vm.cs\"\n },\n \"region\": {\n \"startLine\": 7\n }\n }\n }\n ],\n \"properties\": {\n \"resourceKind\": \"subscription token\",\n \"component\": \"Vm\",\n \"event\": \"SystemEvents.Static\",\n \"handler\": \"OnS\"\n },\n \"suppressions\": [\n {\n \"kind\": \"inSource\",\n \"justification\": \"owned by the host container\"\n }\n ]\n }\n ]\n }\n ]\n}\n", "stderr": "inputs/suppressed_only.facts.json: ok — no subscription leaks found\n\n0 findings, 1 suppressed ([OwnIgnore]).\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/usage-duplicate-flag-last-wins.case.json b/tests/fixtures/cli_ownir/usage-duplicate-flag-last-wins.case.json index 9b003cdb..60e0fb9b 100644 --- a/tests/fixtures/cli_ownir/usage-duplicate-flag-last-wins.case.json +++ b/tests/fixtures/cli_ownir/usage-duplicate-flag-last-wins.case.json @@ -10,7 +10,7 @@ "env": {}, "expected": { "exit": 0, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": []\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": []\n }\n ]\n}\n", "stderr": "../verdict_renders/render_empty.facts.json: ok — no subscription leaks found\n\n0 findings.\n", "os_error_tail": null } diff --git a/tests/fixtures/cli_ownir/usage-equals-spelling-accepted.case.json b/tests/fixtures/cli_ownir/usage-equals-spelling-accepted.case.json index 9afa2ec3..61942cdd 100644 --- a/tests/fixtures/cli_ownir/usage-equals-spelling-accepted.case.json +++ b/tests/fixtures/cli_ownir/usage-equals-spelling-accepted.case.json @@ -9,7 +9,7 @@ "env": {}, "expected": { "exit": 0, - "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [],\n \"properties\": {\n \"ownirSchemaVersion\": 0\n }\n }\n },\n \"results\": []\n }\n ]\n}\n", + "stdout": "{\n \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n \"version\": \"2.1.0\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"name\": \"Owen\",\n \"informationUri\": \"https://github.com/PhysShell/Own.NET\",\n \"rules\": [],\n \"properties\": {\n \"ownirSchemaVersion\": 1\n }\n }\n },\n \"results\": []\n }\n ]\n}\n", "stderr": "../verdict_renders/render_empty.facts.json: ok — no subscription leaks found\n\n0 findings.\n", "os_error_tail": null } diff --git a/tests/fixtures/lowered/alias_join_cases.facts.json b/tests/fixtures/lowered/alias_join_cases.facts.json index 08a4ae2b..6f6124d8 100644 --- a/tests/fixtures/lowered/alias_join_cases.facts.json +++ b/tests/fixtures/lowered/alias_join_cases.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Alias", "functions": [ { diff --git a/tests/fixtures/lowered/alias_join_cases.golden.json b/tests/fixtures/lowered/alias_join_cases.golden.json index 80bc95bc..d7e687da 100644 --- a/tests/fixtures/lowered/alias_join_cases.golden.json +++ b/tests/fixtures/lowered/alias_join_cases.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Alias", "resources": [ { diff --git a/tests/fixtures/lowered/flow_kill_on_rebind.facts.json b/tests/fixtures/lowered/flow_kill_on_rebind.facts.json index 90f3ed80..1d65275e 100644 --- a/tests/fixtures/lowered/flow_kill_on_rebind.facts.json +++ b/tests/fixtures/lowered/flow_kill_on_rebind.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Rebind", "functions": [ { diff --git a/tests/fixtures/lowered/flow_kill_on_rebind.golden.json b/tests/fixtures/lowered/flow_kill_on_rebind.golden.json index d4791147..4d1b389e 100644 --- a/tests/fixtures/lowered/flow_kill_on_rebind.golden.json +++ b/tests/fixtures/lowered/flow_kill_on_rebind.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Rebind", "resources": [ { diff --git a/tests/fixtures/lowered/flow_unmapped_refs.facts.json b/tests/fixtures/lowered/flow_unmapped_refs.facts.json index 80bdd19b..93aa73b2 100644 --- a/tests/fixtures/lowered/flow_unmapped_refs.facts.json +++ b/tests/fixtures/lowered/flow_unmapped_refs.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Unmapped", "functions": [ { diff --git a/tests/fixtures/lowered/flow_unmapped_refs.golden.json b/tests/fixtures/lowered/flow_unmapped_refs.golden.json index 3a26751a..d79f4c55 100644 --- a/tests/fixtures/lowered/flow_unmapped_refs.golden.json +++ b/tests/fixtures/lowered/flow_unmapped_refs.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Unmapped", "resources": [ { diff --git a/tests/fixtures/lowered/fn_params_ordering.facts.json b/tests/fixtures/lowered/fn_params_ordering.facts.json index 32850b34..9bd29f2d 100644 --- a/tests/fixtures/lowered/fn_params_ordering.facts.json +++ b/tests/fixtures/lowered/fn_params_ordering.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Params", "functions": [ { diff --git a/tests/fixtures/lowered/fn_params_ordering.golden.json b/tests/fixtures/lowered/fn_params_ordering.golden.json index 65bb9e67..9cbc7363 100644 --- a/tests/fixtures/lowered/fn_params_ordering.golden.json +++ b/tests/fixtures/lowered/fn_params_ordering.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Params", "resources": [ { diff --git a/tests/fixtures/lowered/handles_global_counters.facts.json b/tests/fixtures/lowered/handles_global_counters.facts.json index d9aac604..94d7c7b6 100644 --- a/tests/fixtures/lowered/handles_global_counters.facts.json +++ b/tests/fixtures/lowered/handles_global_counters.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Counters", "components": [ { diff --git a/tests/fixtures/lowered/handles_global_counters.golden.json b/tests/fixtures/lowered/handles_global_counters.golden.json index 2eb1a399..6e503d3d 100644 --- a/tests/fixtures/lowered/handles_global_counters.golden.json +++ b/tests/fixtures/lowered/handles_global_counters.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Counters", "resources": [ { diff --git a/tests/fixtures/lowered/handles_null_metadata.facts.json b/tests/fixtures/lowered/handles_null_metadata.facts.json index e09d2e52..8f49eef2 100644 --- a/tests/fixtures/lowered/handles_null_metadata.facts.json +++ b/tests/fixtures/lowered/handles_null_metadata.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "NullMeta", "components": [ { diff --git a/tests/fixtures/lowered/handles_null_metadata.golden.json b/tests/fixtures/lowered/handles_null_metadata.golden.json index 1ecac10c..8eea0723 100644 --- a/tests/fixtures/lowered/handles_null_metadata.golden.json +++ b/tests/fixtures/lowered/handles_null_metadata.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "NullMeta", "resources": [ { diff --git a/tests/fixtures/lowered/hoist_neg_early_return.facts.json b/tests/fixtures/lowered/hoist_neg_early_return.facts.json index 7391db70..65e99f30 100644 --- a/tests/fixtures/lowered/hoist_neg_early_return.facts.json +++ b/tests/fixtures/lowered/hoist_neg_early_return.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "HoistNeg3", "functions": [ { diff --git a/tests/fixtures/lowered/hoist_neg_early_return.golden.json b/tests/fixtures/lowered/hoist_neg_early_return.golden.json index b71d10d5..fa00e419 100644 --- a/tests/fixtures/lowered/hoist_neg_early_return.golden.json +++ b/tests/fixtures/lowered/hoist_neg_early_return.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "HoistNeg3", "resources": [ { diff --git a/tests/fixtures/lowered/hoist_neg_nested_depth.facts.json b/tests/fixtures/lowered/hoist_neg_nested_depth.facts.json index dd6428b2..26eb4ec0 100644 --- a/tests/fixtures/lowered/hoist_neg_nested_depth.facts.json +++ b/tests/fixtures/lowered/hoist_neg_nested_depth.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "HoistNeg1", "functions": [ { diff --git a/tests/fixtures/lowered/hoist_neg_nested_depth.golden.json b/tests/fixtures/lowered/hoist_neg_nested_depth.golden.json index 63cbee3c..00446216 100644 --- a/tests/fixtures/lowered/hoist_neg_nested_depth.golden.json +++ b/tests/fixtures/lowered/hoist_neg_nested_depth.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "HoistNeg1", "resources": [ { diff --git a/tests/fixtures/lowered/hoist_neg_while_body.facts.json b/tests/fixtures/lowered/hoist_neg_while_body.facts.json index a55a0f16..4fd47302 100644 --- a/tests/fixtures/lowered/hoist_neg_while_body.facts.json +++ b/tests/fixtures/lowered/hoist_neg_while_body.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "HoistNeg2", "functions": [ { diff --git a/tests/fixtures/lowered/hoist_neg_while_body.golden.json b/tests/fixtures/lowered/hoist_neg_while_body.golden.json index 616bac85..86174ba4 100644 --- a/tests/fixtures/lowered/hoist_neg_while_body.golden.json +++ b/tests/fixtures/lowered/hoist_neg_while_body.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "HoistNeg2", "resources": [ { diff --git a/tests/fixtures/lowered/hoist_pool_kind.facts.json b/tests/fixtures/lowered/hoist_pool_kind.facts.json index 3bc2dad9..e4857ec9 100644 --- a/tests/fixtures/lowered/hoist_pool_kind.facts.json +++ b/tests/fixtures/lowered/hoist_pool_kind.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "HoistPool", "functions": [ { diff --git a/tests/fixtures/lowered/hoist_pool_kind.golden.json b/tests/fixtures/lowered/hoist_pool_kind.golden.json index 7a4c1418..b59f75dc 100644 --- a/tests/fixtures/lowered/hoist_pool_kind.golden.json +++ b/tests/fixtures/lowered/hoist_pool_kind.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "HoistPool", "resources": [ { diff --git a/tests/fixtures/lowered/hoist_positive_release.facts.json b/tests/fixtures/lowered/hoist_positive_release.facts.json index b047f4cb..ec736570 100644 --- a/tests/fixtures/lowered/hoist_positive_release.facts.json +++ b/tests/fixtures/lowered/hoist_positive_release.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Hoist", "functions": [ { diff --git a/tests/fixtures/lowered/hoist_positive_release.golden.json b/tests/fixtures/lowered/hoist_positive_release.golden.json index 9cbb385a..4b070e14 100644 --- a/tests/fixtures/lowered/hoist_positive_release.golden.json +++ b/tests/fixtures/lowered/hoist_positive_release.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Hoist", "resources": [ { diff --git a/tests/fixtures/lowered/hoist_positive_use_only.facts.json b/tests/fixtures/lowered/hoist_positive_use_only.facts.json index 1249f1f3..83a74b73 100644 --- a/tests/fixtures/lowered/hoist_positive_use_only.facts.json +++ b/tests/fixtures/lowered/hoist_positive_use_only.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "HoistUse", "functions": [ { diff --git a/tests/fixtures/lowered/hoist_positive_use_only.golden.json b/tests/fixtures/lowered/hoist_positive_use_only.golden.json index 8c7cefed..6e128b03 100644 --- a/tests/fixtures/lowered/hoist_positive_use_only.golden.json +++ b/tests/fixtures/lowered/hoist_positive_use_only.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "HoistUse", "resources": [ { diff --git a/tests/fixtures/lowered/lines_preserved.facts.json b/tests/fixtures/lowered/lines_preserved.facts.json index d4070dbf..740d921b 100644 --- a/tests/fixtures/lowered/lines_preserved.facts.json +++ b/tests/fixtures/lowered/lines_preserved.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Lines", "components": [ { diff --git a/tests/fixtures/lowered/lines_preserved.golden.json b/tests/fixtures/lowered/lines_preserved.golden.json index 8ec7dac5..4248d504 100644 --- a/tests/fixtures/lowered/lines_preserved.golden.json +++ b/tests/fixtures/lowered/lines_preserved.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Lines", "resources": [ { diff --git a/tests/fixtures/lowered/manifest.json b/tests/fixtures/lowered/manifest.json index 08a4ba54..6bb93d0c 100644 --- a/tests/fixtures/lowered/manifest.json +++ b/tests/fixtures/lowered/manifest.json @@ -1,6 +1,6 @@ { "comment": "The frozen Layer 2 case ledger (#259). Hand-maintained: adding or removing a case is a deliberate contract change, and the harness requires manifest == facts == goldens exactly, so a fixture pair cannot silently disappear. rust_replay: false marks a Python-only behavior snapshot pinning an open decision (see 'decision'); the Rust own-bridge replays every rust_replay: true case byte-for-byte.", - "lowered_version": 1, + "lowered_version": 2, "cases": [ { "name": "alias_join_cases", @@ -200,6 +200,271 @@ ], "rust_replay": true }, + { + "name": "typestate_cs_a1_alias_of_entity", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_a2_entity_through_indexer", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_g6_capability_not_spent", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l10_region_inside_try", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l11_region_beside_disposable", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l12_region_beside_leaked_disposable", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l13_values_captured_into_region", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l1_stale_capability", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l2_copied_capability", + "rules": [ + "BR-L12", + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l3_raw_mutation", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l4_raw_escape", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l5_normal_transition", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l6_two_transitions", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l6b_chained_transitions", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l7_read_after_region", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l8_branch_inside_region", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_l9_opaque_calls_around_region", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_cs_x1_nested_region_same_entity", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_ef_orderbackend", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_g6_capability_not_spent", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_l1_stale_capability", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_l2_copied_capability", + "rules": [ + "BR-L12", + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_l3_raw_entity_mutation", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_l3b_second_region", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_l4_raw_entity_unknown_call", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_l4b_raw_entity_shared_call", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_l4c_raw_entity_consumed", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_l4d_token_unknown_call", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_l5_normal_transition", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_l5b_two_transitions", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_l5c_read_after_region", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_n1_token_spent_on_other_entity", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_n2_two_tokens_one_region", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_n3_raw_mutation_inferred_effect", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_n4_token_outlives_region", + "rules": [ + "BR-L13" + ], + "rust_replay": true + }, + { + "name": "typestate_w1_loan_wordings", + "rules": [ + "BR-L12", + "BR-L13", + "BR-V4" + ], + "rust_replay": true + }, + { + "name": "typestate_w2_loan_wordings_pooled", + "rules": [ + "BR-L12", + "BR-L13", + "BR-V4" + ], + "rust_replay": true + }, { "name": "vocab_unknown_op", "rules": [ diff --git a/tests/fixtures/lowered/mos_call_channel_overload_sig.facts.json b/tests/fixtures/lowered/mos_call_channel_overload_sig.facts.json index 2eb9eec9..4bd22d8f 100644 --- a/tests/fixtures/lowered/mos_call_channel_overload_sig.facts.json +++ b/tests/fixtures/lowered/mos_call_channel_overload_sig.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Overloads", "functions": [ { diff --git a/tests/fixtures/lowered/mos_call_channel_overload_sig.golden.json b/tests/fixtures/lowered/mos_call_channel_overload_sig.golden.json index 5f89d2ec..5baf3c83 100644 --- a/tests/fixtures/lowered/mos_call_channel_overload_sig.golden.json +++ b/tests/fixtures/lowered/mos_call_channel_overload_sig.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Overloads", "resources": [ { diff --git a/tests/fixtures/lowered/mos_call_direct_consume.facts.json b/tests/fixtures/lowered/mos_call_direct_consume.facts.json index 06febc9d..3b00bf1f 100644 --- a/tests/fixtures/lowered/mos_call_direct_consume.facts.json +++ b/tests/fixtures/lowered/mos_call_direct_consume.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Direct", "functions": [ { diff --git a/tests/fixtures/lowered/mos_call_direct_consume.golden.json b/tests/fixtures/lowered/mos_call_direct_consume.golden.json index 5ec4317d..3b3fb7ab 100644 --- a/tests/fixtures/lowered/mos_call_direct_consume.golden.json +++ b/tests/fixtures/lowered/mos_call_direct_consume.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Direct", "resources": [ { diff --git a/tests/fixtures/lowered/mos_call_unknown_drop.facts.json b/tests/fixtures/lowered/mos_call_unknown_drop.facts.json index c225ca25..1ecbe439 100644 --- a/tests/fixtures/lowered/mos_call_unknown_drop.facts.json +++ b/tests/fixtures/lowered/mos_call_unknown_drop.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Drop", "functions": [ { diff --git a/tests/fixtures/lowered/mos_call_unknown_drop.golden.json b/tests/fixtures/lowered/mos_call_unknown_drop.golden.json index 5b7a3a52..b5383dea 100644 --- a/tests/fixtures/lowered/mos_call_unknown_drop.golden.json +++ b/tests/fixtures/lowered/mos_call_unknown_drop.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Drop", "resources": [ { diff --git a/tests/fixtures/lowered/mos_fresh_mint.facts.json b/tests/fixtures/lowered/mos_fresh_mint.facts.json index 8a6e910b..fcb16e30 100644 --- a/tests/fixtures/lowered/mos_fresh_mint.facts.json +++ b/tests/fixtures/lowered/mos_fresh_mint.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Fresh", "functions": [ { diff --git a/tests/fixtures/lowered/mos_fresh_mint.golden.json b/tests/fixtures/lowered/mos_fresh_mint.golden.json index eb875eed..2817d8e7 100644 --- a/tests/fixtures/lowered/mos_fresh_mint.golden.json +++ b/tests/fixtures/lowered/mos_fresh_mint.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Fresh", "resources": [ { diff --git a/tests/fixtures/lowered/mos_killsite_toplevel.facts.json b/tests/fixtures/lowered/mos_killsite_toplevel.facts.json index 4a28235c..18a69151 100644 --- a/tests/fixtures/lowered/mos_killsite_toplevel.facts.json +++ b/tests/fixtures/lowered/mos_killsite_toplevel.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "KillSite", "functions": [ { diff --git a/tests/fixtures/lowered/mos_killsite_toplevel.golden.json b/tests/fixtures/lowered/mos_killsite_toplevel.golden.json index 17b38129..897c0824 100644 --- a/tests/fixtures/lowered/mos_killsite_toplevel.golden.json +++ b/tests/fixtures/lowered/mos_killsite_toplevel.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "KillSite", "resources": [ { diff --git a/tests/fixtures/lowered/mos_untrack_inbranch.facts.json b/tests/fixtures/lowered/mos_untrack_inbranch.facts.json index 7623d084..78b93728 100644 --- a/tests/fixtures/lowered/mos_untrack_inbranch.facts.json +++ b/tests/fixtures/lowered/mos_untrack_inbranch.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Untrack", "functions": [ { diff --git a/tests/fixtures/lowered/mos_untrack_inbranch.golden.json b/tests/fixtures/lowered/mos_untrack_inbranch.golden.json index 68248fda..057496bb 100644 --- a/tests/fixtures/lowered/mos_untrack_inbranch.golden.json +++ b/tests/fixtures/lowered/mos_untrack_inbranch.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "Untrack", "resources": [ { diff --git a/tests/fixtures/lowered/routing_r1_unresolved.facts.json b/tests/fixtures/lowered/routing_r1_unresolved.facts.json index 51734477..aa5da99e 100644 --- a/tests/fixtures/lowered/routing_r1_unresolved.facts.json +++ b/tests/fixtures/lowered/routing_r1_unresolved.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "R1", "components": [ { diff --git a/tests/fixtures/lowered/routing_r1_unresolved.golden.json b/tests/fixtures/lowered/routing_r1_unresolved.golden.json index c1faff33..3164f9fb 100644 --- a/tests/fixtures/lowered/routing_r1_unresolved.golden.json +++ b/tests/fixtures/lowered/routing_r1_unresolved.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "R1", "resources": [ { diff --git a/tests/fixtures/lowered/routing_r2_subscribe_self.facts.json b/tests/fixtures/lowered/routing_r2_subscribe_self.facts.json index c88e1789..1a9a4879 100644 --- a/tests/fixtures/lowered/routing_r2_subscribe_self.facts.json +++ b/tests/fixtures/lowered/routing_r2_subscribe_self.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "R2", "components": [ { diff --git a/tests/fixtures/lowered/routing_r2_subscribe_self.golden.json b/tests/fixtures/lowered/routing_r2_subscribe_self.golden.json index 4abbe0de..b19f88e2 100644 --- a/tests/fixtures/lowered/routing_r2_subscribe_self.golden.json +++ b/tests/fixtures/lowered/routing_r2_subscribe_self.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "R2", "resources": [ { diff --git a/tests/fixtures/lowered/routing_r3_capture_static.facts.json b/tests/fixtures/lowered/routing_r3_capture_static.facts.json index 2beca0c8..74077eab 100644 --- a/tests/fixtures/lowered/routing_r3_capture_static.facts.json +++ b/tests/fixtures/lowered/routing_r3_capture_static.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "R3", "components": [ { diff --git a/tests/fixtures/lowered/routing_r3_capture_static.golden.json b/tests/fixtures/lowered/routing_r3_capture_static.golden.json index cff46efd..b2cf0552 100644 --- a/tests/fixtures/lowered/routing_r3_capture_static.golden.json +++ b/tests/fixtures/lowered/routing_r3_capture_static.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "R3", "resources": [ { diff --git a/tests/fixtures/lowered/routing_r4_returned_fresh.facts.json b/tests/fixtures/lowered/routing_r4_returned_fresh.facts.json index a4fa4707..adb55f8d 100644 --- a/tests/fixtures/lowered/routing_r4_returned_fresh.facts.json +++ b/tests/fixtures/lowered/routing_r4_returned_fresh.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "R4", "components": [ { diff --git a/tests/fixtures/lowered/routing_r4_returned_fresh.golden.json b/tests/fixtures/lowered/routing_r4_returned_fresh.golden.json index c3726089..fbaf66ec 100644 --- a/tests/fixtures/lowered/routing_r4_returned_fresh.golden.json +++ b/tests/fixtures/lowered/routing_r4_returned_fresh.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "R4", "resources": [ { diff --git a/tests/fixtures/lowered/routing_r5_di_capture.facts.json b/tests/fixtures/lowered/routing_r5_di_capture.facts.json index 24617c4d..d3e70e6b 100644 --- a/tests/fixtures/lowered/routing_r5_di_capture.facts.json +++ b/tests/fixtures/lowered/routing_r5_di_capture.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "R5", "components": [ { diff --git a/tests/fixtures/lowered/routing_r5_di_capture.golden.json b/tests/fixtures/lowered/routing_r5_di_capture.golden.json index ebfdbda2..3d572e00 100644 --- a/tests/fixtures/lowered/routing_r5_di_capture.golden.json +++ b/tests/fixtures/lowered/routing_r5_di_capture.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "R5", "resources": [ { diff --git a/tests/fixtures/lowered/routing_r6_token_kinds.facts.json b/tests/fixtures/lowered/routing_r6_token_kinds.facts.json index cc9dcc94..5e87ce03 100644 --- a/tests/fixtures/lowered/routing_r6_token_kinds.facts.json +++ b/tests/fixtures/lowered/routing_r6_token_kinds.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "R6", "components": [ { diff --git a/tests/fixtures/lowered/routing_r6_token_kinds.golden.json b/tests/fixtures/lowered/routing_r6_token_kinds.golden.json index fd157e1a..18e2c5cb 100644 --- a/tests/fixtures/lowered/routing_r6_token_kinds.golden.json +++ b/tests/fixtures/lowered/routing_r6_token_kinds.golden.json @@ -1,5 +1,5 @@ { - "lowered_version": 1, + "lowered_version": 2, "module": "R6", "resources": [ { diff --git a/tests/fixtures/lowered/tolerant_unknown_kind.facts.json b/tests/fixtures/lowered/tolerant_unknown_kind.facts.json index 1a60649e..ed817928 100644 --- a/tests/fixtures/lowered/tolerant_unknown_kind.facts.json +++ b/tests/fixtures/lowered/tolerant_unknown_kind.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "OD2", "components": [ { diff --git a/tests/fixtures/lowered/tolerant_unknown_kind.golden.json b/tests/fixtures/lowered/tolerant_unknown_kind.golden.json index 4f4692cf..273fb9c7 100644 --- a/tests/fixtures/lowered/tolerant_unknown_kind.golden.json +++ b/tests/fixtures/lowered/tolerant_unknown_kind.golden.json @@ -1,4 +1,4 @@ { - "lowered_version": 1, + "lowered_version": 2, "error": "unknown resource kind 'mutex' — a new kind is a vocabulary change that must bump OWNIR_VERSION (see spec/OwnIR.md §2)" } diff --git a/tests/fixtures/lowered/typestate_cs_a1_alias_of_entity.facts.json b/tests/fixtures/lowered/typestate_cs_a1_alias_of_entity.facts.json new file mode 100644 index 00000000..6d4eee76 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_a1_alias_of_entity.facts.json @@ -0,0 +1,79 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.A1AliasOfEntity.Run", + "file": "frontend/roslyn/protocol-samples/cases/A1_alias_of_entity.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 15 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 15, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 15 + }, + { + "op": "use", + "var": "order", + "line": 17 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 18 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 15 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_a1_alias_of_entity.golden.json b/tests/fixtures/lowered/typestate_cs_a1_alias_of_entity.golden.json new file mode 100644 index 00000000..ec3a2a4e --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_a1_alias_of_entity.golden.json @@ -0,0 +1,215 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.A1AliasOfEntity.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 15 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 15 + }, + { + "stmt": "use", + "handle": "loc_0", + "line": 17 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 18 + } + ], + "line": 15 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 15 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_3", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.A1AliasOfEntity.Run", + "file": "frontend/roslyn/protocol-samples/cases/A1_alias_of_entity.cs", + "line": 15, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.A1AliasOfEntity.Run", + "file": "frontend/roslyn/protocol-samples/cases/A1_alias_of_entity.cs", + "line": 15, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_3", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_a2_entity_through_indexer.facts.json b/tests/fixtures/lowered/typestate_cs_a2_entity_through_indexer.facts.json new file mode 100644 index 00000000..1d232591 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_a2_entity_through_indexer.facts.json @@ -0,0 +1,79 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.A2EntityThroughIndexer.Run", + "file": "frontend/roslyn/protocol-samples/cases/A2_entity_through_indexer.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 15 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 15, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 15 + }, + { + "op": "use", + "var": "order", + "line": 17 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 18 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 15 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_a2_entity_through_indexer.golden.json b/tests/fixtures/lowered/typestate_cs_a2_entity_through_indexer.golden.json new file mode 100644 index 00000000..7aacf30e --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_a2_entity_through_indexer.golden.json @@ -0,0 +1,215 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.A2EntityThroughIndexer.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 15 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 15 + }, + { + "stmt": "use", + "handle": "loc_0", + "line": 17 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 18 + } + ], + "line": 15 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 15 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_3", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.A2EntityThroughIndexer.Run", + "file": "frontend/roslyn/protocol-samples/cases/A2_entity_through_indexer.cs", + "line": 15, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.A2EntityThroughIndexer.Run", + "file": "frontend/roslyn/protocol-samples/cases/A2_entity_through_indexer.cs", + "line": 15, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_3", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_g6_capability_not_spent.facts.json b/tests/fixtures/lowered/typestate_cs_g6_capability_not_spent.facts.json new file mode 100644 index 00000000..7ad4bfcb --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_g6_capability_not_spent.facts.json @@ -0,0 +1,42 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.G6CapabilityNotSpent.Run", + "file": "frontend/roslyn/protocol-samples/cases/G6_capability_not_spent.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 13 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 13, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 13 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 13 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_g6_capability_not_spent.golden.json b/tests/fixtures/lowered/typestate_cs_g6_capability_not_spent.golden.json new file mode 100644 index 00000000..43018bdf --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_g6_capability_not_spent.golden.json @@ -0,0 +1,149 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.G6CapabilityNotSpent.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 13 + } + ], + "line": 13 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 13 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.G6CapabilityNotSpent.Run", + "file": "frontend/roslyn/protocol-samples/cases/G6_capability_not_spent.cs", + "line": 13, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.G6CapabilityNotSpent.Run", + "file": "frontend/roslyn/protocol-samples/cases/G6_capability_not_spent.cs", + "line": 13, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l10_region_inside_try.facts.json b/tests/fixtures/lowered/typestate_cs_l10_region_inside_try.facts.json new file mode 100644 index 00000000..7ee861d3 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l10_region_inside_try.facts.json @@ -0,0 +1,74 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L10RegionInsideTry.Run", + "file": "frontend/roslyn/protocol-samples/cases/L10_region_inside_try.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 21 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 21, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 21 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 23 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 21 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l10_region_inside_try.golden.json b/tests/fixtures/lowered/typestate_cs_l10_region_inside_try.golden.json new file mode 100644 index 00000000..da06b74e --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l10_region_inside_try.golden.json @@ -0,0 +1,210 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L10RegionInsideTry.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 21 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 21 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 23 + } + ], + "line": 21 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 21 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_3", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L10RegionInsideTry.Run", + "file": "frontend/roslyn/protocol-samples/cases/L10_region_inside_try.cs", + "line": 21, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L10RegionInsideTry.Run", + "file": "frontend/roslyn/protocol-samples/cases/L10_region_inside_try.cs", + "line": 21, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_3", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l11_region_beside_disposable.facts.json b/tests/fixtures/lowered/typestate_cs_l11_region_beside_disposable.facts.json new file mode 100644 index 00000000..6b5588d8 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l11_region_beside_disposable.facts.json @@ -0,0 +1,98 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L11RegionBesideDisposable.Run", + "file": "frontend/roslyn/protocol-samples/cases/L11_region_beside_disposable.cs", + "sig": "Own.Protocols.Sample.Order", + "body": [ + { + "op": "acquire", + "var": "log", + "line": 15, + "column": 13, + "kind": "disposable" + }, + { + "op": "use", + "var": "log", + "line": 16 + }, + { + "op": "release", + "var": "log", + "line": 21 + } + ] + }, + { + "name": "Own.Protocols.Cases.L11RegionBesideDisposable.Run$protocol", + "file": "frontend/roslyn/protocol-samples/cases/L11_region_beside_disposable.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 17 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 17, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 17 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 19 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 17 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 1, + "methods_flow_analysed": 1, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l11_region_beside_disposable.golden.json b/tests/fixtures/lowered/typestate_cs_l11_region_beside_disposable.golden.json new file mode 100644 index 00000000..8ccba53e --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l11_region_beside_disposable.golden.json @@ -0,0 +1,244 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L11RegionBesideDisposable.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 15 + }, + { + "stmt": "use", + "handle": "loc_0", + "line": 16 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 21 + } + ] + }, + { + "name": "Own.Protocols.Cases.L11RegionBesideDisposable.Run$protocol", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_1", + "resource": "Disposable", + "line": 17 + }, + { + "stmt": "borrow_mut", + "owner": "loc_1", + "binding": "brw_2", + "body": [ + { + "stmt": "acquire", + "handle": "loc_3", + "resource": "Disposable", + "line": 17 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_3", + "brw_2" + ], + "line": 19 + } + ], + "line": 17 + }, + { + "stmt": "release", + "handle": "loc_1", + "line": 17 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L11RegionBesideDisposable.Run", + "file": "frontend/roslyn/protocol-samples/cases/L11_region_beside_disposable.cs", + "line": 15, + "event": "log", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_1", + "component": "Own.Protocols.Cases.L11RegionBesideDisposable.Run$protocol", + "file": "frontend/roslyn/protocol-samples/cases/L11_region_beside_disposable.cs", + "line": 17, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_3", + "component": "Own.Protocols.Cases.L11RegionBesideDisposable.Run$protocol", + "file": "frontend/roslyn/protocol-samples/cases/L11_region_beside_disposable.cs", + "line": 17, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l12_region_beside_leaked_disposable.facts.json b/tests/fixtures/lowered/typestate_cs_l12_region_beside_leaked_disposable.facts.json new file mode 100644 index 00000000..8908084c --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l12_region_beside_leaked_disposable.facts.json @@ -0,0 +1,93 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L12RegionBesideLeakedDisposable.Run", + "file": "frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs", + "sig": "Own.Protocols.Sample.Order", + "body": [ + { + "op": "acquire", + "var": "log", + "line": 14, + "column": 13, + "kind": "disposable" + }, + { + "op": "use", + "var": "log", + "line": 15 + } + ] + }, + { + "name": "Own.Protocols.Cases.L12RegionBesideLeakedDisposable.Run$protocol", + "file": "frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 16 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 16, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 16 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 18 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 16 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 1, + "methods_flow_analysed": 1, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l12_region_beside_leaked_disposable.golden.json b/tests/fixtures/lowered/typestate_cs_l12_region_beside_leaked_disposable.golden.json new file mode 100644 index 00000000..d9c2e77d --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l12_region_beside_leaked_disposable.golden.json @@ -0,0 +1,239 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L12RegionBesideLeakedDisposable.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 14 + }, + { + "stmt": "use", + "handle": "loc_0", + "line": 15 + } + ] + }, + { + "name": "Own.Protocols.Cases.L12RegionBesideLeakedDisposable.Run$protocol", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_1", + "resource": "Disposable", + "line": 16 + }, + { + "stmt": "borrow_mut", + "owner": "loc_1", + "binding": "brw_2", + "body": [ + { + "stmt": "acquire", + "handle": "loc_3", + "resource": "Disposable", + "line": 16 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_3", + "brw_2" + ], + "line": 18 + } + ], + "line": 16 + }, + { + "stmt": "release", + "handle": "loc_1", + "line": 16 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L12RegionBesideLeakedDisposable.Run", + "file": "frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs", + "line": 14, + "event": "log", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_1", + "component": "Own.Protocols.Cases.L12RegionBesideLeakedDisposable.Run$protocol", + "file": "frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs", + "line": 16, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_3", + "component": "Own.Protocols.Cases.L12RegionBesideLeakedDisposable.Run$protocol", + "file": "frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs", + "line": 16, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l13_values_captured_into_region.facts.json b/tests/fixtures/lowered/typestate_cs_l13_values_captured_into_region.facts.json new file mode 100644 index 00000000..813bda94 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l13_values_captured_into_region.facts.json @@ -0,0 +1,96 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L13ValuesCapturedIntoRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L13_values_captured_into_region.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 15 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 15, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 15 + }, + { + "op": "use", + "var": "approved", + "line": 17 + }, + { + "op": "if", + "line": 18, + "then": [ + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 20 + } + ], + "else": [ + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 24 + } + ] + } + ] + }, + { + "op": "release", + "var": "order", + "line": 15 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l13_values_captured_into_region.golden.json b/tests/fixtures/lowered/typestate_cs_l13_values_captured_into_region.golden.json new file mode 100644 index 00000000..522f3865 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l13_values_captured_into_region.golden.json @@ -0,0 +1,233 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L13ValuesCapturedIntoRegion.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 15 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 15 + }, + { + "stmt": "use", + "handle": "loc_2", + "line": 17 + }, + { + "stmt": "if", + "cond": "?", + "then": [ + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 20 + } + ], + "else": [ + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 24 + } + ], + "line": 18 + } + ], + "line": 15 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 15 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_3", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L13ValuesCapturedIntoRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L13_values_captured_into_region.cs", + "line": 15, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L13ValuesCapturedIntoRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L13_values_captured_into_region.cs", + "line": 15, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_3", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l1_stale_capability.facts.json b/tests/fixtures/lowered/typestate_cs_l1_stale_capability.facts.json new file mode 100644 index 00000000..733b1354 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l1_stale_capability.facts.json @@ -0,0 +1,143 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L1StaleCapability.Run", + "file": "frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 13 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 13, + "body": [ + { + "op": "acquire", + "var": "draft", + "line": 13 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Submit", + "args": [ + "draft", + "order.region1" + ], + "line": 15 + }, + { + "op": "acquire", + "var": "submitted", + "line": 15 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Cancel", + "args": [ + "draft", + "order.region1" + ], + "line": 16 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.SubmittedOrder.Approve", + "args": [ + "submitted", + "order.region1" + ], + "line": 17 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 13 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Cancel", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 57 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 57 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 57 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 51 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 51 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 51 + } + ] + }, + { + "name": "Own.Protocols.Sample.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 69 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 69 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 69 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l1_stale_capability.golden.json b/tests/fixtures/lowered/typestate_cs_l1_stale_capability.golden.json new file mode 100644 index 00000000..fc17d984 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l1_stale_capability.golden.json @@ -0,0 +1,348 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L1StaleCapability.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Submit", + "args": [ + "loc_2", + "brw_1" + ], + "line": 15 + }, + { + "stmt": "acquire", + "handle": "loc_3", + "resource": "Disposable", + "line": 15 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Cancel", + "args": [ + "loc_2", + "brw_1" + ], + "line": 16 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.SubmittedOrder.Approve", + "args": [ + "loc_3", + "brw_1" + ], + "line": 17 + } + ], + "line": 13 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 13 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Cancel", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 57, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 57, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 57 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 51, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 51, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 51 + } + ] + }, + { + "name": "Own.Protocols.Sample.SubmittedOrder.Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 69, + "lifetime": null + }, + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 69, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_8", + "line": 69 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L1StaleCapability.Run", + "file": "frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs", + "line": 13, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L1StaleCapability.Run", + "file": "frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs", + "line": 13, + "event": "draft", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_3", + "component": "Own.Protocols.Cases.L1StaleCapability.Run", + "file": "frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs", + "line": 15, + "event": "submitted", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.DraftOrder.Cancel", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 57, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Own.Protocols.Sample.DraftOrder.Cancel", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 57, + "event": "entity", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "Own.Protocols.Sample.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 51, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "Own.Protocols.Sample.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 51, + "event": "entity", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "Own.Protocols.Sample.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 69, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_9", + "component": "Own.Protocols.Sample.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 69, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l2_copied_capability.facts.json b/tests/fixtures/lowered/typestate_cs_l2_copied_capability.facts.json new file mode 100644 index 00000000..eb4bac95 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l2_copied_capability.facts.json @@ -0,0 +1,89 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L2CopiedCapability.Run", + "file": "frontend/roslyn/protocol-samples/cases/L2_copied_capability.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 13 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 13, + "body": [ + { + "op": "acquire", + "var": "draft", + "line": 13 + }, + { + "op": "move", + "var": "copy", + "src": "draft", + "line": 15 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Cancel", + "args": [ + "draft", + "order.region1" + ], + "line": 16 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Cancel", + "args": [ + "copy", + "order.region1" + ], + "line": 17 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 13 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Cancel", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 57 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 57 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 57 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l2_copied_capability.golden.json b/tests/fixtures/lowered/typestate_cs_l2_copied_capability.golden.json new file mode 100644 index 00000000..bd595c5a --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l2_copied_capability.golden.json @@ -0,0 +1,235 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L2CopiedCapability.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "move", + "handle": "loc_3", + "src": "loc_2", + "line": 15 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Cancel", + "args": [ + "loc_2", + "brw_1" + ], + "line": 16 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Cancel", + "args": [ + "loc_3", + "brw_1" + ], + "line": 17 + } + ], + "line": 13 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 13 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Cancel", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 57, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 57, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 57 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L2CopiedCapability.Run", + "file": "frontend/roslyn/protocol-samples/cases/L2_copied_capability.cs", + "line": 13, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L2CopiedCapability.Run", + "file": "frontend/roslyn/protocol-samples/cases/L2_copied_capability.cs", + "line": 13, + "event": "draft", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_3", + "component": "Own.Protocols.Cases.L2CopiedCapability.Run", + "file": "frontend/roslyn/protocol-samples/cases/L2_copied_capability.cs", + "line": 15, + "event": "copy", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.DraftOrder.Cancel", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 57, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Own.Protocols.Sample.DraftOrder.Cancel", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 57, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l3_raw_mutation.facts.json b/tests/fixtures/lowered/typestate_cs_l3_raw_mutation.facts.json new file mode 100644 index 00000000..f625405d --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l3_raw_mutation.facts.json @@ -0,0 +1,79 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L3RawMutation.Run", + "file": "frontend/roslyn/protocol-samples/cases/L3_raw_mutation.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 13 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 13, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 13 + }, + { + "op": "use", + "var": "order", + "line": 15 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 16 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 13 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l3_raw_mutation.golden.json b/tests/fixtures/lowered/typestate_cs_l3_raw_mutation.golden.json new file mode 100644 index 00000000..48e7ec4b --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l3_raw_mutation.golden.json @@ -0,0 +1,215 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L3RawMutation.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "use", + "handle": "loc_0", + "line": 15 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 16 + } + ], + "line": 13 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 13 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_3", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L3RawMutation.Run", + "file": "frontend/roslyn/protocol-samples/cases/L3_raw_mutation.cs", + "line": 13, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L3RawMutation.Run", + "file": "frontend/roslyn/protocol-samples/cases/L3_raw_mutation.cs", + "line": 13, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_3", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l4_raw_escape.facts.json b/tests/fixtures/lowered/typestate_cs_l4_raw_escape.facts.json new file mode 100644 index 00000000..0b31942f --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l4_raw_escape.facts.json @@ -0,0 +1,79 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L4RawEscape.Run", + "file": "frontend/roslyn/protocol-samples/cases/L4_raw_escape.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 13 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 13, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 13 + }, + { + "op": "use", + "var": "order", + "line": 15 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 16 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 13 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l4_raw_escape.golden.json b/tests/fixtures/lowered/typestate_cs_l4_raw_escape.golden.json new file mode 100644 index 00000000..3d26b354 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l4_raw_escape.golden.json @@ -0,0 +1,215 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L4RawEscape.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "use", + "handle": "loc_0", + "line": 15 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 16 + } + ], + "line": 13 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 13 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_3", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L4RawEscape.Run", + "file": "frontend/roslyn/protocol-samples/cases/L4_raw_escape.cs", + "line": 13, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L4RawEscape.Run", + "file": "frontend/roslyn/protocol-samples/cases/L4_raw_escape.cs", + "line": 13, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_3", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l5_normal_transition.facts.json b/tests/fixtures/lowered/typestate_cs_l5_normal_transition.facts.json new file mode 100644 index 00000000..532f9b78 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l5_normal_transition.facts.json @@ -0,0 +1,74 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L5NormalTransition.Run", + "file": "frontend/roslyn/protocol-samples/cases/L5_normal_transition.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 12 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 12, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 12 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 14 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 12 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l5_normal_transition.golden.json b/tests/fixtures/lowered/typestate_cs_l5_normal_transition.golden.json new file mode 100644 index 00000000..dc84db51 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l5_normal_transition.golden.json @@ -0,0 +1,210 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L5NormalTransition.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 14 + } + ], + "line": 12 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 12 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_3", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L5NormalTransition.Run", + "file": "frontend/roslyn/protocol-samples/cases/L5_normal_transition.cs", + "line": 12, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L5NormalTransition.Run", + "file": "frontend/roslyn/protocol-samples/cases/L5_normal_transition.cs", + "line": 12, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_3", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l6_two_transitions.facts.json b/tests/fixtures/lowered/typestate_cs_l6_two_transitions.facts.json new file mode 100644 index 00000000..a67ac375 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l6_two_transitions.facts.json @@ -0,0 +1,111 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L6TwoTransitions.Run", + "file": "frontend/roslyn/protocol-samples/cases/L6_two_transitions.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 12 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 12, + "body": [ + { + "op": "acquire", + "var": "draft", + "line": 12 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Submit", + "args": [ + "draft", + "order.region1" + ], + "line": 14 + }, + { + "op": "acquire", + "var": "submitted", + "line": 14 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.SubmittedOrder.Approve", + "args": [ + "submitted", + "order.region1" + ], + "line": 15 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 12 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 51 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 51 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 51 + } + ] + }, + { + "name": "Own.Protocols.Sample.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 69 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 69 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 69 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l6_two_transitions.golden.json b/tests/fixtures/lowered/typestate_cs_l6_two_transitions.golden.json new file mode 100644 index 00000000..68d77eea --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l6_two_transitions.golden.json @@ -0,0 +1,287 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L6TwoTransitions.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Submit", + "args": [ + "loc_2", + "brw_1" + ], + "line": 14 + }, + { + "stmt": "acquire", + "handle": "loc_3", + "resource": "Disposable", + "line": 14 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.SubmittedOrder.Approve", + "args": [ + "loc_3", + "brw_1" + ], + "line": 15 + } + ], + "line": 12 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 12 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 51, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 51, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 51 + } + ] + }, + { + "name": "Own.Protocols.Sample.SubmittedOrder.Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 69, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 69, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 69 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L6TwoTransitions.Run", + "file": "frontend/roslyn/protocol-samples/cases/L6_two_transitions.cs", + "line": 12, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L6TwoTransitions.Run", + "file": "frontend/roslyn/protocol-samples/cases/L6_two_transitions.cs", + "line": 12, + "event": "draft", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_3", + "component": "Own.Protocols.Cases.L6TwoTransitions.Run", + "file": "frontend/roslyn/protocol-samples/cases/L6_two_transitions.cs", + "line": 14, + "event": "submitted", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 51, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Own.Protocols.Sample.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 51, + "event": "entity", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "Own.Protocols.Sample.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 69, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "Own.Protocols.Sample.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 69, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l6b_chained_transitions.facts.json b/tests/fixtures/lowered/typestate_cs_l6b_chained_transitions.facts.json new file mode 100644 index 00000000..f4a08ce2 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l6b_chained_transitions.facts.json @@ -0,0 +1,111 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L6bChainedTransitions.Run", + "file": "frontend/roslyn/protocol-samples/cases/L6b_chained_transitions.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 12 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 12, + "body": [ + { + "op": "acquire", + "var": "draft", + "line": 12 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Submit", + "args": [ + "draft", + "order.region1" + ], + "line": 12 + }, + { + "op": "acquire", + "var": "$t2", + "line": 12 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.SubmittedOrder.Approve", + "args": [ + "$t2", + "order.region1" + ], + "line": 12 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 12 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 51 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 51 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 51 + } + ] + }, + { + "name": "Own.Protocols.Sample.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 69 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 69 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 69 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l6b_chained_transitions.golden.json b/tests/fixtures/lowered/typestate_cs_l6b_chained_transitions.golden.json new file mode 100644 index 00000000..bd3eb0b6 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l6b_chained_transitions.golden.json @@ -0,0 +1,287 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L6bChainedTransitions.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Submit", + "args": [ + "loc_2", + "brw_1" + ], + "line": 12 + }, + { + "stmt": "acquire", + "handle": "loc_3", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.SubmittedOrder.Approve", + "args": [ + "loc_3", + "brw_1" + ], + "line": 12 + } + ], + "line": 12 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 12 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 51, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 51, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 51 + } + ] + }, + { + "name": "Own.Protocols.Sample.SubmittedOrder.Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 69, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 69, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 69 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L6bChainedTransitions.Run", + "file": "frontend/roslyn/protocol-samples/cases/L6b_chained_transitions.cs", + "line": 12, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L6bChainedTransitions.Run", + "file": "frontend/roslyn/protocol-samples/cases/L6b_chained_transitions.cs", + "line": 12, + "event": "draft", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_3", + "component": "Own.Protocols.Cases.L6bChainedTransitions.Run", + "file": "frontend/roslyn/protocol-samples/cases/L6b_chained_transitions.cs", + "line": 12, + "event": "$t2", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 51, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Own.Protocols.Sample.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 51, + "event": "entity", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "Own.Protocols.Sample.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 69, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "Own.Protocols.Sample.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 69, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l7_read_after_region.facts.json b/tests/fixtures/lowered/typestate_cs_l7_read_after_region.facts.json new file mode 100644 index 00000000..29500f8c --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l7_read_after_region.facts.json @@ -0,0 +1,74 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L7ReadAfterRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L7_read_after_region.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 12 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 12, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 12 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 14 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 12 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l7_read_after_region.golden.json b/tests/fixtures/lowered/typestate_cs_l7_read_after_region.golden.json new file mode 100644 index 00000000..9c8e9e91 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l7_read_after_region.golden.json @@ -0,0 +1,210 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L7ReadAfterRegion.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 14 + } + ], + "line": 12 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 12 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_3", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L7ReadAfterRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L7_read_after_region.cs", + "line": 12, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L7ReadAfterRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L7_read_after_region.cs", + "line": 12, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_3", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l8_branch_inside_region.facts.json b/tests/fixtures/lowered/typestate_cs_l8_branch_inside_region.facts.json new file mode 100644 index 00000000..3aadeee4 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l8_branch_inside_region.facts.json @@ -0,0 +1,95 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L8BranchInsideRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L8_branch_inside_region.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 12 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 12, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 12 + }, + { + "op": "use", + "var": "approved", + "line": 14 + }, + { + "op": "if", + "line": 14, + "then": [ + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 16 + } + ], + "else": [] + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 18 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 12 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l8_branch_inside_region.golden.json b/tests/fixtures/lowered/typestate_cs_l8_branch_inside_region.golden.json new file mode 100644 index 00000000..733d75de --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l8_branch_inside_region.golden.json @@ -0,0 +1,232 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L8BranchInsideRegion.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "use", + "handle": "loc_2", + "line": 14 + }, + { + "stmt": "if", + "cond": "?", + "then": [ + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 16 + } + ], + "else": [], + "line": 14 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 18 + } + ], + "line": 12 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 12 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_3", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L8BranchInsideRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L8_branch_inside_region.cs", + "line": 12, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L8BranchInsideRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L8_branch_inside_region.cs", + "line": 12, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_3", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_l9_opaque_calls_around_region.facts.json b/tests/fixtures/lowered/typestate_cs_l9_opaque_calls_around_region.facts.json new file mode 100644 index 00000000..ffe52534 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l9_opaque_calls_around_region.facts.json @@ -0,0 +1,74 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L9OpaqueCallsAroundRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L9_opaque_calls_around_region.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 18 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 18, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 18 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 20 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 18 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_l9_opaque_calls_around_region.golden.json b/tests/fixtures/lowered/typestate_cs_l9_opaque_calls_around_region.golden.json new file mode 100644 index 00000000..fdb4ca07 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_l9_opaque_calls_around_region.golden.json @@ -0,0 +1,210 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.L9OpaqueCallsAroundRegion.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 18 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 18 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 20 + } + ], + "line": 18 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 18 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_3", + "line": 80 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.L9OpaqueCallsAroundRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L9_opaque_calls_around_region.cs", + "line": 18, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.L9OpaqueCallsAroundRegion.Run", + "file": "frontend/roslyn/protocol-samples/cases/L9_opaque_calls_around_region.cs", + "line": 18, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_3", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_4", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_cs_x1_nested_region_same_entity.facts.json b/tests/fixtures/lowered/typestate_cs_x1_nested_region_same_entity.facts.json new file mode 100644 index 00000000..678406a7 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_x1_nested_region_same_entity.facts.json @@ -0,0 +1,119 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "Own.Protocols.Cases.X1NestedRegionSameEntity.Run", + "file": "frontend/roslyn/protocol-samples/cases/X1_nested_region_same_entity.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 13 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 13, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 13 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region2", + "line": 15, + "body": [ + { + "op": "acquire", + "var": "draft", + "line": 15 + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Cancel", + "args": [ + "draft", + "order.region2" + ], + "line": 15 + } + ] + }, + { + "op": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 16 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 13 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 80 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 80 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 80 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Cancel", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 57 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 57 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 57 + } + ] + } + ], + "stats": { + "methods_with_local": 0, + "methods_flow_analysed": 0, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_cs_x1_nested_region_same_entity.golden.json b/tests/fixtures/lowered/typestate_cs_x1_nested_region_same_entity.golden.json new file mode 100644 index 00000000..115028c2 --- /dev/null +++ b/tests/fixtures/lowered/typestate_cs_x1_nested_region_same_entity.golden.json @@ -0,0 +1,295 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Own.Protocols.Cases.X1NestedRegionSameEntity.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_0", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_1", + "body": [ + { + "stmt": "acquire", + "handle": "loc_2", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "borrow_mut", + "owner": "loc_0", + "binding": "brw_3", + "body": [ + { + "stmt": "acquire", + "handle": "loc_4", + "resource": "Disposable", + "line": 15 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.DraftOrder.Cancel", + "args": [ + "loc_4", + "brw_3" + ], + "line": 15 + } + ], + "line": 15 + }, + { + "stmt": "call", + "callee": "Own.Protocols.Sample.ApprovedOrder.Ship", + "args": [ + "loc_2", + "brw_1" + ], + "line": 16 + } + ], + "line": 13 + }, + { + "stmt": "release", + "handle": "loc_0", + "line": 13 + } + ] + }, + { + "name": "Own.Protocols.Sample.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 80, + "lifetime": null + }, + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 80, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_5", + "line": 80 + } + ] + }, + { + "name": "Own.Protocols.Sample.DraftOrder.Cancel", + "lifetime": null, + "params": [ + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 57, + "lifetime": null + }, + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 57, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_7", + "line": 57 + } + ] + } + ], + "handles": [ + { + "handle": "loc_0", + "component": "Own.Protocols.Cases.X1NestedRegionSameEntity.Run", + "file": "frontend/roslyn/protocol-samples/cases/X1_nested_region_same_entity.cs", + "line": 13, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_2", + "component": "Own.Protocols.Cases.X1NestedRegionSameEntity.Run", + "file": "frontend/roslyn/protocol-samples/cases/X1_nested_region_same_entity.cs", + "line": 13, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_4", + "component": "Own.Protocols.Cases.X1NestedRegionSameEntity.Run", + "file": "frontend/roslyn/protocol-samples/cases/X1_nested_region_same_entity.cs", + "line": 15, + "event": "draft", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_5", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_6", + "component": "Own.Protocols.Sample.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 80, + "event": "entity", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_7", + "component": "Own.Protocols.Sample.DraftOrder.Cancel", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 57, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_8", + "component": "Own.Protocols.Sample.DraftOrder.Cancel", + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 57, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_ef_orderbackend.facts.json b/tests/fixtures/lowered/typestate_ef_orderbackend.facts.json new file mode 100644 index 00000000..4b0f69b4 --- /dev/null +++ b/tests/fixtures/lowered/typestate_ef_orderbackend.facts.json @@ -0,0 +1,312 @@ +{ + "ownir_version": 1, + "module": "Extracted", + "components": [], + "services": [], + "functions": [ + { + "name": "OrderBackend.OrderHandlers.Create", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "sig": "System.String,OrderBackend.Data.AppDbContext,System.Threading.CancellationToken", + "params": [ + { + "name": "db", + "line": 16 + } + ], + "body": [ + { + "op": "use", + "var": "db", + "line": 19 + }, + { + "op": "use", + "var": "db", + "line": 20 + }, + { + "op": "return", + "var": null, + "line": 21 + } + ] + }, + { + "name": "OrderBackend.OrderHandlers.Ship", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "sig": "System.Int32,OrderBackend.Data.AppDbContext,Microsoft.Extensions.Logging.ILogger`1,System.Threading.CancellationToken", + "params": [ + { + "name": "db", + "line": 26 + } + ], + "body": [ + { + "op": "if", + "line": 33, + "then": [ + { + "op": "return", + "var": null, + "line": 34 + } + ], + "else": [] + }, + { + "op": "if", + "line": 42, + "then": [ + { + "op": "return", + "var": null, + "line": 40 + } + ], + "else": [] + }, + { + "op": "use", + "var": "db", + "line": 52 + }, + { + "op": "return", + "var": null, + "line": 56 + } + ] + }, + { + "name": "OrderBackend.OrderHandlers.SubmitAndApprove", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "sig": "System.Int32,OrderBackend.Data.AppDbContext,System.Threading.CancellationToken", + "params": [ + { + "name": "db", + "line": 59 + } + ], + "body": [ + { + "op": "if", + "line": 62, + "then": [ + { + "op": "return", + "var": null, + "line": 63 + } + ], + "else": [] + }, + { + "op": "if", + "line": 69, + "then": [ + { + "op": "return", + "var": null, + "line": 66 + } + ], + "else": [] + }, + { + "op": "use", + "var": "db", + "line": 80 + }, + { + "op": "return", + "var": null, + "line": 81 + } + ] + }, + { + "name": "OrderBackend.OrderHandlers.Get", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "sig": "System.Int32,OrderBackend.Data.AppDbContext,System.Threading.CancellationToken", + "params": [ + { + "name": "db", + "line": 84 + } + ], + "body": [ + { + "op": "return", + "var": null, + "line": 87 + } + ] + }, + { + "name": "OrderBackend.OrderHandlers.Ship$protocol", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 42 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 42, + "body": [ + { + "op": "acquire", + "var": "approved", + "line": 42 + }, + { + "op": "call", + "callee": "OrderBackend.Domain.ApprovedOrder.Ship", + "args": [ + "approved", + "order.region1" + ], + "line": 44 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 42 + } + ] + }, + { + "name": "OrderBackend.OrderHandlers.SubmitAndApprove$protocol", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 69 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "order.region1", + "line": 69, + "body": [ + { + "op": "acquire", + "var": "draft", + "line": 69 + }, + { + "op": "call", + "callee": "OrderBackend.Domain.DraftOrder.Submit", + "args": [ + "draft", + "order.region1" + ], + "line": 71 + }, + { + "op": "acquire", + "var": "submitted", + "line": 71 + }, + { + "op": "call", + "callee": "OrderBackend.Domain.SubmittedOrder.Approve", + "args": [ + "submitted", + "order.region1" + ], + "line": 72 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 69 + } + ] + }, + { + "name": "OrderBackend.Domain.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 65 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 65 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 65 + } + ] + }, + { + "name": "OrderBackend.Domain.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 37 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 37 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 37 + } + ] + }, + { + "name": "OrderBackend.Domain.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "params": [ + { + "name": "token", + "effect": "consume", + "line": 53 + }, + { + "name": "entity", + "effect": "borrow_mut", + "line": 53 + } + ], + "body": [ + { + "op": "release", + "var": "token", + "line": 53 + } + ] + } + ], + "stats": { + "methods_with_local": 4, + "methods_flow_analysed": 4, + "methods_skipped_unmodelled": 0 + } +} diff --git a/tests/fixtures/lowered/typestate_ef_orderbackend.golden.json b/tests/fixtures/lowered/typestate_ef_orderbackend.golden.json new file mode 100644 index 00000000..7fbbc2e5 --- /dev/null +++ b/tests/fixtures/lowered/typestate_ef_orderbackend.golden.json @@ -0,0 +1,605 @@ +{ + "lowered_version": 2, + "module": "Extracted", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "OrderBackend.OrderHandlers.Create", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 16, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "use", + "handle": "parg_0", + "line": 19 + }, + { + "stmt": "use", + "handle": "parg_0", + "line": 20 + }, + { + "stmt": "return", + "handle": null, + "line": 21 + } + ] + }, + { + "name": "OrderBackend.OrderHandlers.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 26, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "if", + "cond": "?", + "then": [ + { + "stmt": "return", + "handle": null, + "line": 34 + } + ], + "else": [], + "line": 33 + }, + { + "stmt": "if", + "cond": "?", + "then": [ + { + "stmt": "return", + "handle": null, + "line": 40 + } + ], + "else": [], + "line": 42 + }, + { + "stmt": "use", + "handle": "parg_1", + "line": 52 + }, + { + "stmt": "return", + "handle": null, + "line": 56 + } + ] + }, + { + "name": "OrderBackend.OrderHandlers.SubmitAndApprove", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 59, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "if", + "cond": "?", + "then": [ + { + "stmt": "return", + "handle": null, + "line": 63 + } + ], + "else": [], + "line": 62 + }, + { + "stmt": "if", + "cond": "?", + "then": [ + { + "stmt": "return", + "handle": null, + "line": 66 + } + ], + "else": [], + "line": 69 + }, + { + "stmt": "use", + "handle": "parg_2", + "line": 80 + }, + { + "stmt": "return", + "handle": null, + "line": 81 + } + ] + }, + { + "name": "OrderBackend.OrderHandlers.Get", + "lifetime": null, + "params": [ + { + "handle": "parg_3", + "type": { + "name": "int", + "borrowed": false, + "mutable": false + }, + "line": 84, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "return", + "handle": null, + "line": 87 + } + ] + }, + { + "name": "OrderBackend.OrderHandlers.Ship$protocol", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_4", + "resource": "Disposable", + "line": 42 + }, + { + "stmt": "borrow_mut", + "owner": "loc_4", + "binding": "brw_5", + "body": [ + { + "stmt": "acquire", + "handle": "loc_6", + "resource": "Disposable", + "line": 42 + }, + { + "stmt": "call", + "callee": "OrderBackend.Domain.ApprovedOrder.Ship", + "args": [ + "loc_6", + "brw_5" + ], + "line": 44 + } + ], + "line": 42 + }, + { + "stmt": "release", + "handle": "loc_4", + "line": 42 + } + ] + }, + { + "name": "OrderBackend.OrderHandlers.SubmitAndApprove$protocol", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_7", + "resource": "Disposable", + "line": 69 + }, + { + "stmt": "borrow_mut", + "owner": "loc_7", + "binding": "brw_8", + "body": [ + { + "stmt": "acquire", + "handle": "loc_9", + "resource": "Disposable", + "line": 69 + }, + { + "stmt": "call", + "callee": "OrderBackend.Domain.DraftOrder.Submit", + "args": [ + "loc_9", + "brw_8" + ], + "line": 71 + }, + { + "stmt": "acquire", + "handle": "loc_10", + "resource": "Disposable", + "line": 71 + }, + { + "stmt": "call", + "callee": "OrderBackend.Domain.SubmittedOrder.Approve", + "args": [ + "loc_10", + "brw_8" + ], + "line": 72 + } + ], + "line": 69 + }, + { + "stmt": "release", + "handle": "loc_7", + "line": 69 + } + ] + }, + { + "name": "OrderBackend.Domain.ApprovedOrder.Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_11", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 65, + "lifetime": null + }, + { + "handle": "parg_12", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 65, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_11", + "line": 65 + } + ] + }, + { + "name": "OrderBackend.Domain.DraftOrder.Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_13", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 37, + "lifetime": null + }, + { + "handle": "parg_14", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 37, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_13", + "line": 37 + } + ] + }, + { + "name": "OrderBackend.Domain.SubmittedOrder.Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_15", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 53, + "lifetime": null + }, + { + "handle": "parg_16", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 53, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_15", + "line": 53 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "OrderBackend.OrderHandlers.Create", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 16, + "event": "db", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_1", + "component": "OrderBackend.OrderHandlers.Ship", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 26, + "event": "db", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "OrderBackend.OrderHandlers.SubmitAndApprove", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 59, + "event": "db", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_3", + "component": "OrderBackend.OrderHandlers.Get", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 84, + "event": "db", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "loc_4", + "component": "OrderBackend.OrderHandlers.Ship$protocol", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 42, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_6", + "component": "OrderBackend.OrderHandlers.Ship$protocol", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 42, + "event": "approved", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_7", + "component": "OrderBackend.OrderHandlers.SubmitAndApprove$protocol", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 69, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_9", + "component": "OrderBackend.OrderHandlers.SubmitAndApprove$protocol", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 69, + "event": "draft", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_10", + "component": "OrderBackend.OrderHandlers.SubmitAndApprove$protocol", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 71, + "event": "submitted", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "parg_11", + "component": "OrderBackend.Domain.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "line": 65, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_12", + "component": "OrderBackend.Domain.ApprovedOrder.Ship", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "line": 65, + "event": "entity", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_13", + "component": "OrderBackend.Domain.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "line": 37, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_14", + "component": "OrderBackend.Domain.DraftOrder.Submit", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "line": 37, + "event": "entity", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_15", + "component": "OrderBackend.Domain.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "line": 53, + "event": "token", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_16", + "component": "OrderBackend.Domain.SubmittedOrder.Approve", + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "line": 53, + "event": "entity", + "resource": "flow-local", + "ever_released": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_g6_capability_not_spent.facts.json b/tests/fixtures/lowered/typestate_g6_capability_not_spent.facts.json new file mode 100644 index 00000000..a8375c28 --- /dev/null +++ b/tests/fixtures/lowered/typestate_g6_capability_not_spent.facts.json @@ -0,0 +1,169 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 12 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 14 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_g6_capability_not_spent.golden.json b/tests/fixtures/lowered/typestate_g6_capability_not_spent.golden.json new file mode 100644 index 00000000..6683db30 --- /dev/null +++ b/tests/fixtures/lowered/typestate_g6_capability_not_spent.golden.json @@ -0,0 +1,444 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + } + ], + "line": 11 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 14 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "a", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l1_stale_capability.facts.json b/tests/fixtures/lowered/typestate_l1_stale_capability.facts.json new file mode 100644 index 00000000..4331c126 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l1_stale_capability.facts.json @@ -0,0 +1,187 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "d", + "line": 12 + }, + { + "op": "call", + "callee": "Submit", + "args": [ + "d", + "r" + ], + "line": 13 + }, + { + "op": "call", + "callee": "CancelDraft", + "args": [ + "d", + "r" + ], + "line": 14 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 16 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l1_stale_capability.golden.json b/tests/fixtures/lowered/typestate_l1_stale_capability.golden.json new file mode 100644 index 00000000..bf2e5a38 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l1_stale_capability.golden.json @@ -0,0 +1,462 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "Submit", + "args": [ + "loc_13", + "brw_12" + ], + "line": 13 + }, + { + "stmt": "call", + "callee": "CancelDraft", + "args": [ + "loc_13", + "brw_12" + ], + "line": 14 + } + ], + "line": 11 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 16 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "d", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l2_copied_capability.facts.json b/tests/fixtures/lowered/typestate_l2_copied_capability.facts.json new file mode 100644 index 00000000..6e627b7c --- /dev/null +++ b/tests/fixtures/lowered/typestate_l2_copied_capability.facts.json @@ -0,0 +1,193 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "d1", + "line": 12 + }, + { + "op": "move", + "var": "d2", + "src": "d1", + "line": 13 + }, + { + "op": "call", + "callee": "Submit", + "args": [ + "d1", + "r" + ], + "line": 14 + }, + { + "op": "call", + "callee": "CancelDraft", + "args": [ + "d2", + "r" + ], + "line": 15 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 17 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l2_copied_capability.golden.json b/tests/fixtures/lowered/typestate_l2_copied_capability.golden.json new file mode 100644 index 00000000..61d24705 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l2_copied_capability.golden.json @@ -0,0 +1,478 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "move", + "handle": "loc_14", + "src": "loc_13", + "line": 13 + }, + { + "stmt": "call", + "callee": "Submit", + "args": [ + "loc_13", + "brw_12" + ], + "line": 14 + }, + { + "stmt": "call", + "callee": "CancelDraft", + "args": [ + "loc_14", + "brw_12" + ], + "line": 15 + } + ], + "line": 11 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 17 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "d1", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_14", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 13, + "event": "d2", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l3_raw_entity_mutation.facts.json b/tests/fixtures/lowered/typestate_l3_raw_entity_mutation.facts.json new file mode 100644 index 00000000..94ddaf0e --- /dev/null +++ b/tests/fixtures/lowered/typestate_l3_raw_entity_mutation.facts.json @@ -0,0 +1,186 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 12 + }, + { + "op": "call", + "callee": "CancelOrder", + "args": [ + "order" + ], + "line": 13 + }, + { + "op": "call", + "callee": "Ship", + "args": [ + "a", + "r" + ], + "line": 14 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 16 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l3_raw_entity_mutation.golden.json b/tests/fixtures/lowered/typestate_l3_raw_entity_mutation.golden.json new file mode 100644 index 00000000..2b8f68c6 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l3_raw_entity_mutation.golden.json @@ -0,0 +1,461 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "CancelOrder", + "args": [ + "loc_11" + ], + "line": 13 + }, + { + "stmt": "call", + "callee": "Ship", + "args": [ + "loc_13", + "brw_12" + ], + "line": 14 + } + ], + "line": 11 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 16 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "a", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l3b_second_region.facts.json b/tests/fixtures/lowered/typestate_l3b_second_region.facts.json new file mode 100644 index 00000000..07033b31 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l3b_second_region.facts.json @@ -0,0 +1,194 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 12 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r2", + "line": 13, + "body": [ + { + "op": "call", + "callee": "CancelOrder", + "args": [ + "r2" + ], + "line": 14 + } + ] + }, + { + "op": "call", + "callee": "Ship", + "args": [ + "a", + "r" + ], + "line": 16 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 18 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l3b_second_region.golden.json b/tests/fixtures/lowered/typestate_l3b_second_region.golden.json new file mode 100644 index 00000000..0e99d507 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l3b_second_region.golden.json @@ -0,0 +1,469 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_14", + "body": [ + { + "stmt": "call", + "callee": "CancelOrder", + "args": [ + "brw_14" + ], + "line": 14 + } + ], + "line": 13 + }, + { + "stmt": "call", + "callee": "Ship", + "args": [ + "loc_13", + "brw_12" + ], + "line": 16 + } + ], + "line": 11 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 18 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "a", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l4_raw_entity_unknown_call.facts.json b/tests/fixtures/lowered/typestate_l4_raw_entity_unknown_call.facts.json new file mode 100644 index 00000000..e1f9d18e --- /dev/null +++ b/tests/fixtures/lowered/typestate_l4_raw_entity_unknown_call.facts.json @@ -0,0 +1,186 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 12 + }, + { + "op": "call", + "callee": "Unknown", + "args": [ + "order" + ], + "line": 13 + }, + { + "op": "call", + "callee": "Ship", + "args": [ + "a", + "r" + ], + "line": 14 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 16 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l4_raw_entity_unknown_call.golden.json b/tests/fixtures/lowered/typestate_l4_raw_entity_unknown_call.golden.json new file mode 100644 index 00000000..9418b477 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l4_raw_entity_unknown_call.golden.json @@ -0,0 +1,4 @@ +{ + "lowered_version": 2, + "error": "OwnIR call to 'Unknown' inside a borrow_mut region passes tracked local 'order', but the callee has no ownership contract (Orders.cs:13) — refused: an exclusive region must not hand its entity or tokens to unanalysed code" +} diff --git a/tests/fixtures/lowered/typestate_l4b_raw_entity_shared_call.facts.json b/tests/fixtures/lowered/typestate_l4b_raw_entity_shared_call.facts.json new file mode 100644 index 00000000..2f8aeefa --- /dev/null +++ b/tests/fixtures/lowered/typestate_l4b_raw_entity_shared_call.facts.json @@ -0,0 +1,186 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 12 + }, + { + "op": "call", + "callee": "Peek", + "args": [ + "order" + ], + "line": 13 + }, + { + "op": "call", + "callee": "Ship", + "args": [ + "a", + "r" + ], + "line": 14 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 16 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l4b_raw_entity_shared_call.golden.json b/tests/fixtures/lowered/typestate_l4b_raw_entity_shared_call.golden.json new file mode 100644 index 00000000..b802753c --- /dev/null +++ b/tests/fixtures/lowered/typestate_l4b_raw_entity_shared_call.golden.json @@ -0,0 +1,461 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "Peek", + "args": [ + "loc_11" + ], + "line": 13 + }, + { + "stmt": "call", + "callee": "Ship", + "args": [ + "loc_13", + "brw_12" + ], + "line": 14 + } + ], + "line": 11 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 16 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "a", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l4c_raw_entity_consumed.facts.json b/tests/fixtures/lowered/typestate_l4c_raw_entity_consumed.facts.json new file mode 100644 index 00000000..3707b5d2 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l4c_raw_entity_consumed.facts.json @@ -0,0 +1,181 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 12 + }, + { + "op": "call", + "callee": "Store", + "args": [ + "order" + ], + "line": 13 + }, + { + "op": "call", + "callee": "Ship", + "args": [ + "a", + "r" + ], + "line": 14 + } + ] + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l4c_raw_entity_consumed.golden.json b/tests/fixtures/lowered/typestate_l4c_raw_entity_consumed.golden.json new file mode 100644 index 00000000..116378e4 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l4c_raw_entity_consumed.golden.json @@ -0,0 +1,456 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "Store", + "args": [ + "loc_11" + ], + "line": 13 + }, + { + "stmt": "call", + "callee": "Ship", + "args": [ + "loc_13", + "brw_12" + ], + "line": 14 + } + ], + "line": 11 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "a", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l4d_token_unknown_call.facts.json b/tests/fixtures/lowered/typestate_l4d_token_unknown_call.facts.json new file mode 100644 index 00000000..cd2b9b63 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l4d_token_unknown_call.facts.json @@ -0,0 +1,186 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 12 + }, + { + "op": "call", + "callee": "Unknown", + "args": [ + "a" + ], + "line": 13 + }, + { + "op": "call", + "callee": "Ship", + "args": [ + "a", + "r" + ], + "line": 14 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 16 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l4d_token_unknown_call.golden.json b/tests/fixtures/lowered/typestate_l4d_token_unknown_call.golden.json new file mode 100644 index 00000000..07241a9e --- /dev/null +++ b/tests/fixtures/lowered/typestate_l4d_token_unknown_call.golden.json @@ -0,0 +1,4 @@ +{ + "lowered_version": 2, + "error": "OwnIR call to 'Unknown' inside a borrow_mut region passes tracked local 'a', but the callee has no ownership contract (Orders.cs:13) — refused: an exclusive region must not hand its entity or tokens to unanalysed code" +} diff --git a/tests/fixtures/lowered/typestate_l5_normal_transition.facts.json b/tests/fixtures/lowered/typestate_l5_normal_transition.facts.json new file mode 100644 index 00000000..d3b4e286 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l5_normal_transition.facts.json @@ -0,0 +1,178 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 12 + }, + { + "op": "call", + "callee": "Ship", + "args": [ + "a", + "r" + ], + "line": 13 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 15 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l5_normal_transition.golden.json b/tests/fixtures/lowered/typestate_l5_normal_transition.golden.json new file mode 100644 index 00000000..28e657bc --- /dev/null +++ b/tests/fixtures/lowered/typestate_l5_normal_transition.golden.json @@ -0,0 +1,453 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "Ship", + "args": [ + "loc_13", + "brw_12" + ], + "line": 13 + } + ], + "line": 11 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 15 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "a", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l5b_two_transitions.facts.json b/tests/fixtures/lowered/typestate_l5b_two_transitions.facts.json new file mode 100644 index 00000000..295e58c9 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l5b_two_transitions.facts.json @@ -0,0 +1,192 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "d", + "line": 12 + }, + { + "op": "call", + "callee": "Submit", + "args": [ + "d", + "r" + ], + "line": 13 + }, + { + "op": "acquire", + "var": "s", + "line": 13 + }, + { + "op": "call", + "callee": "Approve", + "args": [ + "s", + "r" + ], + "line": 14 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 16 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l5b_two_transitions.golden.json b/tests/fixtures/lowered/typestate_l5b_two_transitions.golden.json new file mode 100644 index 00000000..b1a36644 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l5b_two_transitions.golden.json @@ -0,0 +1,478 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "Submit", + "args": [ + "loc_13", + "brw_12" + ], + "line": 13 + }, + { + "stmt": "acquire", + "handle": "loc_14", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "call", + "callee": "Approve", + "args": [ + "loc_14", + "brw_12" + ], + "line": 14 + } + ], + "line": 11 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 16 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "d", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_14", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 13, + "event": "s", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l5c_read_after_region.facts.json b/tests/fixtures/lowered/typestate_l5c_read_after_region.facts.json new file mode 100644 index 00000000..d43777d7 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l5c_read_after_region.facts.json @@ -0,0 +1,186 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 12 + }, + { + "op": "call", + "callee": "Ship", + "args": [ + "a", + "r" + ], + "line": 13 + } + ] + }, + { + "op": "call", + "callee": "Peek", + "args": [ + "order" + ], + "line": 15 + }, + { + "op": "release", + "var": "order", + "line": 16 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_l5c_read_after_region.golden.json b/tests/fixtures/lowered/typestate_l5c_read_after_region.golden.json new file mode 100644 index 00000000..3aeb08c3 --- /dev/null +++ b/tests/fixtures/lowered/typestate_l5c_read_after_region.golden.json @@ -0,0 +1,461 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "call", + "callee": "Ship", + "args": [ + "loc_13", + "brw_12" + ], + "line": 13 + } + ], + "line": 11 + }, + { + "stmt": "call", + "callee": "Peek", + "args": [ + "loc_11" + ], + "line": 15 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 16 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "a", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_n1_token_spent_on_other_entity.facts.json b/tests/fixtures/lowered/typestate_n1_token_spent_on_other_entity.facts.json new file mode 100644 index 00000000..d1dc9fc4 --- /dev/null +++ b/tests/fixtures/lowered/typestate_n1_token_spent_on_other_entity.facts.json @@ -0,0 +1,196 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "o1", + "line": 10 + }, + { + "op": "acquire", + "var": "o2", + "line": 11 + }, + { + "op": "borrow_mut", + "owner": "o1", + "binding": "r1", + "line": 12, + "body": [ + { + "op": "borrow_mut", + "owner": "o2", + "binding": "r2", + "line": 13, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 14 + }, + { + "op": "call", + "callee": "Ship", + "args": [ + "a", + "r2" + ], + "line": 15 + } + ] + } + ] + }, + { + "op": "release", + "var": "o1", + "line": 18 + }, + { + "op": "release", + "var": "o2", + "line": 19 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_n1_token_spent_on_other_entity.golden.json b/tests/fixtures/lowered/typestate_n1_token_spent_on_other_entity.golden.json new file mode 100644 index 00000000..3b376e17 --- /dev/null +++ b/tests/fixtures/lowered/typestate_n1_token_spent_on_other_entity.golden.json @@ -0,0 +1,482 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "acquire", + "handle": "loc_12", + "resource": "Disposable", + "line": 11 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_13", + "body": [ + { + "stmt": "borrow_mut", + "owner": "loc_12", + "binding": "brw_14", + "body": [ + { + "stmt": "acquire", + "handle": "loc_15", + "resource": "Disposable", + "line": 14 + }, + { + "stmt": "call", + "callee": "Ship", + "args": [ + "loc_15", + "brw_14" + ], + "line": 15 + } + ], + "line": 13 + } + ], + "line": 12 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 18 + }, + { + "stmt": "release", + "handle": "loc_12", + "line": 19 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "o1", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_12", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 11, + "event": "o2", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_15", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 14, + "event": "a", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_n2_two_tokens_one_region.facts.json b/tests/fixtures/lowered/typestate_n2_two_tokens_one_region.facts.json new file mode 100644 index 00000000..afa9543a --- /dev/null +++ b/tests/fixtures/lowered/typestate_n2_two_tokens_one_region.facts.json @@ -0,0 +1,192 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "d", + "line": 12 + }, + { + "op": "acquire", + "var": "again", + "line": 13 + }, + { + "op": "call", + "callee": "Submit", + "args": [ + "d", + "r" + ], + "line": 14 + }, + { + "op": "call", + "callee": "CancelDraft", + "args": [ + "again", + "r" + ], + "line": 15 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 17 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_n2_two_tokens_one_region.golden.json b/tests/fixtures/lowered/typestate_n2_two_tokens_one_region.golden.json new file mode 100644 index 00000000..37dc9591 --- /dev/null +++ b/tests/fixtures/lowered/typestate_n2_two_tokens_one_region.golden.json @@ -0,0 +1,478 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + }, + { + "stmt": "acquire", + "handle": "loc_14", + "resource": "Disposable", + "line": 13 + }, + { + "stmt": "call", + "callee": "Submit", + "args": [ + "loc_13", + "brw_12" + ], + "line": 14 + }, + { + "stmt": "call", + "callee": "CancelDraft", + "args": [ + "loc_14", + "brw_12" + ], + "line": 15 + } + ], + "line": 11 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 17 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "d", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_14", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 13, + "event": "again", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_n3_raw_mutation_inferred_effect.facts.json b/tests/fixtures/lowered/typestate_n3_raw_mutation_inferred_effect.facts.json new file mode 100644 index 00000000..f0f679db --- /dev/null +++ b/tests/fixtures/lowered/typestate_n3_raw_mutation_inferred_effect.facts.json @@ -0,0 +1,197 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Touch", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "line": 170 + } + ], + "body": [] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 12 + }, + { + "op": "call", + "callee": "Touch", + "args": [ + "order" + ], + "line": 13 + }, + { + "op": "call", + "callee": "Ship", + "args": [ + "a", + "r" + ], + "line": 14 + } + ] + }, + { + "op": "release", + "var": "order", + "line": 16 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_n3_raw_mutation_inferred_effect.golden.json b/tests/fixtures/lowered/typestate_n3_raw_mutation_inferred_effect.golden.json new file mode 100644 index 00000000..e86f2619 --- /dev/null +++ b/tests/fixtures/lowered/typestate_n3_raw_mutation_inferred_effect.golden.json @@ -0,0 +1,4 @@ +{ + "lowered_version": 2, + "error": "OwnIR call to 'Touch' inside a borrow_mut region passes tracked local 'order' at position 0, which carries no ownership effect (Orders.cs:13) — refused: inside an exclusive region every position that receives the entity or a token must state what it does with it" +} diff --git a/tests/fixtures/lowered/typestate_n4_token_outlives_region.facts.json b/tests/fixtures/lowered/typestate_n4_token_outlives_region.facts.json new file mode 100644 index 00000000..dca24eb8 --- /dev/null +++ b/tests/fixtures/lowered/typestate_n4_token_outlives_region.facts.json @@ -0,0 +1,186 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Submit", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 100 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 101 + } + ] + }, + { + "name": "Approve", + "file": "Orders.cs", + "params": [ + { + "name": "s", + "effect": "consume", + "line": 110 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 110 + } + ], + "body": [ + { + "op": "release", + "var": "s", + "line": 111 + } + ] + }, + { + "name": "Ship", + "file": "Orders.cs", + "params": [ + { + "name": "a", + "effect": "consume", + "line": 120 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "a", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "file": "Orders.cs", + "params": [ + { + "name": "d", + "effect": "consume", + "line": 130 + }, + { + "name": "order", + "effect": "borrow_mut", + "line": 130 + } + ], + "body": [ + { + "op": "release", + "var": "d", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow_mut", + "line": 140 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "borrow", + "line": 150 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Orders.cs", + "params": [ + { + "name": "order", + "effect": "consume", + "line": 160 + } + ], + "body": [ + { + "op": "release", + "var": "order", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "file": "Orders.cs", + "body": [ + { + "op": "acquire", + "var": "order", + "line": 10 + }, + { + "op": "borrow_mut", + "owner": "order", + "binding": "r", + "line": 11, + "body": [ + { + "op": "acquire", + "var": "a", + "line": 12 + } + ] + }, + { + "op": "call", + "callee": "CancelOrder", + "args": [ + "order" + ], + "line": 14 + }, + { + "op": "call", + "callee": "Ship", + "args": [ + "a", + "order" + ], + "line": 15 + }, + { + "op": "release", + "var": "order", + "line": 16 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_n4_token_outlives_region.golden.json b/tests/fixtures/lowered/typestate_n4_token_outlives_region.golden.json new file mode 100644 index 00000000..25c7dd42 --- /dev/null +++ b/tests/fixtures/lowered/typestate_n4_token_outlives_region.golden.json @@ -0,0 +1,461 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Submit", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 100, + "lifetime": null + }, + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_0", + "line": 101 + } + ] + }, + { + "name": "Approve", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 110, + "lifetime": null + }, + { + "handle": "parg_3", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 111 + } + ] + }, + { + "name": "Ship", + "lifetime": null, + "params": [ + { + "handle": "parg_4", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + }, + { + "handle": "parg_5", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_4", + "line": 121 + } + ] + }, + { + "name": "CancelDraft", + "lifetime": null, + "params": [ + { + "handle": "parg_6", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 130, + "lifetime": null + }, + { + "handle": "parg_7", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 130, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_6", + "line": 131 + } + ] + }, + { + "name": "CancelOrder", + "lifetime": null, + "params": [ + { + "handle": "parg_8", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 140, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_9", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 150, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_10", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 160, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_10", + "line": 161 + } + ] + }, + { + "name": "Handler.Run", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 10 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 12 + } + ], + "line": 11 + }, + { + "stmt": "call", + "callee": "CancelOrder", + "args": [ + "loc_11" + ], + "line": 14 + }, + { + "stmt": "call", + "callee": "Ship", + "args": [ + "loc_13", + "loc_11" + ], + "line": 15 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 16 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_1", + "component": "Submit", + "file": "Orders.cs", + "line": 100, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "s", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_3", + "component": "Approve", + "file": "Orders.cs", + "line": 110, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_4", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "a", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_5", + "component": "Ship", + "file": "Orders.cs", + "line": 120, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_6", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "d", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "parg_7", + "component": "CancelDraft", + "file": "Orders.cs", + "line": 130, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_8", + "component": "CancelOrder", + "file": "Orders.cs", + "line": 140, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_9", + "component": "Peek", + "file": "Orders.cs", + "line": 150, + "event": "order", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_10", + "component": "Store", + "file": "Orders.cs", + "line": 160, + "event": "order", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_11", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 10, + "event": "order", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "Handler.Run", + "file": "Orders.cs", + "line": 12, + "event": "a", + "resource": "flow-local", + "ever_released": false, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_w1_loan_wordings.facts.json b/tests/fixtures/lowered/typestate_w1_loan_wordings.facts.json new file mode 100644 index 00000000..41bfc219 --- /dev/null +++ b/tests/fixtures/lowered/typestate_w1_loan_wordings.facts.json @@ -0,0 +1,219 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Reborrow", + "file": "Wordings.cs", + "params": [ + { + "name": "o", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Wordings.cs", + "params": [ + { + "name": "o", + "effect": "borrow", + "line": 110 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Wordings.cs", + "params": [ + { + "name": "o", + "effect": "consume", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "o", + "line": 121 + } + ] + }, + { + "name": "W.Moved", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "b", + "line": 1 + }, + { + "op": "move", + "var": "c", + "src": "b", + "line": 2 + }, + { + "op": "use", + "var": "b", + "line": 3 + }, + { + "op": "release", + "var": "c", + "line": 4 + } + ] + }, + { + "name": "W.Consumed", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "o", + "line": 11 + }, + { + "op": "borrow_mut", + "owner": "o", + "binding": "r", + "line": 12, + "body": [ + { + "op": "call", + "callee": "Store", + "args": [ + "o" + ], + "line": 13 + } + ] + } + ] + }, + { + "name": "W.Released", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "o", + "line": 21 + }, + { + "op": "borrow_mut", + "owner": "o", + "binding": "r", + "line": 22, + "body": [ + { + "op": "release", + "var": "o", + "line": 23 + } + ] + } + ] + }, + { + "name": "W.Reborrowed", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "o", + "line": 31 + }, + { + "op": "borrow_mut", + "owner": "o", + "binding": "r", + "line": 32, + "body": [ + { + "op": "call", + "callee": "Reborrow", + "args": [ + "o" + ], + "line": 33 + } + ] + }, + { + "op": "release", + "var": "o", + "line": 35 + } + ] + }, + { + "name": "W.Shared", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "o", + "line": 41 + }, + { + "op": "borrow_mut", + "owner": "o", + "binding": "r", + "line": 42, + "body": [ + { + "op": "call", + "callee": "Peek", + "args": [ + "o" + ], + "line": 43 + } + ] + }, + { + "op": "release", + "var": "o", + "line": 45 + } + ] + }, + { + "name": "W.Used", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "o", + "line": 51 + }, + { + "op": "borrow_mut", + "owner": "o", + "binding": "r", + "line": 52, + "body": [ + { + "op": "use", + "var": "o", + "line": 53 + } + ] + }, + { + "op": "release", + "var": "o", + "line": 55 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_w1_loan_wordings.golden.json b/tests/fixtures/lowered/typestate_w1_loan_wordings.golden.json new file mode 100644 index 00000000..12ab0372 --- /dev/null +++ b/tests/fixtures/lowered/typestate_w1_loan_wordings.golden.json @@ -0,0 +1,442 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Reborrow", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 121 + } + ] + }, + { + "name": "W.Moved", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_3", + "resource": "Disposable", + "line": 1 + }, + { + "stmt": "move", + "handle": "loc_4", + "src": "loc_3", + "line": 2 + }, + { + "stmt": "use", + "handle": "loc_3", + "line": 3 + }, + { + "stmt": "release", + "handle": "loc_4", + "line": 4 + } + ] + }, + { + "name": "W.Consumed", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_5", + "resource": "Disposable", + "line": 11 + }, + { + "stmt": "borrow_mut", + "owner": "loc_5", + "binding": "brw_6", + "body": [ + { + "stmt": "call", + "callee": "Store", + "args": [ + "loc_5" + ], + "line": 13 + } + ], + "line": 12 + } + ] + }, + { + "name": "W.Released", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_7", + "resource": "Disposable", + "line": 21 + }, + { + "stmt": "borrow_mut", + "owner": "loc_7", + "binding": "brw_8", + "body": [ + { + "stmt": "release", + "handle": "loc_7", + "line": 23 + } + ], + "line": 22 + } + ] + }, + { + "name": "W.Reborrowed", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_9", + "resource": "Disposable", + "line": 31 + }, + { + "stmt": "borrow_mut", + "owner": "loc_9", + "binding": "brw_10", + "body": [ + { + "stmt": "call", + "callee": "Reborrow", + "args": [ + "loc_9" + ], + "line": 33 + } + ], + "line": 32 + }, + { + "stmt": "release", + "handle": "loc_9", + "line": 35 + } + ] + }, + { + "name": "W.Shared", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 41 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "call", + "callee": "Peek", + "args": [ + "loc_11" + ], + "line": 43 + } + ], + "line": 42 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 45 + } + ] + }, + { + "name": "W.Used", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 51 + }, + { + "stmt": "borrow_mut", + "owner": "loc_13", + "binding": "brw_14", + "body": [ + { + "stmt": "use", + "handle": "loc_13", + "line": 53 + } + ], + "line": 52 + }, + { + "stmt": "release", + "handle": "loc_13", + "line": 55 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Reborrow", + "file": "Wordings.cs", + "line": 100, + "event": "o", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_1", + "component": "Peek", + "file": "Wordings.cs", + "line": 110, + "event": "o", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Store", + "file": "Wordings.cs", + "line": 120, + "event": "o", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_3", + "component": "W.Moved", + "file": "Wordings.cs", + "line": 1, + "event": "b", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_4", + "component": "W.Moved", + "file": "Wordings.cs", + "line": 2, + "event": "c", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_5", + "component": "W.Consumed", + "file": "Wordings.cs", + "line": 11, + "event": "o", + "resource": "flow-local", + "ever_released": false, + "pool": false + }, + { + "handle": "loc_7", + "component": "W.Released", + "file": "Wordings.cs", + "line": 21, + "event": "o", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_9", + "component": "W.Reborrowed", + "file": "Wordings.cs", + "line": 31, + "event": "o", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_11", + "component": "W.Shared", + "file": "Wordings.cs", + "line": 41, + "event": "o", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_13", + "component": "W.Used", + "file": "Wordings.cs", + "line": 51, + "event": "o", + "resource": "flow-local", + "ever_released": true, + "pool": false + } + ] +} diff --git a/tests/fixtures/lowered/typestate_w2_loan_wordings_pooled.facts.json b/tests/fixtures/lowered/typestate_w2_loan_wordings_pooled.facts.json new file mode 100644 index 00000000..113a898f --- /dev/null +++ b/tests/fixtures/lowered/typestate_w2_loan_wordings_pooled.facts.json @@ -0,0 +1,225 @@ +{ + "ownir_version": 1, + "module": "TypestateGate", + "functions": [ + { + "name": "Reborrow", + "file": "Wordings.cs", + "params": [ + { + "name": "o", + "effect": "borrow_mut", + "line": 100 + } + ], + "body": [] + }, + { + "name": "Peek", + "file": "Wordings.cs", + "params": [ + { + "name": "o", + "effect": "borrow", + "line": 110 + } + ], + "body": [] + }, + { + "name": "Store", + "file": "Wordings.cs", + "params": [ + { + "name": "o", + "effect": "consume", + "line": 120 + } + ], + "body": [ + { + "op": "release", + "var": "o", + "line": 121 + } + ] + }, + { + "name": "W.Moved", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "b", + "line": 1, + "kind": "pool" + }, + { + "op": "move", + "var": "c", + "src": "b", + "line": 2 + }, + { + "op": "use", + "var": "b", + "line": 3 + }, + { + "op": "release", + "var": "c", + "line": 4 + } + ] + }, + { + "name": "W.Consumed", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "o", + "line": 11, + "kind": "pool" + }, + { + "op": "borrow_mut", + "owner": "o", + "binding": "r", + "line": 12, + "body": [ + { + "op": "call", + "callee": "Store", + "args": [ + "o" + ], + "line": 13 + } + ] + } + ] + }, + { + "name": "W.Released", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "o", + "line": 21, + "kind": "pool" + }, + { + "op": "borrow_mut", + "owner": "o", + "binding": "r", + "line": 22, + "body": [ + { + "op": "release", + "var": "o", + "line": 23 + } + ] + } + ] + }, + { + "name": "W.Reborrowed", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "o", + "line": 31, + "kind": "pool" + }, + { + "op": "borrow_mut", + "owner": "o", + "binding": "r", + "line": 32, + "body": [ + { + "op": "call", + "callee": "Reborrow", + "args": [ + "o" + ], + "line": 33 + } + ] + }, + { + "op": "release", + "var": "o", + "line": 35 + } + ] + }, + { + "name": "W.Shared", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "o", + "line": 41, + "kind": "pool" + }, + { + "op": "borrow_mut", + "owner": "o", + "binding": "r", + "line": 42, + "body": [ + { + "op": "call", + "callee": "Peek", + "args": [ + "o" + ], + "line": 43 + } + ] + }, + { + "op": "release", + "var": "o", + "line": 45 + } + ] + }, + { + "name": "W.Used", + "file": "Wordings.cs", + "body": [ + { + "op": "acquire", + "var": "o", + "line": 51, + "kind": "pool" + }, + { + "op": "borrow_mut", + "owner": "o", + "binding": "r", + "line": 52, + "body": [ + { + "op": "use", + "var": "o", + "line": 53 + } + ] + }, + { + "op": "release", + "var": "o", + "line": 55 + } + ] + } + ] +} diff --git a/tests/fixtures/lowered/typestate_w2_loan_wordings_pooled.golden.json b/tests/fixtures/lowered/typestate_w2_loan_wordings_pooled.golden.json new file mode 100644 index 00000000..52138627 --- /dev/null +++ b/tests/fixtures/lowered/typestate_w2_loan_wordings_pooled.golden.json @@ -0,0 +1,442 @@ +{ + "lowered_version": 2, + "module": "TypestateGate", + "resources": [ + { + "name": "Subscription", + "kind": "subscription token", + "members": [ + { + "role": "acquire", + "name": "Subscribe" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "Timer", + "kind": "timer", + "members": [ + { + "role": "acquire", + "name": "Start" + }, + { + "role": "release", + "name": "Stop" + } + ] + }, + { + "name": "Disposable", + "kind": "disposable field", + "members": [ + { + "role": "acquire", + "name": "New" + }, + { + "role": "release", + "name": "Dispose" + } + ] + }, + { + "name": "PooledBuffer", + "kind": "pooled buffer", + "members": [ + { + "role": "acquire", + "name": "Rent" + }, + { + "role": "release", + "name": "Return" + } + ] + } + ], + "externs": [ + { + "name": "$consume", + "params": [ + { + "effect": "consume", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow", + "params": [ + { + "effect": "borrow", + "type": "Disposable" + } + ] + }, + { + "name": "$borrow_mut", + "params": [ + { + "effect": "borrow_mut", + "type": "Disposable" + } + ] + } + ], + "lifetimes": [], + "functions": [ + { + "name": "Reborrow", + "lifetime": null, + "params": [ + { + "handle": "parg_0", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": true + }, + "line": 100, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Peek", + "lifetime": null, + "params": [ + { + "handle": "parg_1", + "type": { + "name": "Disposable", + "borrowed": true, + "mutable": false + }, + "line": 110, + "lifetime": null + } + ], + "ret": null, + "body": [] + }, + { + "name": "Store", + "lifetime": null, + "params": [ + { + "handle": "parg_2", + "type": { + "name": "Disposable", + "borrowed": false, + "mutable": false + }, + "line": 120, + "lifetime": null + } + ], + "ret": null, + "body": [ + { + "stmt": "release", + "handle": "parg_2", + "line": 121 + } + ] + }, + { + "name": "W.Moved", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_3", + "resource": "Disposable", + "line": 1 + }, + { + "stmt": "move", + "handle": "loc_4", + "src": "loc_3", + "line": 2 + }, + { + "stmt": "use", + "handle": "loc_3", + "line": 3 + }, + { + "stmt": "release", + "handle": "loc_4", + "line": 4 + } + ] + }, + { + "name": "W.Consumed", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_5", + "resource": "Disposable", + "line": 11 + }, + { + "stmt": "borrow_mut", + "owner": "loc_5", + "binding": "brw_6", + "body": [ + { + "stmt": "call", + "callee": "Store", + "args": [ + "loc_5" + ], + "line": 13 + } + ], + "line": 12 + } + ] + }, + { + "name": "W.Released", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_7", + "resource": "Disposable", + "line": 21 + }, + { + "stmt": "borrow_mut", + "owner": "loc_7", + "binding": "brw_8", + "body": [ + { + "stmt": "release", + "handle": "loc_7", + "line": 23 + } + ], + "line": 22 + } + ] + }, + { + "name": "W.Reborrowed", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_9", + "resource": "Disposable", + "line": 31 + }, + { + "stmt": "borrow_mut", + "owner": "loc_9", + "binding": "brw_10", + "body": [ + { + "stmt": "call", + "callee": "Reborrow", + "args": [ + "loc_9" + ], + "line": 33 + } + ], + "line": 32 + }, + { + "stmt": "release", + "handle": "loc_9", + "line": 35 + } + ] + }, + { + "name": "W.Shared", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_11", + "resource": "Disposable", + "line": 41 + }, + { + "stmt": "borrow_mut", + "owner": "loc_11", + "binding": "brw_12", + "body": [ + { + "stmt": "call", + "callee": "Peek", + "args": [ + "loc_11" + ], + "line": 43 + } + ], + "line": 42 + }, + { + "stmt": "release", + "handle": "loc_11", + "line": 45 + } + ] + }, + { + "name": "W.Used", + "lifetime": null, + "params": [], + "ret": null, + "body": [ + { + "stmt": "acquire", + "handle": "loc_13", + "resource": "Disposable", + "line": 51 + }, + { + "stmt": "borrow_mut", + "owner": "loc_13", + "binding": "brw_14", + "body": [ + { + "stmt": "use", + "handle": "loc_13", + "line": 53 + } + ], + "line": 52 + }, + { + "stmt": "release", + "handle": "loc_13", + "line": 55 + } + ] + } + ], + "handles": [ + { + "handle": "parg_0", + "component": "Reborrow", + "file": "Wordings.cs", + "line": 100, + "event": "o", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_1", + "component": "Peek", + "file": "Wordings.cs", + "line": 110, + "event": "o", + "resource": "flow-local", + "ever_released": false + }, + { + "handle": "parg_2", + "component": "Store", + "file": "Wordings.cs", + "line": 120, + "event": "o", + "resource": "flow-local", + "ever_released": true + }, + { + "handle": "loc_3", + "component": "W.Moved", + "file": "Wordings.cs", + "line": 1, + "event": "b", + "resource": "flow-local", + "ever_released": false, + "pool": true + }, + { + "handle": "loc_4", + "component": "W.Moved", + "file": "Wordings.cs", + "line": 2, + "event": "c", + "resource": "flow-local", + "ever_released": true, + "pool": false + }, + { + "handle": "loc_5", + "component": "W.Consumed", + "file": "Wordings.cs", + "line": 11, + "event": "o", + "resource": "flow-local", + "ever_released": false, + "pool": true + }, + { + "handle": "loc_7", + "component": "W.Released", + "file": "Wordings.cs", + "line": 21, + "event": "o", + "resource": "flow-local", + "ever_released": true, + "pool": true + }, + { + "handle": "loc_9", + "component": "W.Reborrowed", + "file": "Wordings.cs", + "line": 31, + "event": "o", + "resource": "flow-local", + "ever_released": true, + "pool": true + }, + { + "handle": "loc_11", + "component": "W.Shared", + "file": "Wordings.cs", + "line": 41, + "event": "o", + "resource": "flow-local", + "ever_released": true, + "pool": true + }, + { + "handle": "loc_13", + "component": "W.Used", + "file": "Wordings.cs", + "line": 51, + "event": "o", + "resource": "flow-local", + "ever_released": true, + "pool": true + } + ] +} diff --git a/tests/fixtures/lowered/vocab_unknown_op.facts.json b/tests/fixtures/lowered/vocab_unknown_op.facts.json index 46091dc2..f091a7fc 100644 --- a/tests/fixtures/lowered/vocab_unknown_op.facts.json +++ b/tests/fixtures/lowered/vocab_unknown_op.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Vocab", "functions": [ { diff --git a/tests/fixtures/lowered/vocab_unknown_op.golden.json b/tests/fixtures/lowered/vocab_unknown_op.golden.json index 10d4fa1d..9405464d 100644 --- a/tests/fixtures/lowered/vocab_unknown_op.golden.json +++ b/tests/fixtures/lowered/vocab_unknown_op.golden.json @@ -1,4 +1,4 @@ { - "lowered_version": 1, + "lowered_version": 2, "error": "unknown OwnIR flow op 'try' (F.cs:2) — extractor/core vocabulary skew; a new op must bump OWNIR_VERSION (see spec/OwnIR.md)" } diff --git a/tests/fixtures/ownir/capture.facts.json b/tests/fixtures/ownir/capture.facts.json index aba82fba..5ff5b528 100644 --- a/tests/fixtures/ownir/capture.facts.json +++ b/tests/fixtures/ownir/capture.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "WpfApp", "components": [ { diff --git a/tests/fixtures/ownir/contract_inference.facts.json b/tests/fixtures/ownir/contract_inference.facts.json index a2ebcac0..b7704a95 100644 --- a/tests/fixtures/ownir/contract_inference.facts.json +++ b/tests/fixtures/ownir/contract_inference.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "InferBridge", "functions": [ { diff --git a/tests/fixtures/ownir/di.facts.json b/tests/fixtures/ownir/di.facts.json index 3b91a586..53a90c0e 100644 --- a/tests/fixtures/ownir/di.facts.json +++ b/tests/fixtures/ownir/di.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DiDemo", "components": [], "services": [ diff --git a/tests/fixtures/ownir/di_capture.facts.json b/tests/fixtures/ownir/di_capture.facts.json index 65698ac3..c5f0f536 100644 --- a/tests/fixtures/ownir/di_capture.facts.json +++ b/tests/fixtures/ownir/di_capture.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DiZombie", "components": [ { diff --git a/tests/fixtures/ownir/disposable.facts.json b/tests/fixtures/ownir/disposable.facts.json index e8cbbd1f..b5e87e10 100644 --- a/tests/fixtures/ownir/disposable.facts.json +++ b/tests/fixtures/ownir/disposable.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "WpfDisposables", "components": [ { diff --git a/tests/fixtures/ownir/flow_column_anchors.facts.json b/tests/fixtures/ownir/flow_column_anchors.facts.json index b6b3e821..b0990a35 100644 --- a/tests/fixtures/ownir/flow_column_anchors.facts.json +++ b/tests/fixtures/ownir/flow_column_anchors.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "ColumnAnchors", "_doc": [ "Own.NET#317: the optional 1-based `column` on the finding-anchor path.", diff --git a/tests/fixtures/ownir/flow_finally_switch.facts.json b/tests/fixtures/ownir/flow_finally_switch.facts.json index fb38f435..bf285a54 100644 --- a/tests/fixtures/ownir/flow_finally_switch.facts.json +++ b/tests/fixtures/ownir/flow_finally_switch.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Extracted", "comment": "IR the extractor now emits for finally-before-return and switch lowering. (1) FinallyReturnClean: a `return` inside try-with-finally runs the finally (release r) FIRST, so r is released on the return path -> silent (was bailed before). (2) FinallyReturnLeak: an early return with the dispose AFTER the try (empty finally) leaks on that path. (3) SwitchAllDispose: a no-default switch where every case disposes — modelled with the last case as the tail (no empty no-match branch), so it stays silent (Model A: no phantom-leak FP on an exhaustive switch). (4) SwitchOneLeak: a switch whose default/else branch leaks.", "components": [], diff --git a/tests/fixtures/ownir/flow_leak_on_else.facts.json b/tests/fixtures/ownir/flow_leak_on_else.facts.json index e67c533c..38ac668d 100644 --- a/tests/fixtures/ownir/flow_leak_on_else.facts.json +++ b/tests/fixtures/ownir/flow_leak_on_else.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Extracted", "components": [], "functions": [ diff --git a/tests/fixtures/ownir/flow_leak_two_exits.facts.json b/tests/fixtures/ownir/flow_leak_two_exits.facts.json index 685cf399..bdfdbc90 100644 --- a/tests/fixtures/ownir/flow_leak_two_exits.facts.json +++ b/tests/fixtures/ownir/flow_leak_two_exits.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Extracted", "components": [], "functions": [ diff --git a/tests/fixtures/ownir/flow_nested_throw.facts.json b/tests/fixtures/ownir/flow_nested_throw.facts.json index 644a2b07..dd988b71 100644 --- a/tests/fixtures/ownir/flow_nested_throw.facts.json +++ b/tests/fixtures/ownir/flow_nested_throw.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Extracted", "comment": "Exception-edge RECALL slice: the IR the extractor now emits for three try shapes. (1) NestedThrowLeaks: the throw edge recurses into the `if` and lands before MayThrow() (a nested LEAF), where nestedLeak is still owned -> leaks. (2) DisposeInsideIfWithThrow: the same recursion, but the dispose precedes the throw in each branch, so cif is already released at the edge -> silent. (3) CtorThrowLeaksPrior: a `new` is a throw point; the edge before `var ctorLater = new ...` finds ctorPrior owned -> leaks, while ctorLater (acquired after the edge) is silent.", "components": [], diff --git a/tests/fixtures/ownir/flow_pool_partial.facts.json b/tests/fixtures/ownir/flow_pool_partial.facts.json index a2b2e5a4..312f2987 100644 --- a/tests/fixtures/ownir/flow_pool_partial.facts.json +++ b/tests/fixtures/ownir/flow_pool_partial.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Extracted", "comment": "Pool buffers on the FLOW path must read as a 'pooled buffer' (Return wording / [resource: pooled buffer]), not the generic 'disposable' — the extractor stamps the acquire kind 'pool' for an ArrayPool Rent so the bridge words it right. (1) PartialPoolLeak: a Rent returned on the fall-through but NOT on the throw edge -> 'may not be returned to the pool on every path'. (2) NeverReturnedPool: a Rent never returned -> 'rented but never returned to the pool'. (3) DisposableControl: a plain disposable on the same partial shape keeps the IDisposable wording. Regression for the flow-path pool mislabel surfaced by the --body-throw-edges Npgsql capstone (CompositeBuilder/BitStringConverters ArrayPool rents).", "components": [], diff --git a/tests/fixtures/ownir/flow_while.facts.json b/tests/fixtures/ownir/flow_while.facts.json index 2a46da9a..b080c970 100644 --- a/tests/fixtures/ownir/flow_while.facts.json +++ b/tests/fixtures/ownir/flow_while.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Extracted", "components": [], "functions": [ diff --git a/tests/fixtures/ownir/handoff_contract.facts.json b/tests/fixtures/ownir/handoff_contract.facts.json index 4f2eebb7..31f7ede0 100644 --- a/tests/fixtures/ownir/handoff_contract.facts.json +++ b/tests/fixtures/ownir/handoff_contract.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "HandoffBridge", "functions": [ { diff --git a/tests/fixtures/ownir/local_disposable.facts.json b/tests/fixtures/ownir/local_disposable.facts.json index de628c69..d4db5f31 100644 --- a/tests/fixtures/ownir/local_disposable.facts.json +++ b/tests/fixtures/ownir/local_disposable.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Streams", "components": [ { diff --git a/tests/fixtures/ownir/pool.facts.json b/tests/fixtures/ownir/pool.facts.json index d46b58b4..02b261d3 100644 --- a/tests/fixtures/ownir/pool.facts.json +++ b/tests/fixtures/ownir/pool.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "PoolApp", "components": [ { diff --git a/tests/fixtures/ownir/protocol_isloaded_clean.facts.json b/tests/fixtures/ownir/protocol_isloaded_clean.facts.json index 5b55a906..d6655206 100644 --- a/tests/fixtures/ownir/protocol_isloaded_clean.facts.json +++ b/tests/fixtures/ownir/protocol_isloaded_clean.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Extracted", "components": [], "protocols": [ diff --git a/tests/fixtures/ownir/protocol_isloaded_violation.facts.json b/tests/fixtures/ownir/protocol_isloaded_violation.facts.json index 5c34234f..0daae80f 100644 --- a/tests/fixtures/ownir/protocol_isloaded_violation.facts.json +++ b/tests/fixtures/ownir/protocol_isloaded_violation.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Extracted", "components": [], "protocols": [ diff --git a/tests/fixtures/ownir/sample.facts.json b/tests/fixtures/ownir/sample.facts.json index baced1c5..81bbac18 100644 --- a/tests/fixtures/ownir/sample.facts.json +++ b/tests/fixtures/ownir/sample.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "WpfApp", "components": [ { diff --git a/tests/fixtures/ownir/subscribe.facts.json b/tests/fixtures/ownir/subscribe.facts.json index f43e72b1..61ec1a06 100644 --- a/tests/fixtures/ownir/subscribe.facts.json +++ b/tests/fixtures/ownir/subscribe.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "WpfMessenger", "components": [ { diff --git a/tests/fixtures/ownir/timer.facts.json b/tests/fixtures/ownir/timer.facts.json index 370d5672..d0e257d6 100644 --- a/tests/fixtures/ownir/timer.facts.json +++ b/tests/fixtures/ownir/timer.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "WpfTimers", "components": [ { diff --git a/tests/fixtures/ownir/unitofwork_flow.facts.json b/tests/fixtures/ownir/unitofwork_flow.facts.json index 2a79f5d7..217885de 100644 --- a/tests/fixtures/ownir/unitofwork_flow.facts.json +++ b/tests/fixtures/ownir/unitofwork_flow.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Extracted", "components": [], "functions": [ diff --git a/tests/fixtures/ownir/unresolved.facts.json b/tests/fixtures/ownir/unresolved.facts.json index 37ab2b70..6a420b51 100644 --- a/tests/fixtures/ownir/unresolved.facts.json +++ b/tests/fixtures/ownir/unresolved.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "TierA", "components": [ { diff --git a/tests/fixtures/ownir_validation.json b/tests/fixtures/ownir_validation.json index 7d87929c..90a2a667 100644 --- a/tests/fixtures/ownir_validation.json +++ b/tests/fixtures/ownir_validation.json @@ -41,7 +41,7 @@ "why": "the current version stated explicitly", "section": "version", "document": { - "ownir_version": 0 + "ownir_version": 1 }, "raw": false, "verdict": "accept", @@ -53,7 +53,7 @@ "why": "every known section present but empty — shape-valid, nothing to check", "section": "root", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [], "services": [], "effects": [], @@ -71,7 +71,7 @@ "why": "an unrecognised TOP-LEVEL key is additive and accepted; the strict door gates known vocabulary, it is not a closed-world schema", "section": "root", "document": { - "ownir_version": 0, + "ownir_version": 1, "future_section": [ { "whatever": 1 @@ -88,7 +88,7 @@ "why": "a subscription with no `resource` defaults to 'subscription', which is a known kind", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -107,7 +107,7 @@ "why": "all eight known kinds in one document — the acceptance side of the closed vocabulary", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -149,7 +149,7 @@ "why": "column 1 is the smallest legal column — the boundary just inside the 1-based contract", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -170,7 +170,7 @@ "why": "all three DI lifetimes accepted", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "A", @@ -196,7 +196,7 @@ "why": "all four parameter effects accepted", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -230,7 +230,7 @@ "why": "`effect` is optional — absent is not the same as an unknown value", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -300,7 +300,7 @@ "raw": false, "verdict": "reject", "category": "version", - "message": "OwnIR facts are schema v99, but this core understands v0. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it." + "message": "OwnIR facts are schema v99, but this core understands v1. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it." }, { "name": "version-string", @@ -355,7 +355,7 @@ "why": "`components` must be an array", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": { "a": 1 } @@ -370,7 +370,7 @@ "why": "an array, but not of objects", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ 1, 2 @@ -386,7 +386,7 @@ "why": "each component's `subscriptions` must be an array of objects", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": 7 @@ -403,7 +403,7 @@ "why": "…of OBJECTS, not scalars", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -422,7 +422,7 @@ "why": "`resource` must be a string before its value can be checked", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -443,7 +443,7 @@ "why": "IR4: a present-but-unknown kind changes routing, so it is rejected at the door rather than mis-routed. A new kind must bump OWNIR_VERSION", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -464,7 +464,7 @@ "why": "the empty string is present-but-unknown, not absent", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -485,7 +485,7 @@ "why": "optional `type`, present ⇒ string", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -506,7 +506,7 @@ "why": "optional `source_type`, present ⇒ string", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -527,7 +527,7 @@ "why": "optional `source_provenance`, present ⇒ string", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -548,7 +548,7 @@ "why": "optional `ignore_reason`, present ⇒ string", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -569,7 +569,7 @@ "why": "#317: a column is 1-based or absent. `0` is a producer bug, and reading it as 'unknown' would hide the bug while looking correct", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -590,7 +590,7 @@ "why": "…and so is a negative column", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -611,7 +611,7 @@ "why": "the bool-is-int trap again: `True` would otherwise be accepted as column 1 — a fabricated coordinate. `shape`, not `location`: a bool has no integer form, so the 1-based rule never gets a value to judge", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -632,7 +632,7 @@ "why": "a string column has no integer form either — same axis, and the reference funnels it through the same message", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -653,7 +653,7 @@ "why": "`services` must be an array of objects", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": { "a": 1 } @@ -668,7 +668,7 @@ "why": "the DI lifetime is a closed set", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -686,7 +686,7 @@ "why": "the service name is the identity the DI graph joins on", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "", @@ -704,7 +704,7 @@ "why": "…and it must be a string", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": 7, @@ -722,7 +722,7 @@ "why": "`deps` is an array of strings", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -741,7 +741,7 @@ "why": "…of STRINGS", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -762,7 +762,7 @@ "why": "same for `weak_deps`", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -783,7 +783,7 @@ "why": "same for `root_resolves`", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -804,7 +804,7 @@ "why": "same for `scope_cached`", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -825,7 +825,7 @@ "why": "`file` is a string", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -844,7 +844,7 @@ "why": "`line` is an integer", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -863,7 +863,7 @@ "why": "…and a bool is not an integer", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -882,7 +882,7 @@ "why": "`ctor_file` is a string", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -901,7 +901,7 @@ "why": "`ctor_line` is an integer", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -920,7 +920,7 @@ "why": "`ctor_type` is a string", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -939,7 +939,7 @@ "why": "`root_resolve_sites` is an array of {type,file,line} objects", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -960,7 +960,7 @@ "why": "same for `scope_cache_sites`", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -981,7 +981,7 @@ "why": "`effects` must be an array of objects", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": { "a": 1 } @@ -996,7 +996,7 @@ "why": "`deps` is an array of strings", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "deps": [ @@ -1015,7 +1015,7 @@ "why": "`io` is a boolean", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "io": "yes" @@ -1032,7 +1032,7 @@ "why": "`line` is an integer", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "line": "3" @@ -1049,7 +1049,7 @@ "why": "`bindings` is an array of objects", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "bindings": [ @@ -1068,7 +1068,7 @@ "why": "binding `name` is a string", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "bindings": [ @@ -1089,7 +1089,7 @@ "why": "binding `init` is a string", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "bindings": [ @@ -1110,7 +1110,7 @@ "why": "binding `refs` is string array", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "bindings": [ @@ -1133,7 +1133,7 @@ "why": "binding `line` is an integer", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "bindings": [ @@ -1154,7 +1154,7 @@ "why": "`functions` must be an array of objects", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": { "a": 1 } @@ -1169,7 +1169,7 @@ "why": "`sig` present ⇒ string. It is the overload key MOS resolution joins on, so a non-string is not a cosmetic problem", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "sig": 7 @@ -1186,7 +1186,7 @@ "why": "`params` is an array of objects", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": "x" @@ -1203,7 +1203,7 @@ "why": "…of OBJECTS", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -1222,7 +1222,7 @@ "why": "the parameter name is the identity effects attach to", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -1243,7 +1243,7 @@ "why": "…and it must be a string", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -1264,7 +1264,7 @@ "why": "param `line` is an integer", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -1286,7 +1286,7 @@ "why": "the parameter effect is a closed set", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -1308,7 +1308,7 @@ "why": "`protocols` must be an array of objects", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": { "a": 1 } @@ -1323,7 +1323,7 @@ "why": "the protocol name is the identity verdicts map back by, so a duplicate is an identity collision, not a harmless repeat. Both records are individually VALID — the older version of this control used records with no `opens`/`closes`, which the reference refused for record shape long before it compared any names", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -1363,7 +1363,7 @@ "why": "`protocol_functions` must be an array of objects", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": { "a": 1 } @@ -1386,7 +1386,7 @@ "raw": false, "verdict": "reject", "category": "version", - "message": "OwnIR facts are schema v99, but this core understands v0. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it." + "message": "OwnIR facts are schema v99, but this core understands v1. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it." }, { "name": "order-version-before-resource", @@ -1407,7 +1407,7 @@ "raw": false, "verdict": "reject", "category": "version", - "message": "OwnIR facts are schema v99, but this core understands v0. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it." + "message": "OwnIR facts are schema v99, but this core understands v1. Build the Roslyn extractor and the Python core from the same commit — the OwnIR fact vocabulary changed between the version that produced this file and the one reading it." }, { "name": "order-root-before-version", @@ -1438,7 +1438,7 @@ "why": "`resource` must be a STRING before its value can be tested against the closed set — a shape failure, not a vocabulary one", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -1460,7 +1460,7 @@ "why": "sections are validated in declaration order: `components` before `services`, so a document breaking both reports components", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": { "a": 1 }, @@ -1478,7 +1478,7 @@ "why": "there is no default lifetime: an absent one is `None`, which is outside the closed set exactly like a misspelt one. This is the case `_svc()` could not express, and the port accepted it", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S" @@ -1495,7 +1495,7 @@ "why": "a service record with nothing in it: the lifetime gate fires first, so this is a vocabulary failure and not a missing-name one", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ {} ] @@ -1510,7 +1510,7 @@ "why": "an explicit null is the same as absent here — both are `None`, and neither is in the closed set", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -1528,7 +1528,7 @@ "why": "with a valid lifetime, an absent name reaches the identity check", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "lifetime": "singleton" @@ -1545,7 +1545,7 @@ "why": "the reference checks `lifetime` BEFORE `name`, so a record that breaks both is a vocabulary failure. A port that validates identity first reports `identity` and is wrong about which rule fired", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "", @@ -1563,7 +1563,7 @@ "why": "…and `name` before the remaining service fields", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "lifetime": "singleton", @@ -1582,7 +1582,7 @@ "why": "within one subscription, the resource vocabulary precedes the column contract", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -1604,7 +1604,7 @@ "why": "…and the column precedes the optional `type`", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -1626,7 +1626,7 @@ "why": "inside a function, `sig` is checked before the body's columns", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "sig": 7, @@ -1649,7 +1649,7 @@ "why": "and the BODY's columns are checked before `params` — the least obvious edge in the whole door, because params read like the more primitive thing", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -1676,7 +1676,7 @@ "why": "within a param, identity precedes the line", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -1698,7 +1698,7 @@ "why": "…the line precedes the column", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -1721,7 +1721,7 @@ "why": "…and the column precedes the effect vocabulary", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -1744,7 +1744,7 @@ "why": "a components SHAPE failure outranks a services VOCABULARY failure, because components is validated first — completely. A door that runs all semantic gates before any shape check reports the vocabulary error and inverts the contract", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": { "a": 1 }, @@ -1764,7 +1764,7 @@ "why": "same precedence with a column failure standing in for the shape one", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -1790,7 +1790,7 @@ "why": "and the same one section later: services before effects", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "lifetime": "bad" @@ -1810,7 +1810,7 @@ "why": "effects before functions", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": { "a": 1 }, @@ -1834,7 +1834,7 @@ "why": "functions before protocols", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -1856,7 +1856,7 @@ "why": "protocols before protocol_functions — the last edge in the chain", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "" @@ -1874,7 +1874,7 @@ "why": "column 1 on a flow op, nested and not — the acceptance twin the rejections below need", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -1920,7 +1920,7 @@ "why": "a non-array `body` is SKIPPED, not rejected: the reference returns early rather than raising. Pinned because it is the kind of tolerance a port silently tightens", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": 7 @@ -1937,7 +1937,7 @@ "why": "…and a non-object op inside the body is skipped the same way", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -1958,7 +1958,7 @@ "why": "the 1-based contract reaches flow ops, not just subscriptions", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -1980,7 +1980,7 @@ "why": "…negative likewise", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -2002,7 +2002,7 @@ "why": "…and the bool-is-int trap, which is a representability failure wherever it appears", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -2024,7 +2024,7 @@ "why": "recursion into `then` — a hoisted branch acquire", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -2051,7 +2051,7 @@ "why": "…into `else`", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -2078,7 +2078,7 @@ "why": "…into a loop `body`", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -2105,7 +2105,7 @@ "why": "…and through three levels, so a one-level-deep port fails here", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -2142,7 +2142,7 @@ "why": "the 1-based boundary on a parameter coordinate", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -2164,7 +2164,7 @@ "why": "the same contract on `params[].column` — a separate call site in the reference, and one the port did not have at all", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -2186,7 +2186,7 @@ "why": "…negative likewise", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -2208,7 +2208,7 @@ "why": "…and the bool-is-int trap on the third call site", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -2230,7 +2230,7 @@ "why": "…and a string, which has no integer form to be 1-based about", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -2252,7 +2252,7 @@ "why": "a name plus an opens/closes pair is a complete protocol", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2279,7 +2279,7 @@ "why": "every optional key at once: barriers, allow with narrowed args, an explicit scope, a description, and `exit_barriers` false — legal only BECAUSE a barrier is present", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2328,7 +2328,7 @@ "why": "the other matcher kind: `call`, with and without narrowed args", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2357,7 +2357,7 @@ "why": "two protocols with DIFFERENT names — the twin that makes the duplicate-name rejection discriminating rather than a test of 'more than one protocol is refused'", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "A", @@ -2397,7 +2397,7 @@ "why": "`protocols` is checked only for LIST-ness at the top; a scalar entry is refused by the record parser", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ 7 ] @@ -2412,7 +2412,7 @@ "why": "…and null is not a record", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ null ] @@ -2427,7 +2427,7 @@ "why": "the protocol name is required, not defaulted", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "opens": {}, @@ -2445,7 +2445,7 @@ "why": "…and must be non-empty", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "", @@ -2472,7 +2472,7 @@ "why": "…and a string", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": 7, @@ -2499,7 +2499,7 @@ "why": "a protocol without `opens` cannot state what it is tracking", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2520,7 +2520,7 @@ "why": "…and likewise `closes`", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2541,7 +2541,7 @@ "why": "a matcher is an object", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2564,7 +2564,7 @@ "why": "the matcher kind is a closed vocabulary (assign | call), fail-loud like a flow op", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2590,7 +2590,7 @@ "why": "…and absent is outside that set too", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2615,7 +2615,7 @@ "why": "the assign target is the member name the rule joins events by", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2642,7 +2642,7 @@ "why": "an opens/closes assign matcher must state the written boolean — 'any write opens' is not a checkable protocol", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2668,7 +2668,7 @@ "why": "…and that value is a boolean", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2695,7 +2695,7 @@ "why": "the callee is the name a call event joins by", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2721,7 +2721,7 @@ "why": "narrowed args are strings", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2750,7 +2750,7 @@ "why": "`barriers` is an array", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2778,7 +2778,7 @@ "why": "`allow` is an array", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2806,7 +2806,7 @@ "why": "`exit_barriers` is a boolean", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2834,7 +2834,7 @@ "why": "no barriers AND no exit barriers: the rule can structurally never fire, which the reference refuses as decoration. Every value here is correctly typed and in vocabulary — what fails is meaning, so the category is `well_formedness`", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2862,7 +2862,7 @@ "why": "a barrier identical to `opens` is dead — the walk checks opens first, so the barrier can never fire. Same category, different mechanism: the record is well-typed and means nothing", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2896,7 +2896,7 @@ "why": "`exit_barriers: false` is legal WITH a barrier — the twin that makes `protocol-never-fires` a rejection about meaning rather than a rejection of the field", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2930,7 +2930,7 @@ "why": "…and a barrier that is not `opens` is legal, which is what makes the equality the defect rather than the presence", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2964,7 +2964,7 @@ "why": "`scope` is an object", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -2992,7 +2992,7 @@ "why": "`scope.methods` is an array — a CONTAINER failure", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -3022,7 +3022,7 @@ "why": "…of non-empty method names. The reference raises one message for this and the case above; the ledger separates them because a missing container and an unusable name are different defects", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -3054,7 +3054,7 @@ "why": "`description` is a string", "section": "protocols", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -3082,7 +3082,7 @@ "why": "the name is read before `opens`/`closes` are required, so a record missing everything reports identity", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "" @@ -3099,7 +3099,7 @@ "why": "`opens` is parsed before `closes`", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": [ { "name": "P", @@ -3120,7 +3120,7 @@ "why": "`events` defaults to empty, so a name alone is a valid record", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M" @@ -3137,7 +3137,7 @@ "why": "all six event kinds including the two recursive ones — the acceptance side of the closed `ev` vocabulary", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3213,7 +3213,7 @@ "why": "like `protocols`, only list-ness is checked at the top", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ 7 ] @@ -3228,7 +3228,7 @@ "why": "…and null is not a record", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ null ] @@ -3243,7 +3243,7 @@ "why": "the method name is what a protocol's scope matches against", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "" @@ -3260,7 +3260,7 @@ "why": "…and it is required", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "file": "a.cs" @@ -3277,7 +3277,7 @@ "why": "`file` is optional but, present, a string", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3295,7 +3295,7 @@ "why": "`events` is an ordered array", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3313,7 +3313,7 @@ "why": "…of objects", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3333,7 +3333,7 @@ "why": "the `ev` discriminator is closed (IR4): a present-but-unknown value is rejected, never skipped", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3355,7 +3355,7 @@ "why": "…and an absent one is outside the set too", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3377,7 +3377,7 @@ "why": "an event line is an integer", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3400,7 +3400,7 @@ "why": "…and the bool-is-int trap reaches here as well", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3423,7 +3423,7 @@ "why": "an assign event's target is a name slot", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3446,7 +3446,7 @@ "why": "…its value is a boolean or absent (absent = an opaque write)", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3470,7 +3470,7 @@ "why": "a call event's callee is a name slot", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3493,7 +3493,7 @@ "why": "…its distinguished arg is a string or absent", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3517,7 +3517,7 @@ "why": "the event vocabulary is enforced recursively — inside `then`", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3544,7 +3544,7 @@ "why": "…inside `else`", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3571,7 +3571,7 @@ "why": "…and inside a loop body", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3598,7 +3598,7 @@ "why": "…through three levels, so a one-level-deep port fails here", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3631,7 +3631,7 @@ "why": "the `ev` vocabulary is tested before the line", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3654,7 +3654,7 @@ "why": "…and the line before the per-kind fields", "section": "order", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -3678,7 +3678,7 @@ "why": "a present null is not an absent section", "section": "root", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": null }, "raw": false, @@ -3691,7 +3691,7 @@ "why": "…same for services", "section": "root", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": null }, "raw": false, @@ -3704,7 +3704,7 @@ "why": "…effects", "section": "root", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": null }, "raw": false, @@ -3717,7 +3717,7 @@ "why": "…functions", "section": "root", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": null }, "raw": false, @@ -3730,7 +3730,7 @@ "why": "…protocols", "section": "root", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocols": null }, "raw": false, @@ -3743,7 +3743,7 @@ "why": "…and protocol_functions", "section": "root", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": null }, "raw": false, @@ -3756,7 +3756,7 @@ "why": "…and one level down", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": null @@ -3773,7 +3773,7 @@ "why": "…likewise params", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": null @@ -3790,7 +3790,7 @@ "why": "…and a null string array", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -3809,7 +3809,7 @@ "why": "…and null bindings", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "bindings": null @@ -3826,7 +3826,7 @@ "why": "a null scalar is not an absent scalar either", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -3845,7 +3845,7 @@ "why": "…same for a string scalar", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -3864,7 +3864,7 @@ "why": "…and `resource` null fails the string check before the vocabulary", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -3885,7 +3885,7 @@ "why": "the column is the exception: the reference returns early on None, so an explicit null IS accepted where a null line is not", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -3906,7 +3906,7 @@ "why": "…and a null effect is read as absent, not as an unknown value", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -3928,7 +3928,7 @@ "why": "…and a null `sig` is accepted, because the check is `is not None`", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "sig": null @@ -3945,7 +3945,7 @@ "why": "well-formed DI004/DI005 call-site metadata", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -3977,7 +3977,7 @@ "why": "every site field is defaulted, so `{}` is a legal site", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -3998,7 +3998,7 @@ "why": "the bool-is-int trap inside a site record", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4021,7 +4021,7 @@ "why": "…and a site `type` is a string", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4044,7 +4044,7 @@ "why": "…both site arrays carry the same contract", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4067,7 +4067,7 @@ "why": "the largest REPRESENTABLE line — accepted until the coordinate domain existed, now outside it. Representability and domain are two axes: this value still HAS a signed-64 form, which is exactly why it is `location` and not `shape`", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4086,7 +4086,7 @@ "why": "…and the smallest. The range is still closed at BOTH ends and a port that bounded only the top would pass a one-sided test — the ends have simply moved from the form to the domain", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4105,7 +4105,7 @@ "why": "…and the largest column, which now carries the same upper bound a line does", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -4126,7 +4126,7 @@ "why": "a negative line is REJECTED: it is not the 1-based column rule borrowed, it is the line's own domain — no producer emits one and nothing downstream can point at it (spec/OwnIR.md §4.2)", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4145,7 +4145,7 @@ "why": "…and zero is still the line default, unchanged: it means 'unknown / file-level' and is the bottom of the domain, not an edge case that survived", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4164,7 +4164,7 @@ "why": "one below the range: no representable signed-64 form", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4183,7 +4183,7 @@ "why": "…and one above. This is the value that used to escape into serde: it is still an integer to the parser, so a raw-layer check written as 'is it an integer' waves it through and the typed model refuses it afterwards — a rule living in the model instead of the door", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4202,7 +4202,7 @@ "why": "…the top of the band that is still integer-shaped to the parser", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4221,7 +4221,7 @@ "why": "…and past it, where the parser gives up on integer-ness entirely. Same verdict, same category, different route: the taxonomy must not be able to tell these apart", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4240,7 +4240,7 @@ "why": "a float is not a representable coordinate even when it is whole — and it lands in the same branch as `line-above-u64`, which is why one category over both is the honest reading rather than a convenience", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4259,7 +4259,7 @@ "why": "the constructor coordinate", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4278,7 +4278,7 @@ "why": "a root-resolve site coordinate", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4301,7 +4301,7 @@ "why": "…and the second site array, a separate loop in the reference", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -4324,7 +4324,7 @@ "why": "an effect coordinate", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "line": 9223372036854775808 @@ -4341,7 +4341,7 @@ "why": "…and a binding coordinate", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "bindings": [ @@ -4362,7 +4362,7 @@ "why": "a parameter coordinate", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -4384,7 +4384,7 @@ "why": "…and an event coordinate, which reaches the bound through the shared obligation parser rather than through `load()` directly", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -4407,7 +4407,7 @@ "why": "an unrepresentable column is a representability failure…", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -4428,7 +4428,7 @@ "why": "…by either parser route…", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -4449,7 +4449,7 @@ "why": "…as is a float, even a whole one. Previously recorded as `location` next to `line-float`'s `shape`, with a comment calling the difference intentional — the same violation classified two ways because the reference raises one message for both and the category had been read off the message instead of the mechanism", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -4470,7 +4470,7 @@ "why": "…and the 1-based rule still fires for a value that IS representable, so the new axis did not swallow the old one", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -4491,7 +4491,7 @@ "why": "a flow body nested exactly 32 levels is accepted — the boundary case that caught the reference's own off-by-one", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -4673,7 +4673,7 @@ "why": "…and one level further is refused", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -4860,7 +4860,7 @@ "why": "…through `else` as well", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -5047,7 +5047,7 @@ "why": "…and through a loop body", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -5234,7 +5234,7 @@ "why": "an event tree nested exactly 32 levels is accepted", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -5449,7 +5449,7 @@ "why": "…and one level further is refused", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -5670,7 +5670,7 @@ "why": "…through `else`…", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -5891,7 +5891,7 @@ "why": "…and a loop body", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -6112,7 +6112,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level'", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6131,7 +6131,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6150,7 +6150,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6169,7 +6169,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6188,7 +6188,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level'", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6207,7 +6207,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6226,7 +6226,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6245,7 +6245,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6264,7 +6264,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level'", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6287,7 +6287,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6310,7 +6310,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6333,7 +6333,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6356,7 +6356,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level'", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6379,7 +6379,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6402,7 +6402,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6425,7 +6425,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "services", "document": { - "ownir_version": 0, + "ownir_version": 1, "services": [ { "name": "S", @@ -6448,7 +6448,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level'", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "line": 0 @@ -6465,7 +6465,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "line": 2147483647 @@ -6482,7 +6482,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "line": -1 @@ -6499,7 +6499,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "line": 2147483648 @@ -6516,7 +6516,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level'", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "bindings": [ @@ -6537,7 +6537,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "bindings": [ @@ -6558,7 +6558,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "bindings": [ @@ -6579,7 +6579,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "effects", "document": { - "ownir_version": 0, + "ownir_version": 1, "effects": [ { "bindings": [ @@ -6600,7 +6600,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level'", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -6622,7 +6622,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -6644,7 +6644,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -6666,7 +6666,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -6688,7 +6688,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level'", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -6711,7 +6711,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -6734,7 +6734,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -6757,7 +6757,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "protocol_functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "protocol_functions": [ { "name": "M", @@ -6780,7 +6780,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level' — a field `load()` checked NOWHERE before this change, not even for type (§4.2's recorded exception, now closed)", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -6801,7 +6801,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -6822,7 +6822,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -6843,7 +6843,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -6864,7 +6864,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level' — a field `load()` checked NOWHERE before this change, not even for type (§4.2's recorded exception, now closed)", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -6886,7 +6886,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -6908,7 +6908,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -6930,7 +6930,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -6952,7 +6952,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level' — a field `load()` checked NOWHERE before this change, not even for type (§4.2's recorded exception, now closed)", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -6979,7 +6979,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7006,7 +7006,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7033,7 +7033,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7060,7 +7060,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level' — a field `load()` checked NOWHERE before this change, not even for type (§4.2's recorded exception, now closed)", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7087,7 +7087,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7114,7 +7114,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7141,7 +7141,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7168,7 +7168,7 @@ "why": "`0` is the bottom of the domain and means 'unknown / file-level' — a field `load()` checked NOWHERE before this change, not even for type (§4.2's recorded exception, now closed)", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7195,7 +7195,7 @@ "why": "…and 2147483647 is the top, accepted exactly", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7222,7 +7222,7 @@ "why": "one below the bottom: a representable coordinate outside its domain, so `location` rather than `shape`", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7249,7 +7249,7 @@ "why": "…and one above the top, the value a port that kept the old signed-64 bound would still accept", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7276,7 +7276,7 @@ "why": "a string line has no integer form — `shape`, on the other axis from the domain rejections above", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -7297,7 +7297,7 @@ "why": "…and the bool-is-int trap, which would otherwise read as line 1", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -7318,7 +7318,7 @@ "why": "…and a PRESENT null, which is not the same as absent: the reference reads the slot with a `0` default, so only an absent key takes it", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -7339,7 +7339,7 @@ "why": "a string line has no integer form — `shape`, on the other axis from the domain rejections above", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7361,7 +7361,7 @@ "why": "…and the bool-is-int trap, which would otherwise read as line 1", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7383,7 +7383,7 @@ "why": "…and a PRESENT null, which is not the same as absent: the reference reads the slot with a `0` default, so only an absent key takes it", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7405,7 +7405,7 @@ "why": "a string line has no integer form — `shape`, on the other axis from the domain rejections above", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7432,7 +7432,7 @@ "why": "…and the bool-is-int trap, which would otherwise read as line 1", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7459,7 +7459,7 @@ "why": "…and a PRESENT null, which is not the same as absent: the reference reads the slot with a `0` default, so only an absent key takes it", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7486,7 +7486,7 @@ "why": "a string line has no integer form — `shape`, on the other axis from the domain rejections above", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7513,7 +7513,7 @@ "why": "…and the bool-is-int trap, which would otherwise read as line 1", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7540,7 +7540,7 @@ "why": "…and a PRESENT null, which is not the same as absent: the reference reads the slot with a `0` default, so only an absent key takes it", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7567,7 +7567,7 @@ "why": "a string line has no integer form — `shape`, on the other axis from the domain rejections above", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7594,7 +7594,7 @@ "why": "…and the bool-is-int trap, which would otherwise read as line 1", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7621,7 +7621,7 @@ "why": "…and a PRESENT null, which is not the same as absent: the reference reads the slot with a `0` default, so only an absent key takes it", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7648,7 +7648,7 @@ "why": "the top of the column domain, accepted exactly", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -7669,7 +7669,7 @@ "why": "…and one past it: representable, positive, and outside the domain — `location`, the same axis the 1-based rule is on", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -7690,7 +7690,7 @@ "why": "the top of the column domain, accepted exactly", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -7712,7 +7712,7 @@ "why": "…and one past it: representable, positive, and outside the domain — `location`, the same axis the 1-based rule is on", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "params": [ @@ -7734,7 +7734,7 @@ "why": "the top of the column domain, accepted exactly", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7756,7 +7756,7 @@ "why": "…and one past it: representable, positive, and outside the domain — `location`, the same axis the 1-based rule is on", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7778,7 +7778,7 @@ "why": "a column BELOW the representable form. It is `shape` by mechanism — there is no signed-64 integer here for a 1-based rule to be about — even though the reference's message names the 1-based rule, because it tests `v < 1` before the form. The category is read off the mechanism, which is this ledger's rule and the reason the taxonomy has two axes at all", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -7799,7 +7799,7 @@ "why": "an unknown resource kind and an out-of-domain line in one record: the kind is checked first, so this is `vocabulary`", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -7821,7 +7821,7 @@ "why": "…and the line precedes the column of the same node (§4.1), so a record breaking both reports the line's domain, not the column's type", "section": "components", "document": { - "ownir_version": 0, + "ownir_version": 1, "components": [ { "subscriptions": [ @@ -7843,7 +7843,7 @@ "why": "…and the same order inside a flow op", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ @@ -7866,7 +7866,7 @@ "why": "the BODY's coordinates still precede `params` — the least obvious edge in the door, and now observable through a line as well as a column", "section": "functions", "document": { - "ownir_version": 0, + "ownir_version": 1, "functions": [ { "body": [ diff --git a/tests/fixtures/repro/canonical_key_order.facts.json b/tests/fixtures/repro/canonical_key_order.facts.json index 9c843ef4..24a0a3ef 100644 --- a/tests/fixtures/repro/canonical_key_order.facts.json +++ b/tests/fixtures/repro/canonical_key_order.facts.json @@ -10,5 +10,5 @@ "name": "Zed" } ], - "ownir_version": 0 + "ownir_version": 1 } diff --git a/tests/fixtures/repro/canonical_key_order.repro.json b/tests/fixtures/repro/canonical_key_order.repro.json index 0e8772f8..353447a5 100644 --- a/tests/fixtures/repro/canonical_key_order.repro.json +++ b/tests/fixtures/repro/canonical_key_order.repro.json @@ -1,16 +1,16 @@ { "repro_version": 3, "input": { - "ownir_version": 0, + "ownir_version": 1, "raw": { "algorithm": "sha256", - "digest": "ab2653de96439e538502e2689ddd93fdf8fd6a8956190320245653af75ea0790", + "digest": "fbb8fcbd328fd7ccc555d9f41d3878991ce628f92e3642091b4218eacb6ad3dc", "bytes": 377, - "base64": "ewogICJtb2R1bGUiOiAiS2V5T3JkZXIiLAogICJjb21wb25lbnRzIjogWwogICAgewogICAgICAic3Vic2NyaXB0aW9ucyI6IFsKICAgICAgICB7InJlbGVhc2VkIjogZmFsc2UsICJyZXNvdXJjZSI6ICJzdWJzY3JpYmUiLCAibGluZSI6IDMsICJldmVudCI6ICJiLlpldGEiLCAiaGFuZGxlciI6ICJPblpldGEifSwKICAgICAgICB7ImV2ZW50IjogImEuQWxwaGEiLCAibGluZSI6IDQsICJyZXNvdXJjZSI6ICJzdWJzY3JpYmUiLCAicmVsZWFzZWQiOiB0cnVlLCAiaGFuZGxlciI6ICJPbkFscGhhIn0KICAgICAgXSwKICAgICAgImZpbGUiOiAiS2V5T3JkZXIuY3MiLAogICAgICAibmFtZSI6ICJaZWQiCiAgICB9CiAgXSwKICAib3duaXJfdmVyc2lvbiI6IDAKfQo=" + "base64": "ewogICJtb2R1bGUiOiAiS2V5T3JkZXIiLAogICJjb21wb25lbnRzIjogWwogICAgewogICAgICAic3Vic2NyaXB0aW9ucyI6IFsKICAgICAgICB7InJlbGVhc2VkIjogZmFsc2UsICJyZXNvdXJjZSI6ICJzdWJzY3JpYmUiLCAibGluZSI6IDMsICJldmVudCI6ICJiLlpldGEiLCAiaGFuZGxlciI6ICJPblpldGEifSwKICAgICAgICB7ImV2ZW50IjogImEuQWxwaGEiLCAibGluZSI6IDQsICJyZXNvdXJjZSI6ICJzdWJzY3JpYmUiLCAicmVsZWFzZWQiOiB0cnVlLCAiaGFuZGxlciI6ICJPbkFscGhhIn0KICAgICAgXSwKICAgICAgImZpbGUiOiAiS2V5T3JkZXIuY3MiLAogICAgICAibmFtZSI6ICJaZWQiCiAgICB9CiAgXSwKICAib3duaXJfdmVyc2lvbiI6IDEKfQo=" }, "canonical": { "algorithm": "sha256", - "digest": "e48d4893337f2a3180388946f64524119451eecf4b55c84b9d1cdd6ede17e0cb", + "digest": "84301b7dadc06e52b558b3da71322a8f1d863d45694e9ebe7647c9dce5ebb049", "bytes": 283 }, "document": { @@ -37,7 +37,7 @@ "name": "Zed" } ], - "ownir_version": 0 + "ownir_version": 1 } }, "engines": [ @@ -45,19 +45,19 @@ "id": "python-ownlang", "consumed": { "algorithm": "sha256", - "digest": "ab2653de96439e538502e2689ddd93fdf8fd6a8956190320245653af75ea0790", + "digest": "fbb8fcbd328fd7ccc555d9f41d3878991ce628f92e3642091b4218eacb6ad3dc", "bytes": 377 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "KeyOrder", "resources": [ { @@ -208,7 +208,7 @@ "document": { "degraded": null, "module": "KeyOrder", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -249,7 +249,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "635f4f6808ead896f8e16de2a44bbbfb04410e66bd8fc3326eb9c5d98062424f", + "digest": "d41ab0ff6d94e1967ac19effc4eaf992fc23821d1f2d52f5fba79ccb934f5fe4", "bytes": 793 } } @@ -259,19 +259,19 @@ "id": "rust-own-bridge", "consumed": { "algorithm": "sha256", - "digest": "ab2653de96439e538502e2689ddd93fdf8fd6a8956190320245653af75ea0790", + "digest": "fbb8fcbd328fd7ccc555d9f41d3878991ce628f92e3642091b4218eacb6ad3dc", "bytes": 377 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "KeyOrder", "resources": [ { @@ -422,7 +422,7 @@ "document": { "degraded": null, "module": "KeyOrder", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -463,7 +463,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "635f4f6808ead896f8e16de2a44bbbfb04410e66bd8fc3326eb9c5d98062424f", + "digest": "d41ab0ff6d94e1967ac19effc4eaf992fc23821d1f2d52f5fba79ccb934f5fe4", "bytes": 793 } } diff --git a/tests/fixtures/repro/canonical_key_order.trace.json b/tests/fixtures/repro/canonical_key_order.trace.json index 3e599680..61b16fee 100644 --- a/tests/fixtures/repro/canonical_key_order.trace.json +++ b/tests/fixtures/repro/canonical_key_order.trace.json @@ -7,7 +7,7 @@ "engine": "python-ownlang", "input": { "algorithm": "sha256", - "digest": "e48d4893337f2a3180388946f64524119451eecf4b55c84b9d1cdd6ede17e0cb", + "digest": "84301b7dadc06e52b558b3da71322a8f1d863d45694e9ebe7647c9dce5ebb049", "bytes": 283 }, "layers": [ @@ -21,7 +21,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -210,7 +210,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", @@ -258,7 +258,7 @@ "engine": "rust-own-bridge", "input": { "algorithm": "sha256", - "digest": "e48d4893337f2a3180388946f64524119451eecf4b55c84b9d1cdd6ede17e0cb", + "digest": "84301b7dadc06e52b558b3da71322a8f1d863d45694e9ebe7647c9dce5ebb049", "bytes": 283 }, "layers": [ @@ -272,7 +272,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -461,7 +461,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", diff --git a/tests/fixtures/repro/canonical_minimal.facts.json b/tests/fixtures/repro/canonical_minimal.facts.json index 46c14f81..47940bbf 100644 --- a/tests/fixtures/repro/canonical_minimal.facts.json +++ b/tests/fixtures/repro/canonical_minimal.facts.json @@ -1,4 +1,4 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Minimal" } diff --git a/tests/fixtures/repro/canonical_minimal.repro.json b/tests/fixtures/repro/canonical_minimal.repro.json index ce33249d..ea712740 100644 --- a/tests/fixtures/repro/canonical_minimal.repro.json +++ b/tests/fixtures/repro/canonical_minimal.repro.json @@ -1,20 +1,20 @@ { "repro_version": 3, "input": { - "ownir_version": 0, + "ownir_version": 1, "raw": { "algorithm": "sha256", - "digest": "bc5f3772f24bbbb76ca24828da224d4ab991efe07a87d69db69d0bbef175460a", + "digest": "4573241a7888cac6145dd1005623c1edd6573a221d6779515781eb247041ebc7", "bytes": 48, - "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMCwKICAibW9kdWxlIjogIk1pbmltYWwiCn0K" + "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMSwKICAibW9kdWxlIjogIk1pbmltYWwiCn0K" }, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 }, "document": { - "ownir_version": 0, + "ownir_version": 1, "module": "Minimal" } }, @@ -23,19 +23,19 @@ "id": "python-ownlang", "consumed": { "algorithm": "sha256", - "digest": "bc5f3772f24bbbb76ca24828da224d4ab991efe07a87d69db69d0bbef175460a", + "digest": "4573241a7888cac6145dd1005623c1edd6573a221d6779515781eb247041ebc7", "bytes": 48 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "Minimal", "resources": [ { @@ -139,7 +139,7 @@ "document": { "degraded": null, "module": "Minimal", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -163,7 +163,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "4757bdfa72fd168dda7a1ad95f5eec18c6d72bf792166227faf83ac1b2e4a393", + "digest": "aa7b2312f4da58d124b29b75b86b5f05c767ef43cac802cffa19e566740b3096", "bytes": 289 } } @@ -173,19 +173,19 @@ "id": "rust-own-bridge", "consumed": { "algorithm": "sha256", - "digest": "bc5f3772f24bbbb76ca24828da224d4ab991efe07a87d69db69d0bbef175460a", + "digest": "4573241a7888cac6145dd1005623c1edd6573a221d6779515781eb247041ebc7", "bytes": 48 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "Minimal", "resources": [ { @@ -289,7 +289,7 @@ "document": { "degraded": null, "module": "Minimal", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -313,7 +313,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "4757bdfa72fd168dda7a1ad95f5eec18c6d72bf792166227faf83ac1b2e4a393", + "digest": "aa7b2312f4da58d124b29b75b86b5f05c767ef43cac802cffa19e566740b3096", "bytes": 289 } } diff --git a/tests/fixtures/repro/canonical_minimal.trace.json b/tests/fixtures/repro/canonical_minimal.trace.json index ec1d76eb..420af5cd 100644 --- a/tests/fixtures/repro/canonical_minimal.trace.json +++ b/tests/fixtures/repro/canonical_minimal.trace.json @@ -7,7 +7,7 @@ "engine": "python-ownlang", "input": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 }, "layers": [ @@ -21,7 +21,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -147,7 +147,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", @@ -176,7 +176,7 @@ "engine": "rust-own-bridge", "input": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 }, "layers": [ @@ -190,7 +190,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -316,7 +316,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", diff --git a/tests/fixtures/repro/canonical_torture.facts.json b/tests/fixtures/repro/canonical_torture.facts.json index 2ac2851a..c9361052 100644 --- a/tests/fixtures/repro/canonical_torture.facts.json +++ b/tests/fixtures/repro/canonical_torture.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Tortureé漢字😀", "canonical_probe": { "i64_max": 9223372036854775807, diff --git a/tests/fixtures/repro/canonical_torture.repro.json b/tests/fixtures/repro/canonical_torture.repro.json index 86f702b1..b4e8bb5e 100644 --- a/tests/fixtures/repro/canonical_torture.repro.json +++ b/tests/fixtures/repro/canonical_torture.repro.json @@ -1,20 +1,20 @@ { "repro_version": 3, "input": { - "ownir_version": 0, + "ownir_version": 1, "raw": { "algorithm": "sha256", - "digest": "93ef10f5f3541d1abc5fb75a4ffe74c26a764484b7505ab9039ab151e3883e99", + "digest": "a1c896ce83130584dcbb113c751547e4b488fae6798b00fcc919c8a1cdd8a170", "bytes": 1049, - "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMCwKICAibW9kdWxlIjogIlRvcnR1cmXDqea8ouWtl/CfmIAiLAogICJjYW5vbmljYWxfcHJvYmUiOiB7CiAgICAiaTY0X21heCI6IDkyMjMzNzIwMzY4NTQ3NzU4MDcsCiAgICAiaTY0X21pbiI6IC05MjIzMzcyMDM2ODU0Nzc1ODA4LAogICAgInplcm8iOiAwLAogICAgImJvb2xlYW5zIjogW3RydWUsIGZhbHNlXSwKICAgICJudWxsIjogbnVsbCwKICAgICJlbXB0eV9vYmplY3QiOiB7fSwKICAgICJlbXB0eV9hcnJheSI6IFtdLAogICAgImVtcHR5X3N0cmluZyI6ICIiLAogICAgInR3b19jaGFyX2VzY2FwZXMiOiAiXGJcdFxuXGZcciIsCiAgICAib3RoZXJfYzBfY29udHJvbHMiOiAiXHUwMDAwXHUwMDAxXHUwMDFmIiwKICAgICJkZWxldGVfaXNfbm90X2VzY2FwZWQiOiAiXHUwMDdmIiwKICAgICJxdW90ZV9hbmRfYmFja3NsYXNoIjogImhlIHNhaWQgXCJoaVwiIFxcIGJhY2siLAogICAgIm5ic3AiOiAiwqAiLAogICAgImxpbmVfYW5kX3BhcmFncmFwaF9zZXBhcmF0b3JzIjogIuKAqOKAqSIsCiAgICAiYnl0ZV9vcmRlcl9tYXJrIjogIu+7vyIsCiAgICAiYXN0cmFsIjogIvCfmIAiLAogICAgIvCfmIAiOiAiYW4gYXN0cmFsIGtleSBzb3J0cyBhZnRlciBldmVyeSBBU0NJSSBrZXkiLAogICAgIsOpIjogImFuIGFjY2VudGVkIGtleSBzb3J0cyBhZnRlciBldmVyeSBBU0NJSSBrZXkiLAogICAgIiI6ICJ0aGUgZW1wdHkga2V5IHNvcnRzIGZpcnN0IiwKICAgICJaIjogInVwcGVyY2FzZSBaIHNvcnRzIGJlZm9yZSBsb3dlcmNhc2UgYSIsCiAgICAiYSI6ICJsb3dlcmNhc2UgYSIKICB9LAogICJjb21wb25lbnRzIjogWwogICAgewogICAgICAibmFtZSI6ICJUb3J0dXJlVmlldyIsCiAgICAgICJmaWxlIjogIlTDtnJ0dXJlLmNzIiwKICAgICAgInN1YnNjcmlwdGlvbnMiOiBbCiAgICAgICAgeyJldmVudCI6ICJidXMuU3Vic2NyaWJlIiwgImhhbmRsZXIiOiAiT27mvKIiLCAibGluZSI6IDcsICJyZWxlYXNlZCI6IGZhbHNlLCAicmVzb3VyY2UiOiAic3Vic2NyaWJlIn0KICAgICAgXQogICAgfQogIF0KfQo=" + "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMSwKICAibW9kdWxlIjogIlRvcnR1cmXDqea8ouWtl/CfmIAiLAogICJjYW5vbmljYWxfcHJvYmUiOiB7CiAgICAiaTY0X21heCI6IDkyMjMzNzIwMzY4NTQ3NzU4MDcsCiAgICAiaTY0X21pbiI6IC05MjIzMzcyMDM2ODU0Nzc1ODA4LAogICAgInplcm8iOiAwLAogICAgImJvb2xlYW5zIjogW3RydWUsIGZhbHNlXSwKICAgICJudWxsIjogbnVsbCwKICAgICJlbXB0eV9vYmplY3QiOiB7fSwKICAgICJlbXB0eV9hcnJheSI6IFtdLAogICAgImVtcHR5X3N0cmluZyI6ICIiLAogICAgInR3b19jaGFyX2VzY2FwZXMiOiAiXGJcdFxuXGZcciIsCiAgICAib3RoZXJfYzBfY29udHJvbHMiOiAiXHUwMDAwXHUwMDAxXHUwMDFmIiwKICAgICJkZWxldGVfaXNfbm90X2VzY2FwZWQiOiAiXHUwMDdmIiwKICAgICJxdW90ZV9hbmRfYmFja3NsYXNoIjogImhlIHNhaWQgXCJoaVwiIFxcIGJhY2siLAogICAgIm5ic3AiOiAiwqAiLAogICAgImxpbmVfYW5kX3BhcmFncmFwaF9zZXBhcmF0b3JzIjogIuKAqOKAqSIsCiAgICAiYnl0ZV9vcmRlcl9tYXJrIjogIu+7vyIsCiAgICAiYXN0cmFsIjogIvCfmIAiLAogICAgIvCfmIAiOiAiYW4gYXN0cmFsIGtleSBzb3J0cyBhZnRlciBldmVyeSBBU0NJSSBrZXkiLAogICAgIsOpIjogImFuIGFjY2VudGVkIGtleSBzb3J0cyBhZnRlciBldmVyeSBBU0NJSSBrZXkiLAogICAgIiI6ICJ0aGUgZW1wdHkga2V5IHNvcnRzIGZpcnN0IiwKICAgICJaIjogInVwcGVyY2FzZSBaIHNvcnRzIGJlZm9yZSBsb3dlcmNhc2UgYSIsCiAgICAiYSI6ICJsb3dlcmNhc2UgYSIKICB9LAogICJjb21wb25lbnRzIjogWwogICAgewogICAgICAibmFtZSI6ICJUb3J0dXJlVmlldyIsCiAgICAgICJmaWxlIjogIlTDtnJ0dXJlLmNzIiwKICAgICAgInN1YnNjcmlwdGlvbnMiOiBbCiAgICAgICAgeyJldmVudCI6ICJidXMuU3Vic2NyaWJlIiwgImhhbmRsZXIiOiAiT27mvKIiLCAibGluZSI6IDcsICJyZWxlYXNlZCI6IGZhbHNlLCAicmVzb3VyY2UiOiAic3Vic2NyaWJlIn0KICAgICAgXQogICAgfQogIF0KfQo=" }, "canonical": { "algorithm": "sha256", - "digest": "239383fae8175fd42dc773922af7e3dcf26c2148f99a365f8e4404db58224edd", + "digest": "abe4b1afa5a4f287401f9b50c1dec00c57588ab6baabc77968f3f1af232b49f2", "bytes": 834 }, "document": { - "ownir_version": 0, + "ownir_version": 1, "module": "Tortureé漢字😀", "canonical_probe": { "i64_max": 9223372036854775807, @@ -64,19 +64,19 @@ "id": "python-ownlang", "consumed": { "algorithm": "sha256", - "digest": "93ef10f5f3541d1abc5fb75a4ffe74c26a764484b7505ab9039ab151e3883e99", + "digest": "a1c896ce83130584dcbb113c751547e4b488fae6798b00fcc919c8a1cdd8a170", "bytes": 1049 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "Tortureé漢字😀", "resources": [ { @@ -206,7 +206,7 @@ "document": { "degraded": null, "module": "Tortureé漢字😀", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -247,7 +247,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "f9cd8d129d9b8cb698602b840fe50bd8313891ed814b96a20f97fce78b50632a", + "digest": "78ece332c38aa64e10136c8a809552dc47aafaa5bede557d020be4090fe689da", "bytes": 822 } } @@ -257,19 +257,19 @@ "id": "rust-own-bridge", "consumed": { "algorithm": "sha256", - "digest": "93ef10f5f3541d1abc5fb75a4ffe74c26a764484b7505ab9039ab151e3883e99", + "digest": "a1c896ce83130584dcbb113c751547e4b488fae6798b00fcc919c8a1cdd8a170", "bytes": 1049 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "Tortureé漢字😀", "resources": [ { @@ -399,7 +399,7 @@ "document": { "degraded": null, "module": "Tortureé漢字😀", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -440,7 +440,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "f9cd8d129d9b8cb698602b840fe50bd8313891ed814b96a20f97fce78b50632a", + "digest": "78ece332c38aa64e10136c8a809552dc47aafaa5bede557d020be4090fe689da", "bytes": 822 } } diff --git a/tests/fixtures/repro/canonical_torture.trace.json b/tests/fixtures/repro/canonical_torture.trace.json index 5ca0d22f..f12e3106 100644 --- a/tests/fixtures/repro/canonical_torture.trace.json +++ b/tests/fixtures/repro/canonical_torture.trace.json @@ -7,7 +7,7 @@ "engine": "python-ownlang", "input": { "algorithm": "sha256", - "digest": "239383fae8175fd42dc773922af7e3dcf26c2148f99a365f8e4404db58224edd", + "digest": "abe4b1afa5a4f287401f9b50c1dec00c57588ab6baabc77968f3f1af232b49f2", "bytes": 834 }, "layers": [ @@ -21,7 +21,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -179,7 +179,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", @@ -227,7 +227,7 @@ "engine": "rust-own-bridge", "input": { "algorithm": "sha256", - "digest": "239383fae8175fd42dc773922af7e3dcf26c2148f99a365f8e4404db58224edd", + "digest": "abe4b1afa5a4f287401f9b50c1dec00c57588ab6baabc77968f3f1af232b49f2", "bytes": 834 }, "layers": [ @@ -241,7 +241,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -399,7 +399,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", diff --git a/tests/fixtures/repro/di.repro.json b/tests/fixtures/repro/di.repro.json index 434cb96c..518aa99d 100644 --- a/tests/fixtures/repro/di.repro.json +++ b/tests/fixtures/repro/di.repro.json @@ -1,20 +1,20 @@ { "repro_version": 3, "input": { - "ownir_version": 0, + "ownir_version": 1, "raw": { "algorithm": "sha256", - "digest": "dd16be56d7ba44e9c7720a2a05fc110ed7b6ed0e439825dde28b623a11bcd6c0", + "digest": "0d87c5f755986f17d7e7a4e7f5193030113ecd8549049c98b3108696fcf036da", "bytes": 884, - "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMCwKICAibW9kdWxlIjogIkRpRGVtbyIsCiAgImNvbXBvbmVudHMiOiBbXSwKICAic2VydmljZXMiOiBbCiAgICB7Im5hbWUiOiAiRW1haWxTZW5kZXIiLCAibGlmZXRpbWUiOiAic2luZ2xldG9uIiwgImZpbGUiOiAiU3RhcnR1cC5jcyIsICJsaW5lIjogMTIsICJkZXBzIjogWyJBcHBEYkNvbnRleHQiXSwgImN0b3JfZmlsZSI6ICJFbWFpbFNlbmRlci5jcyIsICJjdG9yX2xpbmUiOiA1LCAiY3Rvcl90eXBlIjogIkVtYWlsU2VuZGVyIn0sCiAgICB7Im5hbWUiOiAiQXBwRGJDb250ZXh0IiwgImxpZmV0aW1lIjogInNjb3BlZCIsICJmaWxlIjogIlN0YXJ0dXAuY3MiLCAibGluZSI6IDEzLCAiZGVwcyI6IFtdfSwKICAgIHsibmFtZSI6ICJDbG9jayIsICJsaWZldGltZSI6ICJzaW5nbGV0b24iLCAiZmlsZSI6ICJTdGFydHVwLmNzIiwgImxpbmUiOiAxNCwgImRlcHMiOiBbXX0sCiAgICB7Im5hbWUiOiAiUmVwb3J0U2VydmljZSIsICJsaWZldGltZSI6ICJzaW5nbGV0b24iLCAiZmlsZSI6ICJTdGFydHVwLmNzIiwgImxpbmUiOiAxNSwgImRlcHMiOiBbIlVuaXRPZldvcmsiXSwgImN0b3JfZmlsZSI6ICJSZXBvcnRTZXJ2aWNlLmNzIiwgImN0b3JfbGluZSI6IDcsICJjdG9yX3R5cGUiOiAiUmVwb3J0U2VydmljZSJ9LAogICAgeyJuYW1lIjogIlVuaXRPZldvcmsiLCAibGlmZXRpbWUiOiAidHJhbnNpZW50IiwgImZpbGUiOiAiU3RhcnR1cC5jcyIsICJsaW5lIjogMTYsICJkZXBzIjogWyJBcHBEYkNvbnRleHQiXX0sCiAgICB7Im5hbWUiOiAiUmVxdWVzdExvZyIsICJsaWZldGltZSI6ICJzY29wZWQiLCAiZmlsZSI6ICJTdGFydHVwLmNzIiwgImxpbmUiOiAxNywgImRlcHMiOiBbIkFwcERiQ29udGV4dCJdfQogIF0KfQo=" + "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMSwKICAibW9kdWxlIjogIkRpRGVtbyIsCiAgImNvbXBvbmVudHMiOiBbXSwKICAic2VydmljZXMiOiBbCiAgICB7Im5hbWUiOiAiRW1haWxTZW5kZXIiLCAibGlmZXRpbWUiOiAic2luZ2xldG9uIiwgImZpbGUiOiAiU3RhcnR1cC5jcyIsICJsaW5lIjogMTIsICJkZXBzIjogWyJBcHBEYkNvbnRleHQiXSwgImN0b3JfZmlsZSI6ICJFbWFpbFNlbmRlci5jcyIsICJjdG9yX2xpbmUiOiA1LCAiY3Rvcl90eXBlIjogIkVtYWlsU2VuZGVyIn0sCiAgICB7Im5hbWUiOiAiQXBwRGJDb250ZXh0IiwgImxpZmV0aW1lIjogInNjb3BlZCIsICJmaWxlIjogIlN0YXJ0dXAuY3MiLCAibGluZSI6IDEzLCAiZGVwcyI6IFtdfSwKICAgIHsibmFtZSI6ICJDbG9jayIsICJsaWZldGltZSI6ICJzaW5nbGV0b24iLCAiZmlsZSI6ICJTdGFydHVwLmNzIiwgImxpbmUiOiAxNCwgImRlcHMiOiBbXX0sCiAgICB7Im5hbWUiOiAiUmVwb3J0U2VydmljZSIsICJsaWZldGltZSI6ICJzaW5nbGV0b24iLCAiZmlsZSI6ICJTdGFydHVwLmNzIiwgImxpbmUiOiAxNSwgImRlcHMiOiBbIlVuaXRPZldvcmsiXSwgImN0b3JfZmlsZSI6ICJSZXBvcnRTZXJ2aWNlLmNzIiwgImN0b3JfbGluZSI6IDcsICJjdG9yX3R5cGUiOiAiUmVwb3J0U2VydmljZSJ9LAogICAgeyJuYW1lIjogIlVuaXRPZldvcmsiLCAibGlmZXRpbWUiOiAidHJhbnNpZW50IiwgImZpbGUiOiAiU3RhcnR1cC5jcyIsICJsaW5lIjogMTYsICJkZXBzIjogWyJBcHBEYkNvbnRleHQiXX0sCiAgICB7Im5hbWUiOiAiUmVxdWVzdExvZyIsICJsaWZldGltZSI6ICJzY29wZWQiLCAiZmlsZSI6ICJTdGFydHVwLmNzIiwgImxpbmUiOiAxNywgImRlcHMiOiBbIkFwcERiQ29udGV4dCJdfQogIF0KfQo=" }, "canonical": { "algorithm": "sha256", - "digest": "1dd40fe8691e32cbab0252b749d17fecfc905382907a1ea48b2a9eae8ae9d929", + "digest": "f032d100186d884fc1778e501715e23675460cef64271ddd00525e42f2a88d85", "bytes": 767 }, "document": { - "ownir_version": 0, + "ownir_version": 1, "module": "DiDemo", "components": [], "services": [ @@ -82,19 +82,19 @@ "id": "python-ownlang", "consumed": { "algorithm": "sha256", - "digest": "dd16be56d7ba44e9c7720a2a05fc110ed7b6ed0e439825dde28b623a11bcd6c0", + "digest": "0d87c5f755986f17d7e7a4e7f5193030113ecd8549049c98b3108696fcf036da", "bytes": 884 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "DiDemo", "resources": [ { @@ -198,7 +198,7 @@ "document": { "degraded": null, "module": "DiDemo", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -294,7 +294,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "e231066acedcbd88fff8bb10f2fa7e658f40d09c2a615f0eeb2331f1ed27dd93", + "digest": "1dc9b97ea182ba82d79f49123f5e491306bc99e1e5fd33349c1fd369986655d8", "bytes": 2564 } } @@ -304,19 +304,19 @@ "id": "rust-own-bridge", "consumed": { "algorithm": "sha256", - "digest": "dd16be56d7ba44e9c7720a2a05fc110ed7b6ed0e439825dde28b623a11bcd6c0", + "digest": "0d87c5f755986f17d7e7a4e7f5193030113ecd8549049c98b3108696fcf036da", "bytes": 884 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "DiDemo", "resources": [ { @@ -420,7 +420,7 @@ "document": { "degraded": null, "module": "DiDemo", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -516,7 +516,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "e231066acedcbd88fff8bb10f2fa7e658f40d09c2a615f0eeb2331f1ed27dd93", + "digest": "1dc9b97ea182ba82d79f49123f5e491306bc99e1e5fd33349c1fd369986655d8", "bytes": 2564 } } diff --git a/tests/fixtures/repro/di.trace.json b/tests/fixtures/repro/di.trace.json index 6e00e31f..ea677c81 100644 --- a/tests/fixtures/repro/di.trace.json +++ b/tests/fixtures/repro/di.trace.json @@ -7,7 +7,7 @@ "engine": "python-ownlang", "input": { "algorithm": "sha256", - "digest": "1dd40fe8691e32cbab0252b749d17fecfc905382907a1ea48b2a9eae8ae9d929", + "digest": "f032d100186d884fc1778e501715e23675460cef64271ddd00525e42f2a88d85", "bytes": 767 }, "layers": [ @@ -21,7 +21,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -147,7 +147,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", @@ -253,7 +253,7 @@ "engine": "rust-own-bridge", "input": { "algorithm": "sha256", - "digest": "1dd40fe8691e32cbab0252b749d17fecfc905382907a1ea48b2a9eae8ae9d929", + "digest": "f032d100186d884fc1778e501715e23675460cef64271ddd00525e42f2a88d85", "bytes": 767 }, "layers": [ @@ -267,7 +267,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -393,7 +393,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", diff --git a/tests/fixtures/repro/digests.json b/tests/fixtures/repro/digests.json index 5f13c373..e77ea8c6 100644 --- a/tests/fixtures/repro/digests.json +++ b/tests/fixtures/repro/digests.json @@ -6,589 +6,811 @@ { "case": "alias_join_cases", "corpus": "lowered", - "digest": "f3ac08b8799096024dc96f9c740cdeb8838102c485440a4338da02bc1cf11a5f", + "digest": "05a2e24920ee8d36006b4dba4b6a10ba398ef3f37a224b9770bb8f9d13e37f54", "bytes": 377 }, { "case": "canonical_key_order", "corpus": "repro", - "digest": "e48d4893337f2a3180388946f64524119451eecf4b55c84b9d1cdd6ede17e0cb", + "digest": "84301b7dadc06e52b558b3da71322a8f1d863d45694e9ebe7647c9dce5ebb049", "bytes": 283 }, { "case": "canonical_minimal", "corpus": "repro", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 }, { "case": "canonical_torture", "corpus": "repro", - "digest": "239383fae8175fd42dc773922af7e3dcf26c2148f99a365f8e4404db58224edd", + "digest": "abe4b1afa5a4f287401f9b50c1dec00c57588ab6baabc77968f3f1af232b49f2", "bytes": 834 }, { "case": "capture", "corpus": "ownir", - "digest": "a3e8dbaa4f3bb7acd77fcd870ef8bec725e705197c219bcc02638e6acc65daf0", + "digest": "a34f862ff561b0649c879d2bca226bf21cc107f4601c3720c45f3b419ed494b6", "bytes": 696 }, { "case": "contract_inference", "corpus": "ownir", - "digest": "4441c9b0bc8bdcf1a16c9c157214988a80c8fe962dd32c560cd41c596e58c109", + "digest": "96f4fc97febff178709e22be42e6ba4f54c00fa018117605499deeb1e4765fdc", "bytes": 785 }, { "case": "di", "corpus": "ownir", - "digest": "1dd40fe8691e32cbab0252b749d17fecfc905382907a1ea48b2a9eae8ae9d929", + "digest": "f032d100186d884fc1778e501715e23675460cef64271ddd00525e42f2a88d85", "bytes": 767 }, { "case": "di_capture", "corpus": "ownir", - "digest": "acd247ddd2d7ffdd4ebaae20eba8bdb43408ae3ef0914e2a9bb5e9481bb67005", + "digest": "718ba4d6bfd8a4a450a3f22fe6e2fc3774819681605be87009af54752ea7f03b", "bytes": 684 }, { "case": "disposable", "corpus": "ownir", - "digest": "9a3942afd5b77c8930e43d9b931ca84dc7722b801907775a41381aab6c7e8b34", + "digest": "c6a27c0be85a5f41f8f0d7527d6ddb3eb14138bb6993f6759061d97285636fed", "bytes": 430 }, { "case": "flow_column_anchors", "corpus": "ownir", - "digest": "a31d297c8484e461a8dd2ba9d6ddc7d28c5a5b023ad110cc3c116d2dd2e9d58e", + "digest": "fd7aa188032f84027570465837631a31ebffbe33e98f9672640f4978dddf75c8", "bytes": 2288 }, { "case": "flow_finally_switch", "corpus": "ownir", - "digest": "455d7bb9e579975b883274656ddef2f4b2c5283245053255f0cb2ebbc17043d5", + "digest": "5091a185081323ef890cab84c7779443e317c7960b1b6cd38c2ac636a7445ea8", "bytes": 1675 }, { "case": "flow_kill_on_rebind", "corpus": "lowered", - "digest": "495304a931624ee28c79d8b2a6910d06b53b193165afd7ac965419ee99c00335", + "digest": "78534e41a0e95815c779cf7b6ddf8ce292580dc16688d0274ca2f9de2ad916f1", "bytes": 229 }, { "case": "flow_leak_on_else", "corpus": "ownir", - "digest": "d0ebae6b7d708fb28904932181eba56e8c6edd91488a3c7f0650ff1eb91f517f", + "digest": "e5f0bf314e3e335b7444565303b11ebacee6def8e4a1a13d9bb75548e5642645", "bytes": 268 }, { "case": "flow_leak_two_exits", "corpus": "ownir", - "digest": "da3952837918230ce159c9d320b3820854a4fbbcdd013b8a3e0a130c86f092b4", + "digest": "3e1bfac01192596271a025941ff40216743b1be6d010681bc20bd66ebb64f834", "bytes": 315 }, { "case": "flow_nested_throw", "corpus": "ownir", - "digest": "f009d8950f48fd37302564747c364d58f49a64430f2c37978bfb6c2c2fd1460b", + "digest": "98eb6b1074373135d1816a92864c8f6aff0c58679af25e3b2bd2d849129bfd3b", "bytes": 1667 }, { "case": "flow_pool_partial", "corpus": "ownir", - "digest": "59c43eda99a86ee30cb2a4c7781b3044142844c62f8042dbf100233ac8e0c8e6", + "digest": "bf9e24dbce660556fb5028ec2927eccb288267beecab4c1f47065bc02bd27715", "bytes": 1426 }, { "case": "flow_unmapped_refs", "corpus": "lowered", - "digest": "8f19e263ab72914d43f5ae18da36d4196f9142f5b97810374074740eec3328f9", + "digest": "8cab4874d2309bbf2719a3a9bf14bcf9ef0ae91ca254811e3ab90c5986609f12", "bytes": 245 }, { "case": "flow_while", "corpus": "ownir", - "digest": "2c67be045fad2a800d27121117af04b1f58ac42fe69ef3f63c1fabeaab5f0b94", + "digest": "eea40449080bab8241995977bd6bfcb16e12976b576f2b7f7101aab68b3f29a3", "bytes": 255 }, { "case": "fn_params_ordering", "corpus": "lowered", - "digest": "5a639b7c8ac76e5d87cb33b6196c8baf67b4f055266367123207ad924d42d260", + "digest": "bb0f0fa3ff63fec2871244f4ff21c42b3d9bb3a7999ebd37e331e3340cd9c015", "bytes": 479 }, { "case": "handles_global_counters", "corpus": "lowered", - "digest": "cd5112cae07211e5c99f4b4d30c73612d61d8e1a7298b2396d5242253635965b", + "digest": "6822d42983b9130a9bb75265e31fc579ac3786a9f4a9afb11a0d6c0508a8558b", "bytes": 500 }, { "case": "handles_null_metadata", "corpus": "lowered", - "digest": "fa2b63f3f16a0ab9a2d73b43dd666f79ea8690c3eb2754de071d5ee90d83826b", + "digest": "924d57ab06917000b7ba05b04fedaddbdcc82ea586b0ae4c1836c59bf391f5e1", "bytes": 304 }, { "case": "handoff_contract", "corpus": "ownir", - "digest": "a85e62876839d352a5d31d6cab2a23be7bd06cd40793a13fddb40816e06f27a8", + "digest": "aea2a6cb9b81cd4efd70f577e2e163187bf461e883b29bb781e5ab1b17617319", "bytes": 658 }, { "case": "hoist_neg_early_return", "corpus": "lowered", - "digest": "e2ad1e5c33eb5cdfe18b057c059d59e1097217e58b4a70b708794e4f1078c9a1", + "digest": "00b86b9298b97dbc78b6152fb1865f36800a8ff2e672da7fd4b7a8e8a1fc4588", "bytes": 226 }, { "case": "hoist_neg_nested_depth", "corpus": "lowered", - "digest": "d588e0d8103c327b2fad4418906b3cfe38eaf97769b17963274bf3c85b75e2b1", + "digest": "f0baa68324567f1543eccdf08ba4ec91a20059311625d9bef6b8cf2bb1c7a055", "bytes": 242 }, { "case": "hoist_neg_while_body", "corpus": "lowered", - "digest": "d2ae2309b105c985d6eeda13a2eb53b72bcd56fe895ac6919023ecdca72ea1e4", + "digest": "8f4807d8dd0a671659335446601ca8ec5db2c7e21bfd0c50164393f5e0478b0a", "bytes": 195 }, { "case": "hoist_pool_kind", "corpus": "lowered", - "digest": "578e1892bfa7932d76846c4216e0f2e749782e43afcf3d2dddabfbfbd28f4cb1", + "digest": "ff611d1b198ad3febbfab220c8c2b64fa5542cdb599c20c40aeb17701b38bb1b", "bytes": 271 }, { "case": "hoist_positive_release", "corpus": "lowered", - "digest": "cf6479caae0c2bb8122ceffc3056de9ac7bc58da78f33dd440e4a9b4c313bdb2", + "digest": "1d04e37ca5ab97b1bd0baa47a4d41fd4176d142204ebc046958fed9e9a9d07bb", "bytes": 233 }, { "case": "hoist_positive_use_only", "corpus": "lowered", - "digest": "0d02a23d958c3444865c17fd50e4b1cb29ea89bb3a1f382546bbb4d26d512038", + "digest": "155e1c1323bc0dfd414b41ef3cb1e4df01a92b72ec52e8d3af3f020aa409f206", "bytes": 197 }, { "case": "lines_preserved", "corpus": "lowered", - "digest": "48b075d0a88ecf2d5d6d96f310e9a94f31e571fc7d70b2ea6f52723d459d319b", + "digest": "6c49de6c378938a2d3967a37ec3a8a5a584042f3d1c4700666157e1c69264bc6", "bytes": 383 }, { "case": "local_disposable", "corpus": "ownir", - "digest": "e5472c04b60a3916349df8fbb9e36d804be4a2548518de2f539c67e7fe2f589b", + "digest": "128e51ecd78928f091c18bdf8730146cbaa4c020178b3d543df58874131e831f", "bytes": 319 }, { "case": "mos_call_channel_overload_sig", "corpus": "lowered", - "digest": "1ae342b45f6fadead130794925ddc7d0d96788dadf2c1b6cc531888d566ce3b6", + "digest": "88f8295d4e8c8627686e12a7a854233da12104755eca10e78932cf7249eb7b83", "bytes": 601 }, { "case": "mos_call_direct_consume", "corpus": "lowered", - "digest": "d6657c687e33de4fd69be1e1b61c3bf37dc09dd75d1f9b49a3d96e936f8bbf60", + "digest": "e262529ab03eaee2d9720e2dfcfbac2291785bbfd9c5e937870fe87d78b87f63", "bytes": 269 }, { "case": "mos_call_unknown_drop", "corpus": "lowered", - "digest": "bc99586d256861fcf585c68b53b5899a52fbee1c42deea91ce53ccfc1c58c5bd", + "digest": "03a7cf5bf7a61004779897db9107147602d36dd6a8c0ccc92dc7f6eb00ed72ad", "bytes": 245 }, { "case": "mos_fresh_mint", "corpus": "lowered", - "digest": "caef5f8298cf98addbbece7a24487b831dbd5bc806ac0b4225391486c9840a7e", + "digest": "76651f7fb27bf41fdf0711e5fa41caa6a7245f4410774d6254c50833cb13bb4d", "bytes": 409 }, { "case": "mos_killsite_toplevel", "corpus": "lowered", - "digest": "b54f9fa40ee79929fe93cf51bdf1bb754b38fae729b53b8717684026fb29bec2", + "digest": "7b082c4968589063a94944caaec515e9935098510c239673d85297040392f8b2", "bytes": 403 }, { "case": "mos_untrack_inbranch", "corpus": "lowered", - "digest": "603a39cdf16770f6bdb843b5b6af1e174d018b0842cab149cf245335587e3fc3", + "digest": "065f5b2d64412dbf5d7a4aa88cc3e56d3bcdec63444ae349d316299a88ec3023", "bytes": 410 }, { "case": "mosdump_degraded_duplicate_key", "corpus": "summaries", - "digest": "92f052ccef71d87b1b80ff9e9bcd9c74ec9db30b7ad34c883af0096a83088fdd", + "digest": "5d67d6918c18d93df76a29b2d58f50202444d3b95e2a7de6cca64541bb71e06d", "bytes": 376 }, { "case": "mosdump_explicit_effects", "corpus": "summaries", - "digest": "0c052dc3634aed1263f4ec1eff3019d27052617390dcc0f18fc61bc5982003a6", + "digest": "6f1e70cf978838a74c41b26683af4baa0f780a6147888d2ef33ccc9725f10def", "bytes": 461 }, { "case": "mosdump_nonascii_escaping", "corpus": "summaries", - "digest": "75932fdf06e0fbbca45369c8d86672f0c252415695e5c41224f9b5c130b82037", + "digest": "2514d4aef9c1b78656d03bd79afec9aa197f12e6ccea178d1e959e94e4ed5e61", "bytes": 219 }, { "case": "mosdump_overload_merge_tiebreaks", "corpus": "summaries", - "digest": "c84e2e6bfac8830df855205fca738c3a8a14d667cc96a30198f753cf7102059c", + "digest": "ea7627b5b854c959f48cbe7606c728c3a55b199869e0cc33941279067c8f4042", "bytes": 403 }, { "case": "mosdump_partial_and_guarded", "corpus": "summaries", - "digest": "fe5b033ed5e348728bcb9ae6223ffaddc72d1388bffe581275ac73117f6c0fc6", + "digest": "83b3f724c3dfa41e0de104d9d5b68bf2cec8886ec8b5cef4b5e0103e5c825ff2", "bytes": 525 }, { "case": "mosdump_return_chains", "corpus": "summaries", - "digest": "233213ac3f8dd75194e25a66a5d784e9622be282d88f1fe4a546d9b8f66dd36d", + "digest": "1ca46385a7b194fa197ae668a9d093036a338db13dde3fc9649c232db188e200", "bytes": 894 }, { "case": "mosdump_scc_cycles", "corpus": "summaries", - "digest": "15f6a6b43a638b766baef273e5fcaa086f57ab469c44664edd15eea3a6c7fc0e", + "digest": "fbdc666a60dcc512f968caa772efc366b53eae7b88f2f34eb7f8503880ba5c7b", "bytes": 655 }, { "case": "mosdump_sig_vocabulary", "corpus": "summaries", - "digest": "b9683de7109d84886fe703cc9a32c63e27149a976d0447121d49cbc13e81224f", + "digest": "5803620f7c3c4099d4fa460faccee4f2632bf551be95909dfbf2a54014e6be13", "bytes": 514 }, { "case": "mosdump_sink_channels", "corpus": "summaries", - "digest": "a71c3f96df86155c36ecc1b1bf7450636b19d18cb9395f56bfe1cad4c967478d", + "digest": "b42ffde2dd251354d30c072db40e471c0783c13b7e11a5809d52424052eefb88", "bytes": 440 }, { "case": "pool", "corpus": "ownir", - "digest": "79cb88310fefc2641559688cf9851f6199927c5ee4eed43ada7711f2547667ae", + "digest": "8e18fd84acdaa4383c9c72d57ec184d448881cd2ae447284d4d1bafdb01e1399", "bytes": 241 }, { "case": "protocol_isloaded_clean", "corpus": "ownir", - "digest": "cc7539104ac5357e0ed76599177da95f8ea271751e64fa94adff49a399f0a95f", + "digest": "31025ef88e97214db901824cca982f64f98cc91c63b2029671347f4ee65eaa87", "bytes": 1193 }, { "case": "protocol_isloaded_violation", "corpus": "ownir", - "digest": "f821b58b0da7e5ecb94798d1868db377876d945242a4e4d013d6499d7af7ee13", + "digest": "1025d2dadba8f7f00604e5d40a264d286a0d5cf62a243b16578fd00b3430d813", "bytes": 1217 }, { "case": "routing_r1_unresolved", "corpus": "lowered", - "digest": "d1b7da34ce0ad0069211e3fa05d8881ab8ed1da2a5d5c5c4d472a6f14d9633a0", + "digest": "d1d165c2e00d1d312a8afa683917ac7805c48ada9e3a8680660a267012e4b5c5", "bytes": 171 }, { "case": "routing_r2_subscribe_self", "corpus": "lowered", - "digest": "25f7d643e602b10195cfe7358b2715f9c511bb8feb5049265fde6c6c826e6bf8", + "digest": "4c675495f03c095a7ab785d90e625d6aa1781a52cfbd0fd68ad11fcd60e749f1", "bytes": 208 }, { "case": "routing_r3_capture_static", "corpus": "lowered", - "digest": "41cf768f49af1bf0179db32a31870f0e7a2bcdc6e0f732627664935a576becc5", + "digest": "7ef0b85183514bbf77a9df2694bb48320edc1f78da5bd3788e3336696685eee0", "bytes": 421 }, { "case": "routing_r4_returned_fresh", "corpus": "lowered", - "digest": "6b4b0ef2a9a66fe9917a78fa64adcb120e152eabb8164dd2c1aa54bffbcd87e1", + "digest": "b60e041a1c4d5a9ba7c614e75d37ef10998c09ae3ad48fb8d63ce7b01a1e6c38", "bytes": 358 }, { "case": "routing_r5_di_capture", "corpus": "lowered", - "digest": "da3a45a5961c42e43b75f486c081a69bb129c00d4a60e8d7210877467d41e125", + "digest": "ad071a1320ce2d33dc2b24e3fa3b87ad97a42bac3293a407e249b5a999c60258", "bytes": 511 }, { "case": "routing_r6_token_kinds", "corpus": "lowered", - "digest": "4ada77267b035f2148303ed7174761f04d96bb36fa5bd28f4ffd7c34b0bafcf0", + "digest": "b9f462df47c6a5940281e06d5baa5de874587d737b1f818fc0fb6e727f7104e1", "bytes": 675 }, { "case": "sample", "corpus": "ownir", - "digest": "fff7156e1bb4e3de0e54b88042a8ed3f9fac9be535c339d4699f916621484db1", + "digest": "74547157cb2ff329176205cd8e9227b9564d8f57c2f7ff0de99e07250e5db955", "bytes": 375 }, { "case": "subscribe", "corpus": "ownir", - "digest": "c883235a21110fc4f4271b40453e6f786fbe3efdea32cecab065e774f174ec31", + "digest": "1fd329e0c89dc4934b2729c3abf8bad890ff989606820e1cf69c2aae0cd10982", "bytes": 216 }, { "case": "timer", "corpus": "ownir", - "digest": "62804547e0b08ed25035805e4a7ecbc69e46fdde977d6ea12683c6165fac1044", + "digest": "1689f54850d2f09a8ed66d1d24ca52e1bb8cef6bf62e648854b9837e58a3e029", "bytes": 379 }, { "case": "tolerant_unknown_kind", "corpus": "lowered", - "digest": "b33cea96ebe7256e92543b8fc5c131a9b0be52157ee73300ca00a1f092d958d9", + "digest": "22a44716bf2ed49acdb8e373c516b4c86429f0db1bf5f4d8f503c02af384cbe4", "bytes": 150 }, + { + "case": "typestate_cs_a1_alias_of_entity", + "corpus": "lowered", + "digest": "82f4a95de310c4646b7517d3b57bd6edfe3bc9c1c58bf885e501e097fac1e58f", + "bytes": 928 + }, + { + "case": "typestate_cs_a2_entity_through_indexer", + "corpus": "lowered", + "digest": "e7b606b6462ae08b09142b0089c0b7120f14ff0a83d0f7ff288d59a9ac28e79f", + "bytes": 942 + }, + { + "case": "typestate_cs_g6_capability_not_spent", + "corpus": "lowered", + "digest": "ef4c3465e8a2b525c362e46a18a072f1cb64310a8ea13b357bbe5cfbd496e700", + "bytes": 521 + }, + { + "case": "typestate_cs_l10_region_inside_try", + "corpus": "lowered", + "digest": "8e5cbb0fec8d12d14589d77f7432bc73f2f114357d7bee82fcc61bafc69d0ab8", + "bytes": 897 + }, + { + "case": "typestate_cs_l11_region_beside_disposable", + "corpus": "lowered", + "digest": "24a25a98be12223eb1c46244fab02489fa0279f1b852bb55cec8c0bd4babe639", + "bytes": 1250 + }, + { + "case": "typestate_cs_l12_region_beside_leaked_disposable", + "corpus": "lowered", + "digest": "1f854d323f991c27c697c58f96768bf6d13d8d59d01f92f4d716745da794c467", + "bytes": 1237 + }, + { + "case": "typestate_cs_l13_values_captured_into_region", + "corpus": "lowered", + "digest": "3c0912f100d55c783873bb54361d9a6e1a369df110462408da4bff5d1b34ec69", + "bytes": 1107 + }, + { + "case": "typestate_cs_l1_stale_capability", + "corpus": "lowered", + "digest": "ab9295ee55b27c9cf3009f0714f8b27170bba776a93a3c56575c9e7b6f893888", + "bytes": 1695 + }, + { + "case": "typestate_cs_l2_copied_capability", + "corpus": "lowered", + "digest": "064172fda48442fec636584b1642d94a382aa5410b8f67de9f6fe2c5164c58cb", + "bytes": 1045 + }, + { + "case": "typestate_cs_l3_raw_mutation", + "corpus": "lowered", + "digest": "15c824bb3c749f78a1c14331f5092977479e15be6ffdfce719bcc8d70ba53498", + "bytes": 923 + }, + { + "case": "typestate_cs_l4_raw_escape", + "corpus": "lowered", + "digest": "c342366a634db5f0ef31cc3edf35860c7e36b78b59643550961b617c21c5718f", + "bytes": 919 + }, + { + "case": "typestate_cs_l5_normal_transition", + "corpus": "lowered", + "digest": "fc645e613bce7fd4bc85951c2b2cd5ac86e727c9eb422810f08ca9ebaa6628c8", + "bytes": 896 + }, + { + "case": "typestate_cs_l6_two_transitions", + "corpus": "lowered", + "digest": "96bfa97439d1f8ee4dc855f029c3c5a57088bd97492915df4b27f602fe300c49", + "bytes": 1318 + }, + { + "case": "typestate_cs_l6b_chained_transitions", + "corpus": "lowered", + "digest": "8cc7bf2bc99e27de18416db155c5949433707b0b795f546982386a6441e7ce12", + "bytes": 1316 + }, + { + "case": "typestate_cs_l7_read_after_region", + "corpus": "lowered", + "digest": "19c9148385ee6ea47086a927c97323eb646e47cd94271d7020198c56409f3daf", + "bytes": 895 + }, + { + "case": "typestate_cs_l8_branch_inside_region", + "corpus": "lowered", + "digest": "4ecb5a073972e2ded095ac1eed365c2db4f1794d4ed1eb331657e25e04feba88", + "bytes": 1093 + }, + { + "case": "typestate_cs_l9_opaque_calls_around_region", + "corpus": "lowered", + "digest": "adfc9ac66558d84d79360ea4654c8ac4ca3cc5aa6f1f21eb556e40f9d526fdcf", + "bytes": 912 + }, + { + "case": "typestate_cs_x1_nested_region_same_entity", + "corpus": "lowered", + "digest": "28fbaa59b3087ed3261c36f5e8e17e1efceddccabc14f2956c2e31eec273d93d", + "bytes": 1407 + }, + { + "case": "typestate_ef_orderbackend", + "corpus": "lowered", + "digest": "64fcc2e1bfb81147e463666c77fdecdb9b606527cbdc90fe5f6a0342bdb9a2a5", + "bytes": 3796 + }, + { + "case": "typestate_g6_capability_not_spent", + "corpus": "lowered", + "digest": "6fc85ee6d17cf09a6614f23a3c566b6c7db1e897df4ad1fa7738557197710c1b", + "bytes": 1413 + }, + { + "case": "typestate_l1_stale_capability", + "corpus": "lowered", + "digest": "930619b44065043eb7a91379f7f2cb84f21f467ee7deae7f1683b392ec74c12b", + "bytes": 1536 + }, + { + "case": "typestate_l2_copied_capability", + "corpus": "lowered", + "digest": "5e0436c98d213fddad7c135a2ba0a9fbcd0e71eef830a13f325223f57d565153", + "bytes": 1585 + }, + { + "case": "typestate_l3_raw_entity_mutation", + "corpus": "lowered", + "digest": "5f2a4467308b602eab4545156f12dc725072fc23c4b79414b8cb8107a492f86d", + "bytes": 1534 + }, + { + "case": "typestate_l3b_second_region", + "corpus": "lowered", + "digest": "3195d8ce58c7a6eb206a6ee1244bab49e5118e68fff0261f3902df3f19787038", + "bytes": 1601 + }, + { + "case": "typestate_l4_raw_entity_unknown_call", + "corpus": "lowered", + "digest": "9e3a7a14fe8deefe265e929d7c51f0d9cd4da2c71e1c065444c0c3ff665e42d6", + "bytes": 1530 + }, + { + "case": "typestate_l4b_raw_entity_shared_call", + "corpus": "lowered", + "digest": "08d2b6968d802c685faeca531f2af0d4d6ad213662f91073144c0e17aed543e8", + "bytes": 1527 + }, + { + "case": "typestate_l4c_raw_entity_consumed", + "corpus": "lowered", + "digest": "4e6a05dbdfb7a2450d3ebc93eb448b6acae0372ade2d31d21c35a88b8eac2d1f", + "bytes": 1487 + }, + { + "case": "typestate_l4d_token_unknown_call", + "corpus": "lowered", + "digest": "c48148d384e05b06c5070e3c0bc89186740bf27588b069894ed4cc865af519ae", + "bytes": 1526 + }, + { + "case": "typestate_l5_normal_transition", + "corpus": "lowered", + "digest": "28b2a5e8152aa574a07f449056d60f06cd48da5e1ff49f50940bd01199fdf383", + "bytes": 1470 + }, + { + "case": "typestate_l5b_two_transitions", + "corpus": "lowered", + "digest": "cc0bb549463056b5eef0aa9a4fd04ffa8d72adb4f4bb32305c09110e061ffe12", + "bytes": 1569 + }, + { + "case": "typestate_l5c_read_after_region", + "corpus": "lowered", + "digest": "52783299f4959c94d4ed5f549f9cd3fbdfe163fa4698807d46d7d9201fdf6652", + "bytes": 1527 + }, + { + "case": "typestate_n1_token_spent_on_other_entity", + "corpus": "lowered", + "digest": "008d1e473674853be2eb7f4ce27047c3a0db3ec4d1344138d38dddfa86cccaa3", + "bytes": 1606 + }, + { + "case": "typestate_n2_two_tokens_one_region", + "corpus": "lowered", + "digest": "7e478c42d5750c276800fa1e5a3599903f01aa492db6633597dbae5a2b587a99", + "bytes": 1581 + }, + { + "case": "typestate_n3_raw_mutation_inferred_effect", + "corpus": "lowered", + "digest": "8195d3b979b6baa824402f891ff95428a4a0f28d2508f14b887b58e7df0dd2e9", + "bytes": 1613 + }, + { + "case": "typestate_n4_token_outlives_region", + "corpus": "lowered", + "digest": "34cd617242d25cbd6649a0351f4934a5bad835b025a62aeb4efe53cb8218c36d", + "bytes": 1538 + }, + { + "case": "typestate_w1_loan_wordings", + "corpus": "lowered", + "digest": "fa2099dab08f4d510f0d3175d62ccad9b704d99bba5da31ac4b1de64847e7eb6", + "bytes": 1712 + }, + { + "case": "typestate_w2_loan_wordings_pooled", + "corpus": "lowered", + "digest": "c112f0ac827146734d2d89ee70a77e9629893ad100122beacb3063a4d2051f34", + "bytes": 1796 + }, { "case": "unitofwork_flow", "corpus": "ownir", - "digest": "2aa3bdb9963c1a74d7559ddf30ecacbf03b8a73f86979454bbe903c2ac956862", + "digest": "f40ec31aeb8c76fdf377cd8f8e9ac7f8b8e345fcf7a2722c30f2732b583de0a4", "bytes": 324 }, { "case": "unresolved", "corpus": "ownir", - "digest": "3475a764d4449355270566bc27f7457535bd465e8294e40519ea8748525c4cbc", + "digest": "ada74fec0dfc74dbaba397622f4b8aa86766d1398b4ae3ea95fd9a5903f67c30", "bytes": 309 }, { "case": "verdict_boundary_effect_line_negative", "corpus": "verdicts", - "digest": "741485197684324305e4424ef715c7bdb0eefc5fff958b9dffa879041603475c", + "digest": "b3385658d5e763c4a3c14ad3f1e338826df85554f072e8f571966a31ce854c00", "bytes": 336 }, { "case": "verdict_boundary_line_above_u32", "corpus": "verdicts", - "digest": "65bf5f41f6af899cfd01e5b8b371d1d765e477d60ed18a537eacad72de0263a1", + "digest": "592b6343827472c8b8a1e9356441f730f940841abec3515ae3361c5be32f71eb", "bytes": 434 }, { "case": "verdict_boundary_line_negative", "corpus": "verdicts", - "digest": "083698faa3f6aba6d3da9ccc86b9dcdb7326f6a3c967ed6e71e250f1f6993cb5", + "digest": "051678520a5b3c8a926ea635db29560b4eb14424d1e3f15691a28aa9c22a4315", "bytes": 541 }, { "case": "verdict_boundary_service_line_negative", "corpus": "verdicts", - "digest": "50b78714f0ef7f3b080cb537dd8820f80a5a17765e15db39e0d5183558142cf1", + "digest": "5fb548d3e58c11a19d8894a6d6406ea1c8469ce7644977d3c0297fb5b43b88c2", "bytes": 480 }, { "case": "verdict_column_sort_order", "corpus": "verdicts", - "digest": "33f0ab59bd9c1dadc09aa5b2c4f110b9540116f9b51a4e4d2e0c0085e9c96169", + "digest": "2c3f97e4065a1f0ff00732ef10dd2238523d14d4e6105ce863b8537123380aba", "bytes": 960 }, { "case": "verdict_dedup_key_members", "corpus": "verdicts", - "digest": "510950daa897efb50b527b16a15165441d8d84f76299d662402935a19e0c2a05", + "digest": "80e51b34954837db6d2e62c6c700732b7f24e09dc01dbadf078d2a1b8afcd075", "bytes": 973 }, { "case": "verdict_dedup_needs_the_message", "corpus": "verdicts", - "digest": "81fc98c8a762811e57040baeb5b02f19fa6e5b58ad1388056b73811da863f685", + "digest": "6062283edcf11bf49a3464872121f0d73daa269b49f979e1fe1b5ab35c0cec52", "bytes": 663 }, { "case": "verdict_degenerate_metadata", "corpus": "verdicts", - "digest": "b068af472cf610e2860561fe4dc7177f61bc170fdfec5e81279413e7f05d9524", + "digest": "31895c280f2ec7cc7072c60b238c0b3346f63b5a3d6acdf066b2a3e0aabcb461", "bytes": 856 }, { "case": "verdict_di004_call_site_anchor", "corpus": "verdicts", - "digest": "09be7af7d79b67f5d79e462b3b9a9a5ac04eb313c1021344fdf16caf081b8aeb", + "digest": "2bab34529d15196ab3aa1a260e84f2f851a74b7b585430da49e19ef616ebd1f7", "bytes": 1410 }, { "case": "verdict_di005_cache_site_anchor", "corpus": "verdicts", - "digest": "ae041b3d718acac511214765f00efaac3ef77b70d2f0296ff7b1abeaf17f6292", + "digest": "9c36e23eecc9f737ce4df8e4437e52c6c5ec856a1914269d67675982eefe4236", "bytes": 1137 }, { "case": "verdict_di_duplicate_sites_last_wins", "corpus": "verdicts", - "digest": "33aff7fd9961eed97afe9bd78b0271d0333ab27734df86bccfee2e892a226fb6", + "digest": "10904137f0d7efdcf79ef99052ae57f10a9cab99d3c9a09e7bcdd9a0566c25e0", "bytes": 1888 }, { "case": "verdict_di_evidence_degradations", "corpus": "verdicts", - "digest": "e56949c13fd63bceb00fb098bbd9ddab806cfbb00bcc9a80b2a2e49f7cf6584a", + "digest": "37a0e719e16d2c256ca3b9f27e0af509156d9a8b0046aa9e1a064842f340498b", "bytes": 851 }, { "case": "verdict_di_graph_families", "corpus": "verdicts", - "digest": "f6f1dd6bc8ac40468e2252b10e6856dc510be0a38ac4c426fb656c25ecffd457", + "digest": "dd691c530acb0667019af74adea6e1fe6076afe5249a30678063b78e249229ce", "bytes": 1077 }, { "case": "verdict_di_tolerant_typed_coercions", "corpus": "verdicts", - "digest": "db35f35c937b9014c3c3ca14c983ad9f0504925b233cbfbb457885fd60e9ddf0", + "digest": "398d4f6271fd938ec4f11682d5cf1e881c7d52bc7475cd0529ced60847c910d1", "bytes": 901 }, { "case": "verdict_domain_tolerant_readers", "corpus": "verdicts", - "digest": "361da587ce14c818d776f824bb563adaa6ff3e315c337bf21f339abf0a503c55", + "digest": "6da7a9a361830a55d2ab2d32eb05290be3fc93f3323efe72a3650ca4b71a4019", "bytes": 1307 }, { "case": "verdict_door_effect_deps_not_strings", "corpus": "verdicts", - "digest": "9ac5b570e4a2956f2d771aa0cc5de5e1a7584683d0feefe453df6da4d5007c7b", + "digest": "b85df11e6d8bd400d38b209009a3517bce2bce543e65fd2faac863dcfd057cf1", "bytes": 622 }, { "case": "verdict_door_service_unknown_lifetime", "corpus": "verdicts", - "digest": "f812c4f014338788c67b3e3f29f035d074d84714f048b0ff8b696b57432a3752", + "digest": "e7820a76b22a9c562e7533d3f429ee16535c85bef592209d4466dcfa9d83f613", "bytes": 541 }, { "case": "verdict_eff001_slice_dropped", "corpus": "verdicts", - "digest": "5cefb40937373c3f12d6fe5c35f1c97851fc9a071556855c4823c369689ec67b", + "digest": "08def69c7e11a67c510b08cc58e6541bdbd9667a548a253c3f39145991ae6cd5", "bytes": 417 }, { "case": "verdict_eff001_storm_and_memo", "corpus": "verdicts", - "digest": "68d132b1eb434c314635ace94f186ea4f080cb04e79fc26d0d2feb3846f80fb5", + "digest": "94ed663fe27fa19b913bd7cb1696040b5043d908c71995c53ae944d40634a8d3", "bytes": 1054 }, { "case": "verdict_flow_local_wordings", "corpus": "verdicts", - "digest": "59d7ecea9efc0547acddc90d4868713dd50c10abede3b41250f96e66592404c3", + "digest": "9f4f197a3efb607880a2e75a7425c45f8672f06f8f2c43eeba117a4bfb16b60a", "bytes": 1273 }, { "case": "verdict_multi_file_ordering", "corpus": "verdicts", - "digest": "ba871ddc6cc81bd4e3185e51855561ab4b9ed5bd3b400f308a87987bad80d2a0", + "digest": "e111eabe5fbd8fcb152fc4b28b2ab5b4fdb8b92725ea240349c27389837bf8e0", "bytes": 1036 }, { "case": "verdict_own014_escape_slice_dropped", "corpus": "verdicts", - "digest": "48c6d1f5ad01e10b73d984a1140f542b9c56fb1c462a1e17002398362f66ed32", + "digest": "666dc9182ccbb4adc9b6122a90a59b6b38534d22e7f27061289f343c4cfe9b46", "bytes": 598 }, { "case": "verdict_own014_wordings", "corpus": "verdicts", - "digest": "af69dbeed1437c27cc5fcb634231842c4ab0525ea7cc1f68e13e0148a23b12b9", + "digest": "fefa681ee9eda9ed9161d8bf66cfddd865fcc5114b83a200a8fd83e66d048e56", "bytes": 1035 }, { "case": "verdict_own051_gates", "corpus": "verdicts", - "digest": "ff3d7627f6143425985ad86aedb3f6fb962118c72de6e1b047ba8bf0026238ec", + "digest": "4590bdda62b257e16b225991ca1203e050e2627905346ea55d793cdced15b5a2", "bytes": 1262 }, { "case": "verdict_pool_view_anchor", "corpus": "verdicts", - "digest": "67a62185aec92d3afdd043816def76b1d69fce13a3fa5339c61cefb69c823504", + "digest": "0c425103a62f8d845d620663d8fbe067e80c2967d9505d7f72466c8840fe549a", "bytes": 493 }, { "case": "verdict_protocol_absent_rules_are_silent", "corpus": "verdicts", - "digest": "6a2c86794db9155f50e8d2f49b5a0750e89bbb2308ddb3e745cbad04cba1debb", + "digest": "cdea8afef32c64801071e43fd6d2cc1bff323372640b3b7801818795b2559bc0", "bytes": 1158 }, { "case": "verdict_protocol_barrier_maybe_two_step_slice", "corpus": "verdicts", - "digest": "10070bdf723cddb776b8bcddbe0ec201d4d58f9018ff8271572436b8c96c5955", + "digest": "40328aaf2b11a6c715eb9a873f39562788fef78b22c0d62363d78cb072ba955b", "bytes": 1309 }, { "case": "verdict_protocol_beside_other_families", "corpus": "verdicts", - "digest": "ad5e4f3f36a1d359ccb8532365801cc4043babd9176556f2a072a85bc334e0f5", + "digest": "30dea8e128ba72742004db04be010e7424b79ae82d3c76b58cd9abc704c7ab7c", "bytes": 1709 }, { "case": "verdict_protocol_exit_codes_and_wordings", "corpus": "verdicts", - "digest": "443ca8012688649cd1f1af51d7fe31b421af9af9518c775f07ff33ff1cecfd31", + "digest": "8533d5e4452f7c353cc81ce0ee4bb1ba7df3267edf5830737aeee90f9d786629", "bytes": 1679 }, { "case": "verdict_protocol_identity_and_line_guards", "corpus": "verdicts", - "digest": "addb7b7e66c3716aa048eb46a7dd49e45f17bca06c82d54bcfd3e314485c2e28", + "digest": "326c47e5ee40b908ae49481c2afdd4647fb29b5f10d8a4770af58d52869fc0f1", "bytes": 1771 }, { "case": "verdict_protocol_obl005_dead_rule", "corpus": "verdicts", - "digest": "cb3e6c15db1365ee93db746d073de313083cbe592a22aaa366d83bc5019c0254", + "digest": "b33de9a202cde29dce6c253c33860af9aebe6e639b03817699712bf4373c715f", "bytes": 2220 }, { "case": "verdict_protocol_tolerant_door_rules", "corpus": "verdicts", - "digest": "d98ab7fa30b2680cf0dbb605fec21bf62a44cd824b6bee6d98a27d23c3994edc", + "digest": "1816d2f2c5e90d64fda2b8edb55d946ac6091fcd6840d74c3c8e03432c444fb0", "bytes": 2018 }, { "case": "verdict_same_site_distinct_events", "corpus": "verdicts", - "digest": "657cb50c6df9feecf3c9791f02a6555aed08810d622b182313ca507623e37c41", + "digest": "2e4358f94b476b9abb5e655d263cfe3e385b0b66fbb626910df6933a1f11967e", "bytes": 1543 }, { "case": "verdict_severity_tiers_and_suppression", "corpus": "verdicts", - "digest": "10ffbd221bd11913531b3b672dabd966dee52983330856941358b0d9416819ad", + "digest": "b769397a6dd2afbfdf5ccbfe7300fee31b5c5b2824288389db0e19f4630bdd29", "bytes": 1916 }, { "case": "verdict_skip_list_artifacts", "corpus": "verdicts", - "digest": "7cec84e88b7002fc0dced5d9c086cbf5763f6b26a6d4f5b8236f47bb67646299", + "digest": "a7db24c76096b3d1214ede7180abcc542f0c460bb0e1e3156cfcc7a41ef79b76", "bytes": 1460 }, { "case": "verdict_subscription_lambda_note", "corpus": "verdicts", - "digest": "526566f9b3ceb3fb45fea6af1d86d0fe722df32159a05410b5cc15fcefa48001", + "digest": "1b3566896123392c76e5b5694b12a68db5c2ca613821e81b0bc44dfdf522199a", "bytes": 589 }, { "case": "verdict_unknown_line_guards", "corpus": "verdicts", - "digest": "94ee4ea27864e84ae6113ed4b17856f1c6882714a3865602a73f289bdb7ab8d2", + "digest": "188ca5752faf2f440513511c65ac823ce48c69bf8effa7af403516f7c0bde9aa", "bytes": 1216 }, { "case": "vocab_unknown_op", "corpus": "lowered", - "digest": "c07494fb952154bcb0f026969754a20bf856c5c12b54a1c9b2aca0468315838d", + "digest": "866a9d0b4bf4898bbcb2a52b9522201efea52c0a0ff7a75ee2963ad4c88851a9", "bytes": 108 } ] diff --git a/tests/fixtures/repro/domain_exponent.facts.json b/tests/fixtures/repro/domain_exponent.facts.json index e2f26a41..641ad6a3 100644 --- a/tests/fixtures/repro/domain_exponent.facts.json +++ b/tests/fixtures/repro/domain_exponent.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DomainExponent", "canonical_probe": 1e3 } diff --git a/tests/fixtures/repro/domain_float.facts.json b/tests/fixtures/repro/domain_float.facts.json index 7ed98021..10ece038 100644 --- a/tests/fixtures/repro/domain_float.facts.json +++ b/tests/fixtures/repro/domain_float.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DomainFloat", "canonical_probe": 1.5 } diff --git a/tests/fixtures/repro/domain_int_above_i64.facts.json b/tests/fixtures/repro/domain_int_above_i64.facts.json index f9393931..9ee63f5d 100644 --- a/tests/fixtures/repro/domain_int_above_i64.facts.json +++ b/tests/fixtures/repro/domain_int_above_i64.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DomainIntAboveI64", "canonical_probe": 9223372036854775808 } diff --git a/tests/fixtures/repro/domain_int_below_i64.facts.json b/tests/fixtures/repro/domain_int_below_i64.facts.json index 6f9efdca..b1fb1c19 100644 --- a/tests/fixtures/repro/domain_int_below_i64.facts.json +++ b/tests/fixtures/repro/domain_int_below_i64.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DomainIntBelowI64", "canonical_probe": -9223372036854775809 } diff --git a/tests/fixtures/repro/domain_negative_zero.facts.json b/tests/fixtures/repro/domain_negative_zero.facts.json index 62e554f3..6d336d98 100644 --- a/tests/fixtures/repro/domain_negative_zero.facts.json +++ b/tests/fixtures/repro/domain_negative_zero.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DomainNegativeZero", "canonical_probe": -0 } diff --git a/tests/fixtures/repro/domain_non_finite.facts.json b/tests/fixtures/repro/domain_non_finite.facts.json index 8dd18dbb..3e33dbf1 100644 --- a/tests/fixtures/repro/domain_non_finite.facts.json +++ b/tests/fixtures/repro/domain_non_finite.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DomainNonFinite", "canonical_probe": NaN } diff --git a/tests/fixtures/repro/hoist_neg_while_body.repro.json b/tests/fixtures/repro/hoist_neg_while_body.repro.json index 050ace09..7d0ee0c2 100644 --- a/tests/fixtures/repro/hoist_neg_while_body.repro.json +++ b/tests/fixtures/repro/hoist_neg_while_body.repro.json @@ -1,20 +1,20 @@ { "repro_version": 3, "input": { - "ownir_version": 0, + "ownir_version": 1, "raw": { "algorithm": "sha256", - "digest": "c1440b469739846502d5f4530184c535580de0f1cbb3f11a88db408c32cf0b4a", + "digest": "a7f863be7b028758543a21c7981207f446de936b37927a3cb1d0bac5289fb977", "bytes": 446, - "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMCwKICAibW9kdWxlIjogIkhvaXN0TmVnMiIsCiAgImZ1bmN0aW9ucyI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiTSIsCiAgICAgICJmaWxlIjogIkYuY3MiLAogICAgICAiYm9keSI6IFsKICAgICAgICB7CiAgICAgICAgICAib3AiOiAid2hpbGUiLAogICAgICAgICAgImxpbmUiOiAyLAogICAgICAgICAgImJvZHkiOiBbCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAib3AiOiAiYWNxdWlyZSIsCiAgICAgICAgICAgICAgInZhciI6ICJyIiwKICAgICAgICAgICAgICAibGluZSI6IDMKICAgICAgICAgICAgfQogICAgICAgICAgXQogICAgICAgIH0sCiAgICAgICAgewogICAgICAgICAgIm9wIjogInJlbGVhc2UiLAogICAgICAgICAgInZhciI6ICJyIiwKICAgICAgICAgICJsaW5lIjogNQogICAgICAgIH0KICAgICAgXQogICAgfQogIF0KfQo=" + "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMSwKICAibW9kdWxlIjogIkhvaXN0TmVnMiIsCiAgImZ1bmN0aW9ucyI6IFsKICAgIHsKICAgICAgIm5hbWUiOiAiTSIsCiAgICAgICJmaWxlIjogIkYuY3MiLAogICAgICAiYm9keSI6IFsKICAgICAgICB7CiAgICAgICAgICAib3AiOiAid2hpbGUiLAogICAgICAgICAgImxpbmUiOiAyLAogICAgICAgICAgImJvZHkiOiBbCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAib3AiOiAiYWNxdWlyZSIsCiAgICAgICAgICAgICAgInZhciI6ICJyIiwKICAgICAgICAgICAgICAibGluZSI6IDMKICAgICAgICAgICAgfQogICAgICAgICAgXQogICAgICAgIH0sCiAgICAgICAgewogICAgICAgICAgIm9wIjogInJlbGVhc2UiLAogICAgICAgICAgInZhciI6ICJyIiwKICAgICAgICAgICJsaW5lIjogNQogICAgICAgIH0KICAgICAgXQogICAgfQogIF0KfQo=" }, "canonical": { "algorithm": "sha256", - "digest": "d2ae2309b105c985d6eeda13a2eb53b72bcd56fe895ac6919023ecdca72ea1e4", + "digest": "8f4807d8dd0a671659335446601ca8ec5db2c7e21bfd0c50164393f5e0478b0a", "bytes": 195 }, "document": { - "ownir_version": 0, + "ownir_version": 1, "module": "HoistNeg2", "functions": [ { @@ -47,19 +47,19 @@ "id": "python-ownlang", "consumed": { "algorithm": "sha256", - "digest": "c1440b469739846502d5f4530184c535580de0f1cbb3f11a88db408c32cf0b4a", + "digest": "a7f863be7b028758543a21c7981207f446de936b37927a3cb1d0bac5289fb977", "bytes": 446 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "HoistNeg2", "resources": [ { @@ -201,7 +201,7 @@ "document": { "degraded": null, "module": "HoistNeg2", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", @@ -239,19 +239,19 @@ "id": "rust-own-bridge", "consumed": { "algorithm": "sha256", - "digest": "c1440b469739846502d5f4530184c535580de0f1cbb3f11a88db408c32cf0b4a", + "digest": "a7f863be7b028758543a21c7981207f446de936b37927a3cb1d0bac5289fb977", "bytes": 446 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "HoistNeg2", "resources": [ { @@ -393,7 +393,7 @@ "document": { "degraded": null, "module": "HoistNeg2", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/repro/hoist_neg_while_body.trace.json b/tests/fixtures/repro/hoist_neg_while_body.trace.json index 2f7e2f85..e65040a3 100644 --- a/tests/fixtures/repro/hoist_neg_while_body.trace.json +++ b/tests/fixtures/repro/hoist_neg_while_body.trace.json @@ -7,7 +7,7 @@ "engine": "python-ownlang", "input": { "algorithm": "sha256", - "digest": "d2ae2309b105c985d6eeda13a2eb53b72bcd56fe895ac6919023ecdca72ea1e4", + "digest": "8f4807d8dd0a671659335446601ca8ec5db2c7e21bfd0c50164393f5e0478b0a", "bytes": 195 }, "layers": [ @@ -21,7 +21,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -194,7 +194,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", @@ -233,7 +233,7 @@ "engine": "rust-own-bridge", "input": { "algorithm": "sha256", - "digest": "d2ae2309b105c985d6eeda13a2eb53b72bcd56fe895ac6919023ecdca72ea1e4", + "digest": "8f4807d8dd0a671659335446601ca8ec5db2c7e21bfd0c50164393f5e0478b0a", "bytes": 195 }, "layers": [ @@ -247,7 +247,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -420,7 +420,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", diff --git a/tests/fixtures/repro/mosdump_degraded_duplicate_key.repro.json b/tests/fixtures/repro/mosdump_degraded_duplicate_key.repro.json index 8a49b262..27f0e7f1 100644 --- a/tests/fixtures/repro/mosdump_degraded_duplicate_key.repro.json +++ b/tests/fixtures/repro/mosdump_degraded_duplicate_key.repro.json @@ -1,20 +1,20 @@ { "repro_version": 3, "input": { - "ownir_version": 0, + "ownir_version": 1, "raw": { "algorithm": "sha256", - "digest": "a8f0744b8bdbf9b5cdbbd55b381a44b53d49345f915b852b2019e9cb9aeb2d5a", + "digest": "60debca1e8aede04c2048c023af7f29a7af41131d9b081abaace6d40761bfae5", "bytes": 747, - "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMCwKICAibW9kdWxlIjogIkRlZ3JhZGVkIiwKICAiZnVuY3Rpb25zIjogWwogICAgewogICAgICAibmFtZSI6ICJUYWtlIiwKICAgICAgInNpZyI6ICJTeXN0ZW0uSU8uU3RyZWFtIiwKICAgICAgImZpbGUiOiAidDEuY3MiLAogICAgICAicGFyYW1zIjogWwogICAgICAgIHsKICAgICAgICAgICJuYW1lIjogInAiLAogICAgICAgICAgImxpbmUiOiAxCiAgICAgICAgfQogICAgICBdLAogICAgICAiYm9keSI6IFsKICAgICAgICB7CiAgICAgICAgICAib3AiOiAicmVsZWFzZSIsCiAgICAgICAgICAidmFyIjogInAiLAogICAgICAgICAgImxpbmUiOiAyCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgewogICAgICAibmFtZSI6ICJUYWtlIiwKICAgICAgInNpZyI6ICJTeXN0ZW0uU3RyaW5nIiwKICAgICAgImZpbGUiOiAidDIuY3MiLAogICAgICAicGFyYW1zIjogWwogICAgICAgIHsKICAgICAgICAgICJuYW1lIjogInAiLAogICAgICAgICAgImxpbmUiOiA1CiAgICAgICAgfQogICAgICBdLAogICAgICAiYm9keSI6IFsKICAgICAgICB7CiAgICAgICAgICAib3AiOiAidXNlIiwKICAgICAgICAgICJ2YXIiOiAicCIsCiAgICAgICAgICAibGluZSI6IDYKICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJuYW1lIjogIlRha2UoU3lzdGVtLklPLlN0cmVhbSkiLAogICAgICAiZmlsZSI6ICJjbGFzaC5jcyIsCiAgICAgICJib2R5IjogW10KICAgIH0KICBdCn0K" + "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMSwKICAibW9kdWxlIjogIkRlZ3JhZGVkIiwKICAiZnVuY3Rpb25zIjogWwogICAgewogICAgICAibmFtZSI6ICJUYWtlIiwKICAgICAgInNpZyI6ICJTeXN0ZW0uSU8uU3RyZWFtIiwKICAgICAgImZpbGUiOiAidDEuY3MiLAogICAgICAicGFyYW1zIjogWwogICAgICAgIHsKICAgICAgICAgICJuYW1lIjogInAiLAogICAgICAgICAgImxpbmUiOiAxCiAgICAgICAgfQogICAgICBdLAogICAgICAiYm9keSI6IFsKICAgICAgICB7CiAgICAgICAgICAib3AiOiAicmVsZWFzZSIsCiAgICAgICAgICAidmFyIjogInAiLAogICAgICAgICAgImxpbmUiOiAyCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgewogICAgICAibmFtZSI6ICJUYWtlIiwKICAgICAgInNpZyI6ICJTeXN0ZW0uU3RyaW5nIiwKICAgICAgImZpbGUiOiAidDIuY3MiLAogICAgICAicGFyYW1zIjogWwogICAgICAgIHsKICAgICAgICAgICJuYW1lIjogInAiLAogICAgICAgICAgImxpbmUiOiA1CiAgICAgICAgfQogICAgICBdLAogICAgICAiYm9keSI6IFsKICAgICAgICB7CiAgICAgICAgICAib3AiOiAidXNlIiwKICAgICAgICAgICJ2YXIiOiAicCIsCiAgICAgICAgICAibGluZSI6IDYKICAgICAgICB9CiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJuYW1lIjogIlRha2UoU3lzdGVtLklPLlN0cmVhbSkiLAogICAgICAiZmlsZSI6ICJjbGFzaC5jcyIsCiAgICAgICJib2R5IjogW10KICAgIH0KICBdCn0K" }, "canonical": { "algorithm": "sha256", - "digest": "92f052ccef71d87b1b80ff9e9bcd9c74ec9db30b7ad34c883af0096a83088fdd", + "digest": "5d67d6918c18d93df76a29b2d58f50202444d3b95e2a7de6cca64541bb71e06d", "bytes": 376 }, "document": { - "ownir_version": 0, + "ownir_version": 1, "module": "Degraded", "functions": [ { @@ -66,19 +66,19 @@ "id": "python-ownlang", "consumed": { "algorithm": "sha256", - "digest": "a8f0744b8bdbf9b5cdbbd55b381a44b53d49345f915b852b2019e9cb9aeb2d5a", + "digest": "60debca1e8aede04c2048c023af7f29a7af41131d9b081abaace6d40761bfae5", "bytes": 747 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "Degraded", "resources": [ { @@ -257,7 +257,7 @@ "document": { "degraded": "ValueError: duplicate MethodSkeleton key: Take(System.IO.Stream)", "module": "Degraded", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -298,7 +298,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "4e4a0768c50f8218e945ee0ed0801127681eca2fea72f4d7bdfbe3fd63a2d9f6", + "digest": "11420b28e02f7d9cf9e1ba3791c8420f1565f9f25f0c8e57fb43eac932a2f152", "bytes": 822 } } @@ -308,19 +308,19 @@ "id": "rust-own-bridge", "consumed": { "algorithm": "sha256", - "digest": "a8f0744b8bdbf9b5cdbbd55b381a44b53d49345f915b852b2019e9cb9aeb2d5a", + "digest": "60debca1e8aede04c2048c023af7f29a7af41131d9b081abaace6d40761bfae5", "bytes": 747 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "Degraded", "resources": [ { @@ -499,7 +499,7 @@ "document": { "degraded": "ValueError: duplicate MethodSkeleton key: Take(System.IO.Stream)", "module": "Degraded", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -540,7 +540,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "4e4a0768c50f8218e945ee0ed0801127681eca2fea72f4d7bdfbe3fd63a2d9f6", + "digest": "11420b28e02f7d9cf9e1ba3791c8420f1565f9f25f0c8e57fb43eac932a2f152", "bytes": 822 } } diff --git a/tests/fixtures/repro/mosdump_degraded_duplicate_key.trace.json b/tests/fixtures/repro/mosdump_degraded_duplicate_key.trace.json index 928e619e..afd5a7f4 100644 --- a/tests/fixtures/repro/mosdump_degraded_duplicate_key.trace.json +++ b/tests/fixtures/repro/mosdump_degraded_duplicate_key.trace.json @@ -7,7 +7,7 @@ "engine": "python-ownlang", "input": { "algorithm": "sha256", - "digest": "92f052ccef71d87b1b80ff9e9bcd9c74ec9db30b7ad34c883af0096a83088fdd", + "digest": "5d67d6918c18d93df76a29b2d58f50202444d3b95e2a7de6cca64541bb71e06d", "bytes": 376 }, "layers": [ @@ -21,7 +21,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -238,7 +238,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", @@ -286,7 +286,7 @@ "engine": "rust-own-bridge", "input": { "algorithm": "sha256", - "digest": "92f052ccef71d87b1b80ff9e9bcd9c74ec9db30b7ad34c883af0096a83088fdd", + "digest": "5d67d6918c18d93df76a29b2d58f50202444d3b95e2a7de6cca64541bb71e06d", "bytes": 376 }, "layers": [ @@ -300,7 +300,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -517,7 +517,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", diff --git a/tests/fixtures/repro/protocol_isloaded_violation.repro.json b/tests/fixtures/repro/protocol_isloaded_violation.repro.json index 9c68ced9..a655fe9b 100644 --- a/tests/fixtures/repro/protocol_isloaded_violation.repro.json +++ b/tests/fixtures/repro/protocol_isloaded_violation.repro.json @@ -1,20 +1,20 @@ { "repro_version": 3, "input": { - "ownir_version": 0, + "ownir_version": 1, "raw": { "algorithm": "sha256", - "digest": "02b88bac6b1c9a22f1c410fda97c9d428fc54a1253ab16ca74a5397a4319e8fc", + "digest": "b5fd4f02066e01f09661c2a68b00faa58ec2b5dd6b43a640ff15514cd1f74033", "bytes": 1559, - "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMCwKICAibW9kdWxlIjogIkV4dHJhY3RlZCIsCiAgImNvbXBvbmVudHMiOiBbXSwKICAicHJvdG9jb2xzIjogWwogICAgewogICAgICAibmFtZSI6ICJEb2N1bWVudExvYWRpbmciLAogICAgICAiZGVzY3JpcHRpb24iOiAiVGhlIGRvY3VtZW50IHRyZWUgaXMgaW5jb25zaXN0ZW50IHdoaWxlIElzTG9hZGVkID09IGZhbHNlOyBwdWJsaXNoaW5nIERvY3VtZW50L1Jvd3MvVG90YWxzIGluIHRoYXQgd2luZG93IGhhbmRzIGJpbmRpbmdzIGEgYnJva2VuIG9iamVjdC4iLAogICAgICAib3BlbnMiOiAgeyJraW5kIjogImFzc2lnbiIsICJ0YXJnZXQiOiAiSXNMb2FkZWQiLCAidmFsdWUiOiBmYWxzZX0sCiAgICAgICJjbG9zZXMiOiB7ImtpbmQiOiAiYXNzaWduIiwgInRhcmdldCI6ICJJc0xvYWRlZCIsICJ2YWx1ZSI6IHRydWV9LAogICAgICAiYmFycmllcnMiOiBbCiAgICAgICAgeyJraW5kIjogImNhbGwiLCAiY2FsbGVlIjogIk9uUHJvcGVydHlDaGFuZ2VkIiwKICAgICAgICAgImFyZ3MiOiBbIkRvY3VtZW50IiwgIlJvd3MiLCAiVG90YWxzIiwgIlNlbGVjdGVkSXRlbSJdfQogICAgICBdLAogICAgICAiYWxsb3ciOiBbCiAgICAgICAgeyJraW5kIjogImNhbGwiLCAiY2FsbGVlIjogIk9uUHJvcGVydHlDaGFuZ2VkIiwKICAgICAgICAgImFyZ3MiOiBbIklzTG9hZGVkIiwgIklzQnVzeSIsICJQcm9ncmVzcyIsICJTdGF0dXNUZXh0Il19CiAgICAgIF0sCiAgICAgICJleGl0X2JhcnJpZXJzIjogdHJ1ZSwKICAgICAgInNjb3BlIjogeyJtZXRob2RzIjogWyJCaWdEb2N1bWVudFZpZXdNb2RlbC5Mb2FkQmlnRG9jdW1lbnQiXX0KICAgIH0KICBdLAogICJwcm90b2NvbF9mdW5jdGlvbnMiOiBbCiAgICB7CiAgICAgICJuYW1lIjogIkJyb2tlci5CaWdEb2N1bWVudFZpZXdNb2RlbC5Mb2FkQmlnRG9jdW1lbnQiLAogICAgICAiZmlsZSI6ICJCaWdEb2N1bWVudFZpZXdNb2RlbC5jcyIsCiAgICAgICJldmVudHMiOiBbCiAgICAgICAgeyJldiI6ICJhc3NpZ24iLCAidGFyZ2V0IjogIklzTG9hZGVkIiwgInZhbHVlIjogZmFsc2UsICJsaW5lIjogMTg0fSwKICAgICAgICB7ImV2IjogImNhbGwiLCAiY2FsbGVlIjogIk9uUHJvcGVydHlDaGFuZ2VkIiwgImFyZyI6ICJJc0J1c3kiLCAibGluZSI6IDE4NX0sCiAgICAgICAgeyJldiI6ICJjYWxsIiwgImNhbGxlZSI6ICJSZWJ1aWxkSW5kZXhlcyIsICJsaW5lIjogMTkwfSwKICAgICAgICB7ImV2IjogImlmIiwgImxpbmUiOiAyMjAsICJ0aGVuIjogWwogICAgICAgICAgeyJldiI6ICJjYWxsIiwgImNhbGxlZSI6ICJPblByb3BlcnR5Q2hhbmdlZCIsICJhcmciOiAiRG9jdW1lbnQiLCAibGluZSI6IDI0MX0KICAgICAgICBdLCAiZWxzZSI6IFtdfSwKICAgICAgICB7ImV2IjogImFzc2lnbiIsICJ0YXJnZXQiOiAiSXNMb2FkZWQiLCAidmFsdWUiOiB0cnVlLCAibGluZSI6IDI2MH0sCiAgICAgICAgeyJldiI6ICJjYWxsIiwgImNhbGxlZSI6ICJPblByb3BlcnR5Q2hhbmdlZCIsICJhcmciOiAiRG9jdW1lbnQiLCAibGluZSI6IDI2MX0KICAgICAgXQogICAgfQogIF0KfQo=" + "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMSwKICAibW9kdWxlIjogIkV4dHJhY3RlZCIsCiAgImNvbXBvbmVudHMiOiBbXSwKICAicHJvdG9jb2xzIjogWwogICAgewogICAgICAibmFtZSI6ICJEb2N1bWVudExvYWRpbmciLAogICAgICAiZGVzY3JpcHRpb24iOiAiVGhlIGRvY3VtZW50IHRyZWUgaXMgaW5jb25zaXN0ZW50IHdoaWxlIElzTG9hZGVkID09IGZhbHNlOyBwdWJsaXNoaW5nIERvY3VtZW50L1Jvd3MvVG90YWxzIGluIHRoYXQgd2luZG93IGhhbmRzIGJpbmRpbmdzIGEgYnJva2VuIG9iamVjdC4iLAogICAgICAib3BlbnMiOiAgeyJraW5kIjogImFzc2lnbiIsICJ0YXJnZXQiOiAiSXNMb2FkZWQiLCAidmFsdWUiOiBmYWxzZX0sCiAgICAgICJjbG9zZXMiOiB7ImtpbmQiOiAiYXNzaWduIiwgInRhcmdldCI6ICJJc0xvYWRlZCIsICJ2YWx1ZSI6IHRydWV9LAogICAgICAiYmFycmllcnMiOiBbCiAgICAgICAgeyJraW5kIjogImNhbGwiLCAiY2FsbGVlIjogIk9uUHJvcGVydHlDaGFuZ2VkIiwKICAgICAgICAgImFyZ3MiOiBbIkRvY3VtZW50IiwgIlJvd3MiLCAiVG90YWxzIiwgIlNlbGVjdGVkSXRlbSJdfQogICAgICBdLAogICAgICAiYWxsb3ciOiBbCiAgICAgICAgeyJraW5kIjogImNhbGwiLCAiY2FsbGVlIjogIk9uUHJvcGVydHlDaGFuZ2VkIiwKICAgICAgICAgImFyZ3MiOiBbIklzTG9hZGVkIiwgIklzQnVzeSIsICJQcm9ncmVzcyIsICJTdGF0dXNUZXh0Il19CiAgICAgIF0sCiAgICAgICJleGl0X2JhcnJpZXJzIjogdHJ1ZSwKICAgICAgInNjb3BlIjogeyJtZXRob2RzIjogWyJCaWdEb2N1bWVudFZpZXdNb2RlbC5Mb2FkQmlnRG9jdW1lbnQiXX0KICAgIH0KICBdLAogICJwcm90b2NvbF9mdW5jdGlvbnMiOiBbCiAgICB7CiAgICAgICJuYW1lIjogIkJyb2tlci5CaWdEb2N1bWVudFZpZXdNb2RlbC5Mb2FkQmlnRG9jdW1lbnQiLAogICAgICAiZmlsZSI6ICJCaWdEb2N1bWVudFZpZXdNb2RlbC5jcyIsCiAgICAgICJldmVudHMiOiBbCiAgICAgICAgeyJldiI6ICJhc3NpZ24iLCAidGFyZ2V0IjogIklzTG9hZGVkIiwgInZhbHVlIjogZmFsc2UsICJsaW5lIjogMTg0fSwKICAgICAgICB7ImV2IjogImNhbGwiLCAiY2FsbGVlIjogIk9uUHJvcGVydHlDaGFuZ2VkIiwgImFyZyI6ICJJc0J1c3kiLCAibGluZSI6IDE4NX0sCiAgICAgICAgeyJldiI6ICJjYWxsIiwgImNhbGxlZSI6ICJSZWJ1aWxkSW5kZXhlcyIsICJsaW5lIjogMTkwfSwKICAgICAgICB7ImV2IjogImlmIiwgImxpbmUiOiAyMjAsICJ0aGVuIjogWwogICAgICAgICAgeyJldiI6ICJjYWxsIiwgImNhbGxlZSI6ICJPblByb3BlcnR5Q2hhbmdlZCIsICJhcmciOiAiRG9jdW1lbnQiLCAibGluZSI6IDI0MX0KICAgICAgICBdLCAiZWxzZSI6IFtdfSwKICAgICAgICB7ImV2IjogImFzc2lnbiIsICJ0YXJnZXQiOiAiSXNMb2FkZWQiLCAidmFsdWUiOiB0cnVlLCAibGluZSI6IDI2MH0sCiAgICAgICAgeyJldiI6ICJjYWxsIiwgImNhbGxlZSI6ICJPblByb3BlcnR5Q2hhbmdlZCIsICJhcmciOiAiRG9jdW1lbnQiLCAibGluZSI6IDI2MX0KICAgICAgXQogICAgfQogIF0KfQo=" }, "canonical": { "algorithm": "sha256", - "digest": "f821b58b0da7e5ecb94798d1868db377876d945242a4e4d013d6499d7af7ee13", + "digest": "1025d2dadba8f7f00604e5d40a264d286a0d5cf62a243b16578fd00b3430d813", "bytes": 1217 }, "document": { - "ownir_version": 0, + "ownir_version": 1, "module": "Extracted", "components": [], "protocols": [ @@ -120,19 +120,19 @@ "id": "python-ownlang", "consumed": { "algorithm": "sha256", - "digest": "02b88bac6b1c9a22f1c410fda97c9d428fc54a1253ab16ca74a5397a4319e8fc", + "digest": "b5fd4f02066e01f09661c2a68b00faa58ec2b5dd6b43a640ff15514cd1f74033", "bytes": 1559 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "Extracted", "resources": [ { @@ -236,7 +236,7 @@ "document": { "degraded": null, "module": "Extracted", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -293,7 +293,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "734ea3f64a91b959b6189ab66c1abb3f35bce0276f882cbbc68c74394cbe8509", + "digest": "8cc50bd50c0c89ba50889e19589622969af9607dc7e43e843156391070340f39", "bytes": 1556 } } @@ -303,19 +303,19 @@ "id": "rust-own-bridge", "consumed": { "algorithm": "sha256", - "digest": "02b88bac6b1c9a22f1c410fda97c9d428fc54a1253ab16ca74a5397a4319e8fc", + "digest": "b5fd4f02066e01f09661c2a68b00faa58ec2b5dd6b43a640ff15514cd1f74033", "bytes": 1559 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "Extracted", "resources": [ { @@ -419,7 +419,7 @@ "document": { "degraded": null, "module": "Extracted", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -476,7 +476,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "734ea3f64a91b959b6189ab66c1abb3f35bce0276f882cbbc68c74394cbe8509", + "digest": "8cc50bd50c0c89ba50889e19589622969af9607dc7e43e843156391070340f39", "bytes": 1556 } } diff --git a/tests/fixtures/repro/protocol_isloaded_violation.trace.json b/tests/fixtures/repro/protocol_isloaded_violation.trace.json index e159cf7a..a5f231f2 100644 --- a/tests/fixtures/repro/protocol_isloaded_violation.trace.json +++ b/tests/fixtures/repro/protocol_isloaded_violation.trace.json @@ -7,7 +7,7 @@ "engine": "python-ownlang", "input": { "algorithm": "sha256", - "digest": "f821b58b0da7e5ecb94798d1868db377876d945242a4e4d013d6499d7af7ee13", + "digest": "1025d2dadba8f7f00604e5d40a264d286a0d5cf62a243b16578fd00b3430d813", "bytes": 1217 }, "layers": [ @@ -21,7 +21,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -147,7 +147,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", @@ -211,7 +211,7 @@ "engine": "rust-own-bridge", "input": { "algorithm": "sha256", - "digest": "f821b58b0da7e5ecb94798d1868db377876d945242a4e4d013d6499d7af7ee13", + "digest": "1025d2dadba8f7f00604e5d40a264d286a0d5cf62a243b16578fd00b3430d813", "bytes": 1217 }, "layers": [ @@ -225,7 +225,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -351,7 +351,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", diff --git a/tests/fixtures/repro/variants/bom.bin b/tests/fixtures/repro/variants/bom.bin index 936fa079..f74ea8de 100644 --- a/tests/fixtures/repro/variants/bom.bin +++ b/tests/fixtures/repro/variants/bom.bin @@ -1,4 +1,4 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Minimal" } diff --git a/tests/fixtures/repro/variants/byte_variants.json b/tests/fixtures/repro/variants/byte_variants.json index 37f68aa2..1571f8a5 100644 --- a/tests/fixtures/repro/variants/byte_variants.json +++ b/tests/fixtures/repro/variants/byte_variants.json @@ -10,7 +10,7 @@ ], "raw": { "algorithm": "sha256", - "digest": "bc2eb8d0b2dd4644ef71114ff85902cfe2c3cbaa1b0ed8d7d679d9d4f87eae19", + "digest": "dcb356dc38153f87557fca387626f9141f3b7e7c0e56a60de05ee3382fb93386", "bytes": 51 }, "python": { @@ -35,7 +35,7 @@ ], "raw": { "algorithm": "sha256", - "digest": "8a55a9878421cf6b34613a885a794bd45b027394d9fd098f3337fbcf691cd08e", + "digest": "b5517c9a81f5beeb40ed922a8513b07be328ee9657c96c8437da7054e39a19bf", "bytes": 52 }, "python": { @@ -50,13 +50,13 @@ "typed_door_accepted": true, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, @@ -68,7 +68,7 @@ ], "raw": { "algorithm": "sha256", - "digest": "cf1235ec70bd51243afc010f749e824c8708e0e847ef979cbfc203b5a9b08de3", + "digest": "2b6f6f3963575300fd43ca5e771be0bbc9f9c2cbf9d628832df6e5c1d3879ca5", "bytes": 49 }, "python": { @@ -83,13 +83,13 @@ "typed_door_accepted": true, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, @@ -101,7 +101,7 @@ ], "raw": { "algorithm": "sha256", - "digest": "85dbfe4aaf6b983a4e953cc4715e98d85b803103f787ea993c664dbbf0908219", + "digest": "6394abbd914117b7da2699abbc12e791a5a78b9699aad8bfd1e6232c8d5236b6", "bytes": 48 }, "python": { @@ -126,7 +126,7 @@ ], "raw": { "algorithm": "sha256", - "digest": "daf4baa39138c6c5c4b08de8483ecf395de2c7c1d4767edb65fac626c0a2bbf2", + "digest": "5a7a1a044ea315d567f9a0777031b7b5ba7d16f32606a3e331acdab7cf47d7df", "bytes": 48 }, "python": { @@ -141,13 +141,13 @@ "typed_door_accepted": true, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, @@ -159,7 +159,7 @@ ], "raw": { "algorithm": "sha256", - "digest": "9ac14b4869fb80a85e6e24299145189a393613b278f746007f3fec2a3e4552e3", + "digest": "ed5c01c1831c9a090b346c96334da79b70b85951d851f92621a5d896e03935b8", "bytes": 46 }, "python": { @@ -184,7 +184,7 @@ ], "raw": { "algorithm": "sha256", - "digest": "932481c5c66c42b5be67ece2fc1ebd326ff3d88f24c1313b48581b1c55251c09", + "digest": "b3d45f877b03fc8129e6343f1b2344f8d493ae8590a5ca8c9dad12edfbc58b21", "bytes": 47 }, "python": { @@ -199,13 +199,13 @@ "typed_door_accepted": true, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, @@ -217,7 +217,7 @@ ], "raw": { "algorithm": "sha256", - "digest": "bc5f3772f24bbbb76ca24828da224d4ab991efe07a87d69db69d0bbef175460a", + "digest": "4573241a7888cac6145dd1005623c1edd6573a221d6779515781eb247041ebc7", "bytes": 48 }, "python": { @@ -232,13 +232,13 @@ "typed_door_accepted": true, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, @@ -250,7 +250,7 @@ ], "raw": { "algorithm": "sha256", - "digest": "125efc27f3f0d62857b16bd305557332692df8396892a77485b559004d64ca0e", + "digest": "6fe99998f020698c05a07209e1de1c86ee9eef20fcde2ad4eab5e0ee18192dbc", "bytes": 38 }, "python": { @@ -265,13 +265,13 @@ "typed_door_accepted": true, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, @@ -283,7 +283,7 @@ ], "raw": { "algorithm": "sha256", - "digest": "3482b974719a7f7e9465b9db87016cc59688ac86dce80a7bb94c0db6d6c9e3b1", + "digest": "74c40b0a7c1b78ed452f72dc9f61894c9f601b9b5de73a1f97cd6d7ec416d963", "bytes": 52 }, "python": { @@ -298,13 +298,13 @@ "typed_door_accepted": true, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } }, "canonical": { "algorithm": "sha256", - "digest": "c5387976673a8a5a1cc3078398935ae907cddf9c0fc9fdf9cb1c63ed74722313", + "digest": "0f3af651cbd63f59131e16da784820e334fa68ed69b29fb068076abf4374d319", "bytes": 38 } } diff --git a/tests/fixtures/repro/variants/crlf.bin b/tests/fixtures/repro/variants/crlf.bin index 9d17f39b..14a675ad 100644 --- a/tests/fixtures/repro/variants/crlf.bin +++ b/tests/fixtures/repro/variants/crlf.bin @@ -1,4 +1,4 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Minimal" } diff --git a/tests/fixtures/repro/variants/extra_trailing_newline.bin b/tests/fixtures/repro/variants/extra_trailing_newline.bin index 95b3fb24..735741ec 100644 --- a/tests/fixtures/repro/variants/extra_trailing_newline.bin +++ b/tests/fixtures/repro/variants/extra_trailing_newline.bin @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Minimal" } diff --git a/tests/fixtures/repro/variants/invalid_utf8.bin b/tests/fixtures/repro/variants/invalid_utf8.bin index 14637283..323c1c03 100644 --- a/tests/fixtures/repro/variants/invalid_utf8.bin +++ b/tests/fixtures/repro/variants/invalid_utf8.bin @@ -1,4 +1,4 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Minim€l" } diff --git a/tests/fixtures/repro/variants/key_order_reversed.bin b/tests/fixtures/repro/variants/key_order_reversed.bin index 6c942cd0..893cc908 100644 --- a/tests/fixtures/repro/variants/key_order_reversed.bin +++ b/tests/fixtures/repro/variants/key_order_reversed.bin @@ -1,4 +1,4 @@ { "module": "Minimal", - "ownir_version": 0 + "ownir_version": 1 } diff --git a/tests/fixtures/repro/variants/malformed_json.bin b/tests/fixtures/repro/variants/malformed_json.bin index f0c2dc1c..849e3c97 100644 --- a/tests/fixtures/repro/variants/malformed_json.bin +++ b/tests/fixtures/repro/variants/malformed_json.bin @@ -1,3 +1,3 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Minimal" diff --git a/tests/fixtures/repro/variants/no_trailing_newline.bin b/tests/fixtures/repro/variants/no_trailing_newline.bin index dd0aa2e6..4cbc3f44 100644 --- a/tests/fixtures/repro/variants/no_trailing_newline.bin +++ b/tests/fixtures/repro/variants/no_trailing_newline.bin @@ -1,4 +1,4 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Minimal" } \ No newline at end of file diff --git a/tests/fixtures/repro/variants/plain.bin b/tests/fixtures/repro/variants/plain.bin index 46c14f81..47940bbf 100644 --- a/tests/fixtures/repro/variants/plain.bin +++ b/tests/fixtures/repro/variants/plain.bin @@ -1,4 +1,4 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Minimal" } diff --git a/tests/fixtures/repro/variants/whitespace_compact.bin b/tests/fixtures/repro/variants/whitespace_compact.bin index e26d3bec..c3d1a7f4 100644 --- a/tests/fixtures/repro/variants/whitespace_compact.bin +++ b/tests/fixtures/repro/variants/whitespace_compact.bin @@ -1 +1 @@ -{"ownir_version":0,"module":"Minimal"} \ No newline at end of file +{"ownir_version":1,"module":"Minimal"} \ No newline at end of file diff --git a/tests/fixtures/repro/variants/whitespace_expanded.bin b/tests/fixtures/repro/variants/whitespace_expanded.bin index fcc7fc9f..b425f44e 100644 --- a/tests/fixtures/repro/variants/whitespace_expanded.bin +++ b/tests/fixtures/repro/variants/whitespace_expanded.bin @@ -1,4 +1,4 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Minimal" } diff --git a/tests/fixtures/repro/verdict_door_effect_deps_not_strings.repro.json b/tests/fixtures/repro/verdict_door_effect_deps_not_strings.repro.json index 6bc75a9e..cb344746 100644 --- a/tests/fixtures/repro/verdict_door_effect_deps_not_strings.repro.json +++ b/tests/fixtures/repro/verdict_door_effect_deps_not_strings.repro.json @@ -1,20 +1,20 @@ { "repro_version": 3, "input": { - "ownir_version": 0, + "ownir_version": 1, "raw": { "algorithm": "sha256", - "digest": "1709103f7676c80ef52d6d0ad42ce4040842a61f6b63f55a5b133620e778b069", + "digest": "c28447881206b741a6c80335c945308cbbf16b8eb7a44e6490a44e1c17a9ab05", "bytes": 948, - "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMCwKICAibW9kdWxlIjogIkRvb3IiLAogICJjb21wb25lbnRzIjogW10sCiAgIl9kb2MiOiBbCiAgICAiIzI5NCBPRC0xOiBCUi1EMidzIHNraXAtbm90LWNvZXJjZSBvbiB0aGUgdG9sZXJhbnQgZG9vci4gVGhlIHJlZmVyZW5jZSBTS0lQUyIsCiAgICAidGhlIGVmZmVjdCB3aG9zZSBkZXBzIGlzIGEgYmFyZSBzdHJpbmcgKG5vIHNwdXJpb3VzIEVGRjAwMSkgYW5kIHJlcG9ydHMgdGhlIiwKICAgICJ3ZWxsLWZvcm1lZCBzaWJsaW5nOyB0aGUgUnVzdCB0b2xlcmFudCBlbnRyeSBpcyB0aGUgdHlwZWQgY29uc3RydWN0b3IsIHdoaWNoIiwKICAgICJyZWZ1c2VzIHRoZSBkb2N1bWVudCBiZWZvcmUgdGhlIGJyaWRnZSdzIHNraXAgcnVsZSBjYW4gcnVuLiIKICBdLAogICJlZmZlY3RzIjogWwogICAgewogICAgICAiY29tcG9uZW50IjogIlgiLAogICAgICAiZmlsZSI6ICJYLnRzeCIsCiAgICAgICJsaW5lIjogMSwKICAgICAgImlvIjogdHJ1ZSwKICAgICAgImRlcHMiOiAiYSIsCiAgICAgICJiaW5kaW5ncyI6IFsKICAgICAgICB7CiAgICAgICAgICAibmFtZSI6ICJhIiwKICAgICAgICAgICJpbml0IjogIm9iamVjdCIsCiAgICAgICAgICAicmVmcyI6IFtdLAogICAgICAgICAgImxpbmUiOiAxCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgewogICAgICAiY29tcG9uZW50IjogIk9rIiwKICAgICAgImZpbGUiOiAiT2sudHN4IiwKICAgICAgImxpbmUiOiA5LAogICAgICAiaW8iOiB0cnVlLAogICAgICAiZGVwcyI6IFsKICAgICAgICAibyIKICAgICAgXSwKICAgICAgImJpbmRpbmdzIjogWwogICAgICAgIHsKICAgICAgICAgICJuYW1lIjogIm8iLAogICAgICAgICAgImluaXQiOiAib2JqZWN0IiwKICAgICAgICAgICJyZWZzIjogW10sCiAgICAgICAgICAibGluZSI6IDgKICAgICAgICB9CiAgICAgIF0KICAgIH0KICBdCn0K" + "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMSwKICAibW9kdWxlIjogIkRvb3IiLAogICJjb21wb25lbnRzIjogW10sCiAgIl9kb2MiOiBbCiAgICAiIzI5NCBPRC0xOiBCUi1EMidzIHNraXAtbm90LWNvZXJjZSBvbiB0aGUgdG9sZXJhbnQgZG9vci4gVGhlIHJlZmVyZW5jZSBTS0lQUyIsCiAgICAidGhlIGVmZmVjdCB3aG9zZSBkZXBzIGlzIGEgYmFyZSBzdHJpbmcgKG5vIHNwdXJpb3VzIEVGRjAwMSkgYW5kIHJlcG9ydHMgdGhlIiwKICAgICJ3ZWxsLWZvcm1lZCBzaWJsaW5nOyB0aGUgUnVzdCB0b2xlcmFudCBlbnRyeSBpcyB0aGUgdHlwZWQgY29uc3RydWN0b3IsIHdoaWNoIiwKICAgICJyZWZ1c2VzIHRoZSBkb2N1bWVudCBiZWZvcmUgdGhlIGJyaWRnZSdzIHNraXAgcnVsZSBjYW4gcnVuLiIKICBdLAogICJlZmZlY3RzIjogWwogICAgewogICAgICAiY29tcG9uZW50IjogIlgiLAogICAgICAiZmlsZSI6ICJYLnRzeCIsCiAgICAgICJsaW5lIjogMSwKICAgICAgImlvIjogdHJ1ZSwKICAgICAgImRlcHMiOiAiYSIsCiAgICAgICJiaW5kaW5ncyI6IFsKICAgICAgICB7CiAgICAgICAgICAibmFtZSI6ICJhIiwKICAgICAgICAgICJpbml0IjogIm9iamVjdCIsCiAgICAgICAgICAicmVmcyI6IFtdLAogICAgICAgICAgImxpbmUiOiAxCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgewogICAgICAiY29tcG9uZW50IjogIk9rIiwKICAgICAgImZpbGUiOiAiT2sudHN4IiwKICAgICAgImxpbmUiOiA5LAogICAgICAiaW8iOiB0cnVlLAogICAgICAiZGVwcyI6IFsKICAgICAgICAibyIKICAgICAgXSwKICAgICAgImJpbmRpbmdzIjogWwogICAgICAgIHsKICAgICAgICAgICJuYW1lIjogIm8iLAogICAgICAgICAgImluaXQiOiAib2JqZWN0IiwKICAgICAgICAgICJyZWZzIjogW10sCiAgICAgICAgICAibGluZSI6IDgKICAgICAgICB9CiAgICAgIF0KICAgIH0KICBdCn0K" }, "canonical": { "algorithm": "sha256", - "digest": "9ac5b570e4a2956f2d771aa0cc5de5e1a7584683d0feefe453df6da4d5007c7b", + "digest": "b85df11e6d8bd400d38b209009a3517bce2bce543e65fd2faac863dcfd057cf1", "bytes": 622 }, "document": { - "ownir_version": 0, + "ownir_version": 1, "module": "Door", "components": [], "_doc": [ @@ -64,19 +64,19 @@ "id": "python-ownlang", "consumed": { "algorithm": "sha256", - "digest": "1709103f7676c80ef52d6d0ad42ce4040842a61f6b63f55a5b133620e778b069", + "digest": "c28447881206b741a6c80335c945308cbbf16b8eb7a44e6490a44e1c17a9ab05", "bytes": 948 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "Door", "resources": [ { @@ -180,7 +180,7 @@ "document": { "degraded": null, "module": "Door", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -232,7 +232,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "2d8c03c893da95c8f84ac51786a70f37c10cefb5fa060ca9acf513638f0b1a27", + "digest": "77b36d9af18fb0d999b5c37a36ea82da135413dddb3f4a1ae9cda738736e46fc", "bytes": 1299 } } @@ -242,7 +242,7 @@ "id": "rust-own-bridge", "consumed": { "algorithm": "sha256", - "digest": "1709103f7676c80ef52d6d0ad42ce4040842a61f6b63f55a5b133620e778b069", + "digest": "c28447881206b741a6c80335c945308cbbf16b8eb7a44e6490a44e1c17a9ab05", "bytes": 948 }, "layers": [ diff --git a/tests/fixtures/repro/verdict_door_effect_deps_not_strings.trace.json b/tests/fixtures/repro/verdict_door_effect_deps_not_strings.trace.json index 1b843568..060b9146 100644 --- a/tests/fixtures/repro/verdict_door_effect_deps_not_strings.trace.json +++ b/tests/fixtures/repro/verdict_door_effect_deps_not_strings.trace.json @@ -7,7 +7,7 @@ "engine": "python-ownlang", "input": { "algorithm": "sha256", - "digest": "9ac5b570e4a2956f2d771aa0cc5de5e1a7584683d0feefe453df6da4d5007c7b", + "digest": "b85df11e6d8bd400d38b209009a3517bce2bce543e65fd2faac863dcfd057cf1", "bytes": 622 }, "layers": [ @@ -21,7 +21,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -147,7 +147,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", @@ -206,7 +206,7 @@ "engine": "rust-own-bridge", "input": { "algorithm": "sha256", - "digest": "9ac5b570e4a2956f2d771aa0cc5de5e1a7584683d0feefe453df6da4d5007c7b", + "digest": "b85df11e6d8bd400d38b209009a3517bce2bce543e65fd2faac863dcfd057cf1", "bytes": 622 }, "layers": [ diff --git a/tests/fixtures/repro/verdict_door_service_unknown_lifetime.repro.json b/tests/fixtures/repro/verdict_door_service_unknown_lifetime.repro.json index 1b688f3a..0d4f951f 100644 --- a/tests/fixtures/repro/verdict_door_service_unknown_lifetime.repro.json +++ b/tests/fixtures/repro/verdict_door_service_unknown_lifetime.repro.json @@ -1,20 +1,20 @@ { "repro_version": 3, "input": { - "ownir_version": 0, + "ownir_version": 1, "raw": { "algorithm": "sha256", - "digest": "264d8b7e5005c0fae9988871dc82e2c366dec9059c2e6532d9f15044684fd095", + "digest": "a354246b1cf569608b3741390343cca8b9fab03471af9a176ca026a6b8359b0e", "bytes": 759, - "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMCwKICAibW9kdWxlIjogIkRvb3IiLAogICJjb21wb25lbnRzIjogW10sCiAgIl9kb2MiOiBbCiAgICAiIzI5NCBPRC0xOiBhbiB1bmtub3duIERJIGxpZmV0aW1lIGlzIElHTk9SRUQgYnkgdGhlIHJlZmVyZW5jZSBmaW5kZXJzIG9uIHRoZSIsCiAgICAidG9sZXJhbnQgZG9vciAoTXlzdGVyeSBpcyBuZWl0aGVyIHNjb3BlZCBub3IgdHJhbnNpZW50LCBzbyBBcHAgLT4gRGIgaXMgdGhlIiwKICAgICJvbmx5IERJMDAxKTsgdGhlIHR5cGVkIFJ1c3QgZG9vciByZWZ1c2VzIHRoZSBkb2N1bWVudCBhdCB0aGUgbGlmZXRpbWUgZW51bS4iCiAgXSwKICAic2VydmljZXMiOiBbCiAgICB7CiAgICAgICJuYW1lIjogIkFwcCIsCiAgICAgICJsaWZldGltZSI6ICJzaW5nbGV0b24iLAogICAgICAiZmlsZSI6ICJyZWcuY3MiLAogICAgICAibGluZSI6IDUsCiAgICAgICJkZXBzIjogWwogICAgICAgICJNeXN0ZXJ5IiwKICAgICAgICAiRGIiCiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJuYW1lIjogIk15c3RlcnkiLAogICAgICAibGlmZXRpbWUiOiAicHJvdG90eXBlIiwKICAgICAgImZpbGUiOiAicmVnLmNzIiwKICAgICAgImxpbmUiOiA2LAogICAgICAiZGVwcyI6IFtdCiAgICB9LAogICAgewogICAgICAibmFtZSI6ICJEYiIsCiAgICAgICJsaWZldGltZSI6ICJzY29wZWQiLAogICAgICAiZmlsZSI6ICJyZWcuY3MiLAogICAgICAibGluZSI6IDcsCiAgICAgICJkZXBzIjogW10KICAgIH0KICBdCn0K" + "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMSwKICAibW9kdWxlIjogIkRvb3IiLAogICJjb21wb25lbnRzIjogW10sCiAgIl9kb2MiOiBbCiAgICAiIzI5NCBPRC0xOiBhbiB1bmtub3duIERJIGxpZmV0aW1lIGlzIElHTk9SRUQgYnkgdGhlIHJlZmVyZW5jZSBmaW5kZXJzIG9uIHRoZSIsCiAgICAidG9sZXJhbnQgZG9vciAoTXlzdGVyeSBpcyBuZWl0aGVyIHNjb3BlZCBub3IgdHJhbnNpZW50LCBzbyBBcHAgLT4gRGIgaXMgdGhlIiwKICAgICJvbmx5IERJMDAxKTsgdGhlIHR5cGVkIFJ1c3QgZG9vciByZWZ1c2VzIHRoZSBkb2N1bWVudCBhdCB0aGUgbGlmZXRpbWUgZW51bS4iCiAgXSwKICAic2VydmljZXMiOiBbCiAgICB7CiAgICAgICJuYW1lIjogIkFwcCIsCiAgICAgICJsaWZldGltZSI6ICJzaW5nbGV0b24iLAogICAgICAiZmlsZSI6ICJyZWcuY3MiLAogICAgICAibGluZSI6IDUsCiAgICAgICJkZXBzIjogWwogICAgICAgICJNeXN0ZXJ5IiwKICAgICAgICAiRGIiCiAgICAgIF0KICAgIH0sCiAgICB7CiAgICAgICJuYW1lIjogIk15c3RlcnkiLAogICAgICAibGlmZXRpbWUiOiAicHJvdG90eXBlIiwKICAgICAgImZpbGUiOiAicmVnLmNzIiwKICAgICAgImxpbmUiOiA2LAogICAgICAiZGVwcyI6IFtdCiAgICB9LAogICAgewogICAgICAibmFtZSI6ICJEYiIsCiAgICAgICJsaWZldGltZSI6ICJzY29wZWQiLAogICAgICAiZmlsZSI6ICJyZWcuY3MiLAogICAgICAibGluZSI6IDcsCiAgICAgICJkZXBzIjogW10KICAgIH0KICBdCn0K" }, "canonical": { "algorithm": "sha256", - "digest": "f812c4f014338788c67b3e3f29f035d074d84714f048b0ff8b696b57432a3752", + "digest": "e7820a76b22a9c562e7533d3f429ee16535c85bef592209d4466dcfa9d83f613", "bytes": 541 }, "document": { - "ownir_version": 0, + "ownir_version": 1, "module": "Door", "components": [], "_doc": [ @@ -55,19 +55,19 @@ "id": "python-ownlang", "consumed": { "algorithm": "sha256", - "digest": "264d8b7e5005c0fae9988871dc82e2c366dec9059c2e6532d9f15044684fd095", + "digest": "a354246b1cf569608b3741390343cca8b9fab03471af9a176ca026a6b8359b0e", "bytes": 759 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, "status": "produced", "document": { - "lowered_version": 1, + "lowered_version": 2, "module": "Door", "resources": [ { @@ -171,7 +171,7 @@ "document": { "degraded": null, "module": "Door", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } @@ -223,7 +223,7 @@ "status": "produced", "canonical": { "algorithm": "sha256", - "digest": "b129618f92150bfc7c3215303bb8506421fcebd05d43c309cde72b0c48ce9799", + "digest": "6a55b516f689e0269a5e32b5c418e91e9c365aaefd913769af9fc636fb92d600", "bytes": 1078 } } @@ -233,7 +233,7 @@ "id": "rust-own-bridge", "consumed": { "algorithm": "sha256", - "digest": "264d8b7e5005c0fae9988871dc82e2c366dec9059c2e6532d9f15044684fd095", + "digest": "a354246b1cf569608b3741390343cca8b9fab03471af9a176ca026a6b8359b0e", "bytes": 759 }, "layers": [ diff --git a/tests/fixtures/repro/verdict_door_service_unknown_lifetime.trace.json b/tests/fixtures/repro/verdict_door_service_unknown_lifetime.trace.json index 4b312ab6..444043f3 100644 --- a/tests/fixtures/repro/verdict_door_service_unknown_lifetime.trace.json +++ b/tests/fixtures/repro/verdict_door_service_unknown_lifetime.trace.json @@ -7,7 +7,7 @@ "engine": "python-ownlang", "input": { "algorithm": "sha256", - "digest": "f812c4f014338788c67b3e3f29f035d074d84714f048b0ff8b696b57432a3752", + "digest": "e7820a76b22a9c562e7533d3f429ee16535c85bef592209d4466dcfa9d83f613", "bytes": 541 }, "layers": [ @@ -21,7 +21,7 @@ "steps": [ { "id": "lowered_version", - "value": 1 + "value": 2 }, { "id": "module", @@ -147,7 +147,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", @@ -206,7 +206,7 @@ "engine": "rust-own-bridge", "input": { "algorithm": "sha256", - "digest": "f812c4f014338788c67b3e3f29f035d074d84714f048b0ff8b696b57432a3752", + "digest": "e7820a76b22a9c562e7533d3f429ee16535c85bef592209d4466dcfa9d83f613", "bytes": 541 }, "layers": [ diff --git a/tests/fixtures/repro/vocab_unknown_op.repro.json b/tests/fixtures/repro/vocab_unknown_op.repro.json index ad1aebc7..8cd2c045 100644 --- a/tests/fixtures/repro/vocab_unknown_op.repro.json +++ b/tests/fixtures/repro/vocab_unknown_op.repro.json @@ -1,20 +1,20 @@ { "repro_version": 3, "input": { - "ownir_version": 0, + "ownir_version": 1, "raw": { "algorithm": "sha256", - "digest": "cdf50bec31cb870daa324b5ee37db4ad504aa499eaa7bafbe8c3c35a5a48eede", + "digest": "91e2d42aad1ded1dc0ae052a2dd3b66ab098761709514dd7f7711408015f759d", "bytes": 208, - "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMCwKICAibW9kdWxlIjogIlZvY2FiIiwKICAiZnVuY3Rpb25zIjogWwogICAgewogICAgICAibmFtZSI6ICJNIiwKICAgICAgImZpbGUiOiAiRi5jcyIsCiAgICAgICJib2R5IjogWwogICAgICAgIHsKICAgICAgICAgICJvcCI6ICJ0cnkiLAogICAgICAgICAgImxpbmUiOiAyCiAgICAgICAgfQogICAgICBdCiAgICB9CiAgXQp9Cg==" + "base64": "ewogICJvd25pcl92ZXJzaW9uIjogMSwKICAibW9kdWxlIjogIlZvY2FiIiwKICAiZnVuY3Rpb25zIjogWwogICAgewogICAgICAibmFtZSI6ICJNIiwKICAgICAgImZpbGUiOiAiRi5jcyIsCiAgICAgICJib2R5IjogWwogICAgICAgIHsKICAgICAgICAgICJvcCI6ICJ0cnkiLAogICAgICAgICAgImxpbmUiOiAyCiAgICAgICAgfQogICAgICBdCiAgICB9CiAgXQp9Cg==" }, "canonical": { "algorithm": "sha256", - "digest": "c07494fb952154bcb0f026969754a20bf856c5c12b54a1c9b2aca0468315838d", + "digest": "866a9d0b4bf4898bbcb2a52b9522201efea52c0a0ff7a75ee2963ad4c88851a9", "bytes": 108 }, "document": { - "ownir_version": 0, + "ownir_version": 1, "module": "Vocab", "functions": [ { @@ -35,13 +35,13 @@ "id": "python-ownlang", "consumed": { "algorithm": "sha256", - "digest": "cdf50bec31cb870daa324b5ee37db4ad504aa499eaa7bafbe8c3c35a5a48eede", + "digest": "91e2d42aad1ded1dc0ae052a2dd3b66ab098761709514dd7f7711408015f759d", "bytes": 208 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, @@ -58,7 +58,7 @@ "document": { "degraded": null, "module": "Vocab", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", @@ -96,13 +96,13 @@ "id": "rust-own-bridge", "consumed": { "algorithm": "sha256", - "digest": "cdf50bec31cb870daa324b5ee37db4ad504aa499eaa7bafbe8c3c35a5a48eede", + "digest": "91e2d42aad1ded1dc0ae052a2dd3b66ab098761709514dd7f7711408015f759d", "bytes": 208 }, "layers": [ { "layer": "lowered", - "surface_version": 1, + "surface_version": 2, "projection": { "kind": "full" }, @@ -119,7 +119,7 @@ "document": { "degraded": null, "module": "Vocab", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/repro/vocab_unknown_op.trace.json b/tests/fixtures/repro/vocab_unknown_op.trace.json index d3fe88f1..105344a1 100644 --- a/tests/fixtures/repro/vocab_unknown_op.trace.json +++ b/tests/fixtures/repro/vocab_unknown_op.trace.json @@ -7,7 +7,7 @@ "engine": "python-ownlang", "input": { "algorithm": "sha256", - "digest": "c07494fb952154bcb0f026969754a20bf856c5c12b54a1c9b2aca0468315838d", + "digest": "866a9d0b4bf4898bbcb2a52b9522201efea52c0a0ff7a75ee2963ad4c88851a9", "bytes": 108 }, "layers": [ @@ -36,7 +36,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", @@ -75,7 +75,7 @@ "engine": "rust-own-bridge", "input": { "algorithm": "sha256", - "digest": "c07494fb952154bcb0f026969754a20bf856c5c12b54a1c9b2aca0468315838d", + "digest": "866a9d0b4bf4898bbcb2a52b9522201efea52c0a0ff7a75ee2963ad4c88851a9", "bytes": 108 }, "layers": [ @@ -104,7 +104,7 @@ }, { "id": "ownir_version", - "value": 0 + "value": 1 }, { "id": "degraded", diff --git a/tests/fixtures/summaries/alias_join_cases.summaries.json b/tests/fixtures/summaries/alias_join_cases.summaries.json index eda3733e..b7bd6861 100644 --- a/tests/fixtures/summaries/alias_join_cases.summaries.json +++ b/tests/fixtures/summaries/alias_join_cases.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Alias", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/flow_kill_on_rebind.summaries.json b/tests/fixtures/summaries/flow_kill_on_rebind.summaries.json index da03ea93..bd6304b4 100644 --- a/tests/fixtures/summaries/flow_kill_on_rebind.summaries.json +++ b/tests/fixtures/summaries/flow_kill_on_rebind.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Rebind", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/flow_unmapped_refs.summaries.json b/tests/fixtures/summaries/flow_unmapped_refs.summaries.json index 9f741fa7..6372f1c7 100644 --- a/tests/fixtures/summaries/flow_unmapped_refs.summaries.json +++ b/tests/fixtures/summaries/flow_unmapped_refs.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Unmapped", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/fn_params_ordering.summaries.json b/tests/fixtures/summaries/fn_params_ordering.summaries.json index 7aae7b6d..faea3751 100644 --- a/tests/fixtures/summaries/fn_params_ordering.summaries.json +++ b/tests/fixtures/summaries/fn_params_ordering.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Params", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/handles_global_counters.summaries.json b/tests/fixtures/summaries/handles_global_counters.summaries.json index 2b544d52..ddf15b9e 100644 --- a/tests/fixtures/summaries/handles_global_counters.summaries.json +++ b/tests/fixtures/summaries/handles_global_counters.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Counters", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } diff --git a/tests/fixtures/summaries/handles_null_metadata.summaries.json b/tests/fixtures/summaries/handles_null_metadata.summaries.json index 87650e56..d5f8e43d 100644 --- a/tests/fixtures/summaries/handles_null_metadata.summaries.json +++ b/tests/fixtures/summaries/handles_null_metadata.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "NullMeta", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } diff --git a/tests/fixtures/summaries/hoist_neg_early_return.summaries.json b/tests/fixtures/summaries/hoist_neg_early_return.summaries.json index 0317f80e..7f6d31ca 100644 --- a/tests/fixtures/summaries/hoist_neg_early_return.summaries.json +++ b/tests/fixtures/summaries/hoist_neg_early_return.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "HoistNeg3", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/hoist_neg_nested_depth.summaries.json b/tests/fixtures/summaries/hoist_neg_nested_depth.summaries.json index 451932a3..2516c762 100644 --- a/tests/fixtures/summaries/hoist_neg_nested_depth.summaries.json +++ b/tests/fixtures/summaries/hoist_neg_nested_depth.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "HoistNeg1", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/hoist_neg_while_body.summaries.json b/tests/fixtures/summaries/hoist_neg_while_body.summaries.json index 4c91de72..e797ab77 100644 --- a/tests/fixtures/summaries/hoist_neg_while_body.summaries.json +++ b/tests/fixtures/summaries/hoist_neg_while_body.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "HoistNeg2", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/hoist_pool_kind.summaries.json b/tests/fixtures/summaries/hoist_pool_kind.summaries.json index 20699714..5416327d 100644 --- a/tests/fixtures/summaries/hoist_pool_kind.summaries.json +++ b/tests/fixtures/summaries/hoist_pool_kind.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "HoistPool", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/hoist_positive_release.summaries.json b/tests/fixtures/summaries/hoist_positive_release.summaries.json index afb77def..b7dff134 100644 --- a/tests/fixtures/summaries/hoist_positive_release.summaries.json +++ b/tests/fixtures/summaries/hoist_positive_release.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Hoist", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/hoist_positive_use_only.summaries.json b/tests/fixtures/summaries/hoist_positive_use_only.summaries.json index 0a6f8c13..901d7d88 100644 --- a/tests/fixtures/summaries/hoist_positive_use_only.summaries.json +++ b/tests/fixtures/summaries/hoist_positive_use_only.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "HoistUse", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/lines_preserved.summaries.json b/tests/fixtures/summaries/lines_preserved.summaries.json index 43141d2a..010bf551 100644 --- a/tests/fixtures/summaries/lines_preserved.summaries.json +++ b/tests/fixtures/summaries/lines_preserved.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Lines", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/manifest.json b/tests/fixtures/summaries/manifest.json index b501515a..bcf13a4e 100644 --- a/tests/fixtures/summaries/manifest.json +++ b/tests/fixtures/summaries/manifest.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "excluded_lowered": [ { "name": "tolerant_unknown_kind", diff --git a/tests/fixtures/summaries/mos_call_channel_overload_sig.summaries.json b/tests/fixtures/summaries/mos_call_channel_overload_sig.summaries.json index 33999609..244d88e2 100644 --- a/tests/fixtures/summaries/mos_call_channel_overload_sig.summaries.json +++ b/tests/fixtures/summaries/mos_call_channel_overload_sig.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Overloads", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/mos_call_direct_consume.summaries.json b/tests/fixtures/summaries/mos_call_direct_consume.summaries.json index 70ccc5e7..5c8e83f6 100644 --- a/tests/fixtures/summaries/mos_call_direct_consume.summaries.json +++ b/tests/fixtures/summaries/mos_call_direct_consume.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Direct", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/mos_call_unknown_drop.summaries.json b/tests/fixtures/summaries/mos_call_unknown_drop.summaries.json index 7641318e..ad06c667 100644 --- a/tests/fixtures/summaries/mos_call_unknown_drop.summaries.json +++ b/tests/fixtures/summaries/mos_call_unknown_drop.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Drop", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/mos_fresh_mint.summaries.json b/tests/fixtures/summaries/mos_fresh_mint.summaries.json index 901bce6e..4c4022b5 100644 --- a/tests/fixtures/summaries/mos_fresh_mint.summaries.json +++ b/tests/fixtures/summaries/mos_fresh_mint.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Fresh", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/mos_killsite_toplevel.summaries.json b/tests/fixtures/summaries/mos_killsite_toplevel.summaries.json index 8fb5b39c..95e48b59 100644 --- a/tests/fixtures/summaries/mos_killsite_toplevel.summaries.json +++ b/tests/fixtures/summaries/mos_killsite_toplevel.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "KillSite", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/mos_untrack_inbranch.summaries.json b/tests/fixtures/summaries/mos_untrack_inbranch.summaries.json index b48ee3f9..5a470b55 100644 --- a/tests/fixtures/summaries/mos_untrack_inbranch.summaries.json +++ b/tests/fixtures/summaries/mos_untrack_inbranch.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Untrack", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/summaries/mosdump_degraded_duplicate_key.facts.json b/tests/fixtures/summaries/mosdump_degraded_duplicate_key.facts.json index 341995c0..ec928b44 100644 --- a/tests/fixtures/summaries/mosdump_degraded_duplicate_key.facts.json +++ b/tests/fixtures/summaries/mosdump_degraded_duplicate_key.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Degraded", "functions": [ { diff --git a/tests/fixtures/summaries/mosdump_degraded_duplicate_key.summaries.json b/tests/fixtures/summaries/mosdump_degraded_duplicate_key.summaries.json index 9e783bcc..338c50fa 100644 --- a/tests/fixtures/summaries/mosdump_degraded_duplicate_key.summaries.json +++ b/tests/fixtures/summaries/mosdump_degraded_duplicate_key.summaries.json @@ -1,7 +1,7 @@ { "degraded": "ValueError: duplicate MethodSkeleton key: Take(System.IO.Stream)", "module": "Degraded", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } diff --git a/tests/fixtures/summaries/mosdump_explicit_effects.facts.json b/tests/fixtures/summaries/mosdump_explicit_effects.facts.json index e0801598..19f8d8e0 100644 --- a/tests/fixtures/summaries/mosdump_explicit_effects.facts.json +++ b/tests/fixtures/summaries/mosdump_explicit_effects.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Effects", "functions": [ { diff --git a/tests/fixtures/summaries/mosdump_explicit_effects.summaries.json b/tests/fixtures/summaries/mosdump_explicit_effects.summaries.json index 159a7fe9..780acf19 100644 --- a/tests/fixtures/summaries/mosdump_explicit_effects.summaries.json +++ b/tests/fixtures/summaries/mosdump_explicit_effects.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Effects", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "ef.cs", diff --git a/tests/fixtures/summaries/mosdump_nonascii_escaping.facts.json b/tests/fixtures/summaries/mosdump_nonascii_escaping.facts.json index 848b9b08..d36243b5 100644 --- a/tests/fixtures/summaries/mosdump_nonascii_escaping.facts.json +++ b/tests/fixtures/summaries/mosdump_nonascii_escaping.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Юникод—М😀", "functions": [ { diff --git a/tests/fixtures/summaries/mosdump_nonascii_escaping.summaries.json b/tests/fixtures/summaries/mosdump_nonascii_escaping.summaries.json index 2e68de6a..5a21503b 100644 --- a/tests/fixtures/summaries/mosdump_nonascii_escaping.summaries.json +++ b/tests/fixtures/summaries/mosdump_nonascii_escaping.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "\u042e\u043d\u0438\u043a\u043e\u0434\u2014\u041c\ud83d\ude00", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "\u0444\u0430\u0439\u043b\ud83d\ude00.cs", diff --git a/tests/fixtures/summaries/mosdump_overload_merge_tiebreaks.facts.json b/tests/fixtures/summaries/mosdump_overload_merge_tiebreaks.facts.json index f76f3169..06ef382a 100644 --- a/tests/fixtures/summaries/mosdump_overload_merge_tiebreaks.facts.json +++ b/tests/fixtures/summaries/mosdump_overload_merge_tiebreaks.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "MergeTie", "functions": [ { diff --git a/tests/fixtures/summaries/mosdump_overload_merge_tiebreaks.summaries.json b/tests/fixtures/summaries/mosdump_overload_merge_tiebreaks.summaries.json index e540bba6..eb8c2f13 100644 --- a/tests/fixtures/summaries/mosdump_overload_merge_tiebreaks.summaries.json +++ b/tests/fixtures/summaries/mosdump_overload_merge_tiebreaks.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "MergeTie", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "a.cs", diff --git a/tests/fixtures/summaries/mosdump_partial_and_guarded.facts.json b/tests/fixtures/summaries/mosdump_partial_and_guarded.facts.json index ae488676..01f01310 100644 --- a/tests/fixtures/summaries/mosdump_partial_and_guarded.facts.json +++ b/tests/fixtures/summaries/mosdump_partial_and_guarded.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Guarded", "functions": [ { diff --git a/tests/fixtures/summaries/mosdump_partial_and_guarded.summaries.json b/tests/fixtures/summaries/mosdump_partial_and_guarded.summaries.json index ef87eff4..679a66c8 100644 --- a/tests/fixtures/summaries/mosdump_partial_and_guarded.summaries.json +++ b/tests/fixtures/summaries/mosdump_partial_and_guarded.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Guarded", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "pg.cs", diff --git a/tests/fixtures/summaries/mosdump_return_chains.facts.json b/tests/fixtures/summaries/mosdump_return_chains.facts.json index b9d4304a..44d7b143 100644 --- a/tests/fixtures/summaries/mosdump_return_chains.facts.json +++ b/tests/fixtures/summaries/mosdump_return_chains.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "ReturnChains", "functions": [ { diff --git a/tests/fixtures/summaries/mosdump_return_chains.summaries.json b/tests/fixtures/summaries/mosdump_return_chains.summaries.json index 9f026dfa..254f649d 100644 --- a/tests/fixtures/summaries/mosdump_return_chains.summaries.json +++ b/tests/fixtures/summaries/mosdump_return_chains.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "ReturnChains", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "cyc.cs", diff --git a/tests/fixtures/summaries/mosdump_scc_cycles.facts.json b/tests/fixtures/summaries/mosdump_scc_cycles.facts.json index b0ab94e2..65d4fa79 100644 --- a/tests/fixtures/summaries/mosdump_scc_cycles.facts.json +++ b/tests/fixtures/summaries/mosdump_scc_cycles.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "SccCycles", "functions": [ { diff --git a/tests/fixtures/summaries/mosdump_scc_cycles.summaries.json b/tests/fixtures/summaries/mosdump_scc_cycles.summaries.json index d314be74..56341b0c 100644 --- a/tests/fixtures/summaries/mosdump_scc_cycles.summaries.json +++ b/tests/fixtures/summaries/mosdump_scc_cycles.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "SccCycles", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "cy.cs", diff --git a/tests/fixtures/summaries/mosdump_sig_vocabulary.facts.json b/tests/fixtures/summaries/mosdump_sig_vocabulary.facts.json index b8213acf..6290f4af 100644 --- a/tests/fixtures/summaries/mosdump_sig_vocabulary.facts.json +++ b/tests/fixtures/summaries/mosdump_sig_vocabulary.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "SigVocab", "functions": [ { diff --git a/tests/fixtures/summaries/mosdump_sig_vocabulary.summaries.json b/tests/fixtures/summaries/mosdump_sig_vocabulary.summaries.json index 7e1db564..9d76cb7e 100644 --- a/tests/fixtures/summaries/mosdump_sig_vocabulary.summaries.json +++ b/tests/fixtures/summaries/mosdump_sig_vocabulary.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "SigVocab", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "f.cs", diff --git a/tests/fixtures/summaries/mosdump_sink_channels.facts.json b/tests/fixtures/summaries/mosdump_sink_channels.facts.json index cb5cebfe..dca63604 100644 --- a/tests/fixtures/summaries/mosdump_sink_channels.facts.json +++ b/tests/fixtures/summaries/mosdump_sink_channels.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Sinks", "functions": [ { diff --git a/tests/fixtures/summaries/mosdump_sink_channels.summaries.json b/tests/fixtures/summaries/mosdump_sink_channels.summaries.json index 144e0edc..00e2bc77 100644 --- a/tests/fixtures/summaries/mosdump_sink_channels.summaries.json +++ b/tests/fixtures/summaries/mosdump_sink_channels.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Sinks", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "sk.cs", diff --git a/tests/fixtures/summaries/routing_r1_unresolved.summaries.json b/tests/fixtures/summaries/routing_r1_unresolved.summaries.json index 0cd0417f..1fd45b51 100644 --- a/tests/fixtures/summaries/routing_r1_unresolved.summaries.json +++ b/tests/fixtures/summaries/routing_r1_unresolved.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "R1", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } diff --git a/tests/fixtures/summaries/routing_r2_subscribe_self.summaries.json b/tests/fixtures/summaries/routing_r2_subscribe_self.summaries.json index b9f992f0..3c80d2aa 100644 --- a/tests/fixtures/summaries/routing_r2_subscribe_self.summaries.json +++ b/tests/fixtures/summaries/routing_r2_subscribe_self.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "R2", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } diff --git a/tests/fixtures/summaries/routing_r3_capture_static.summaries.json b/tests/fixtures/summaries/routing_r3_capture_static.summaries.json index 0e379113..15436bba 100644 --- a/tests/fixtures/summaries/routing_r3_capture_static.summaries.json +++ b/tests/fixtures/summaries/routing_r3_capture_static.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "R3", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } diff --git a/tests/fixtures/summaries/routing_r4_returned_fresh.summaries.json b/tests/fixtures/summaries/routing_r4_returned_fresh.summaries.json index 25eed67b..a395834e 100644 --- a/tests/fixtures/summaries/routing_r4_returned_fresh.summaries.json +++ b/tests/fixtures/summaries/routing_r4_returned_fresh.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "R4", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } diff --git a/tests/fixtures/summaries/routing_r5_di_capture.summaries.json b/tests/fixtures/summaries/routing_r5_di_capture.summaries.json index f186c178..17194997 100644 --- a/tests/fixtures/summaries/routing_r5_di_capture.summaries.json +++ b/tests/fixtures/summaries/routing_r5_di_capture.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "R5", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } diff --git a/tests/fixtures/summaries/routing_r6_token_kinds.summaries.json b/tests/fixtures/summaries/routing_r6_token_kinds.summaries.json index 27e7f1de..74811789 100644 --- a/tests/fixtures/summaries/routing_r6_token_kinds.summaries.json +++ b/tests/fixtures/summaries/routing_r6_token_kinds.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "R6", - "ownir_version": 0, + "ownir_version": 1, "summaries": [], "unresolved": [] } diff --git a/tests/fixtures/summaries/typestate_cs_a1_alias_of_entity.summaries.json b/tests/fixtures/summaries/typestate_cs_a1_alias_of_entity.summaries.json new file mode 100644 index 00000000..66ecf19d --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_a1_alias_of_entity.summaries.json @@ -0,0 +1,41 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/A1_alias_of_entity.cs", + "line": 0, + "method": "Own.Protocols.Cases.A1AliasOfEntity.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_a2_entity_through_indexer.summaries.json b/tests/fixtures/summaries/typestate_cs_a2_entity_through_indexer.summaries.json new file mode 100644 index 00000000..1ad05239 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_a2_entity_through_indexer.summaries.json @@ -0,0 +1,41 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/A2_entity_through_indexer.cs", + "line": 0, + "method": "Own.Protocols.Cases.A2EntityThroughIndexer.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_g6_capability_not_spent.summaries.json b/tests/fixtures/summaries/typestate_cs_g6_capability_not_spent.summaries.json new file mode 100644 index 00000000..8f95b7e2 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_g6_capability_not_spent.summaries.json @@ -0,0 +1,18 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/G6_capability_not_spent.cs", + "line": 0, + "method": "Own.Protocols.Cases.G6CapabilityNotSpent.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l10_region_inside_try.summaries.json b/tests/fixtures/summaries/typestate_cs_l10_region_inside_try.summaries.json new file mode 100644 index 00000000..d51857e9 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l10_region_inside_try.summaries.json @@ -0,0 +1,41 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L10_region_inside_try.cs", + "line": 0, + "method": "Own.Protocols.Cases.L10RegionInsideTry.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l11_region_beside_disposable.summaries.json b/tests/fixtures/summaries/typestate_cs_l11_region_beside_disposable.summaries.json new file mode 100644 index 00000000..1f17c842 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l11_region_beside_disposable.summaries.json @@ -0,0 +1,51 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L11_region_beside_disposable.cs", + "line": 0, + "method": "Own.Protocols.Cases.L11RegionBesideDisposable.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/cases/L11_region_beside_disposable.cs", + "line": 0, + "method": "Own.Protocols.Cases.L11RegionBesideDisposable.Run$protocol", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l12_region_beside_leaked_disposable.summaries.json b/tests/fixtures/summaries/typestate_cs_l12_region_beside_leaked_disposable.summaries.json new file mode 100644 index 00000000..b609e4a5 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l12_region_beside_leaked_disposable.summaries.json @@ -0,0 +1,51 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs", + "line": 0, + "method": "Own.Protocols.Cases.L12RegionBesideLeakedDisposable.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs", + "line": 0, + "method": "Own.Protocols.Cases.L12RegionBesideLeakedDisposable.Run$protocol", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l13_values_captured_into_region.summaries.json b/tests/fixtures/summaries/typestate_cs_l13_values_captured_into_region.summaries.json new file mode 100644 index 00000000..a27019a2 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l13_values_captured_into_region.summaries.json @@ -0,0 +1,41 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L13_values_captured_into_region.cs", + "line": 0, + "method": "Own.Protocols.Cases.L13ValuesCapturedIntoRegion.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l1_stale_capability.summaries.json b/tests/fixtures/summaries/typestate_cs_l1_stale_capability.summaries.json new file mode 100644 index 00000000..0a18d7b4 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l1_stale_capability.summaries.json @@ -0,0 +1,87 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs", + "line": 0, + "method": "Own.Protocols.Cases.L1StaleCapability.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.DraftOrder.Cancel", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.DraftOrder.Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.SubmittedOrder.Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l2_copied_capability.summaries.json b/tests/fixtures/summaries/typestate_cs_l2_copied_capability.summaries.json new file mode 100644 index 00000000..9a093a97 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l2_copied_capability.summaries.json @@ -0,0 +1,41 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L2_copied_capability.cs", + "line": 0, + "method": "Own.Protocols.Cases.L2CopiedCapability.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.DraftOrder.Cancel", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l3_raw_mutation.summaries.json b/tests/fixtures/summaries/typestate_cs_l3_raw_mutation.summaries.json new file mode 100644 index 00000000..ee502f80 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l3_raw_mutation.summaries.json @@ -0,0 +1,41 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L3_raw_mutation.cs", + "line": 0, + "method": "Own.Protocols.Cases.L3RawMutation.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l4_raw_escape.summaries.json b/tests/fixtures/summaries/typestate_cs_l4_raw_escape.summaries.json new file mode 100644 index 00000000..cf1196e0 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l4_raw_escape.summaries.json @@ -0,0 +1,41 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L4_raw_escape.cs", + "line": 0, + "method": "Own.Protocols.Cases.L4RawEscape.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l5_normal_transition.summaries.json b/tests/fixtures/summaries/typestate_cs_l5_normal_transition.summaries.json new file mode 100644 index 00000000..7284583f --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l5_normal_transition.summaries.json @@ -0,0 +1,41 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L5_normal_transition.cs", + "line": 0, + "method": "Own.Protocols.Cases.L5NormalTransition.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l6_two_transitions.summaries.json b/tests/fixtures/summaries/typestate_cs_l6_two_transitions.summaries.json new file mode 100644 index 00000000..ebc9b1a0 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l6_two_transitions.summaries.json @@ -0,0 +1,64 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L6_two_transitions.cs", + "line": 0, + "method": "Own.Protocols.Cases.L6TwoTransitions.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.DraftOrder.Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.SubmittedOrder.Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l6b_chained_transitions.summaries.json b/tests/fixtures/summaries/typestate_cs_l6b_chained_transitions.summaries.json new file mode 100644 index 00000000..96c78b68 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l6b_chained_transitions.summaries.json @@ -0,0 +1,64 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L6b_chained_transitions.cs", + "line": 0, + "method": "Own.Protocols.Cases.L6bChainedTransitions.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.DraftOrder.Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.SubmittedOrder.Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l7_read_after_region.summaries.json b/tests/fixtures/summaries/typestate_cs_l7_read_after_region.summaries.json new file mode 100644 index 00000000..c30ede9b --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l7_read_after_region.summaries.json @@ -0,0 +1,41 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L7_read_after_region.cs", + "line": 0, + "method": "Own.Protocols.Cases.L7ReadAfterRegion.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l8_branch_inside_region.summaries.json b/tests/fixtures/summaries/typestate_cs_l8_branch_inside_region.summaries.json new file mode 100644 index 00000000..f96918e0 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l8_branch_inside_region.summaries.json @@ -0,0 +1,41 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L8_branch_inside_region.cs", + "line": 0, + "method": "Own.Protocols.Cases.L8BranchInsideRegion.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_l9_opaque_calls_around_region.summaries.json b/tests/fixtures/summaries/typestate_cs_l9_opaque_calls_around_region.summaries.json new file mode 100644 index 00000000..97cebfc4 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_l9_opaque_calls_around_region.summaries.json @@ -0,0 +1,41 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L9_opaque_calls_around_region.cs", + "line": 0, + "method": "Own.Protocols.Cases.L9OpaqueCallsAroundRegion.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_cs_x1_nested_region_same_entity.summaries.json b/tests/fixtures/summaries/typestate_cs_x1_nested_region_same_entity.summaries.json new file mode 100644 index 00000000..dccd8a12 --- /dev/null +++ b/tests/fixtures/summaries/typestate_cs_x1_nested_region_same_entity.summaries.json @@ -0,0 +1,64 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/cases/X1_nested_region_same_entity.cs", + "line": 0, + "method": "Own.Protocols.Cases.X1NestedRegionSameEntity.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/Api/ProtocolApi.cs", + "line": 0, + "method": "Own.Protocols.Sample.DraftOrder.Cancel", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_ef_orderbackend.summaries.json b/tests/fixtures/summaries/typestate_ef_orderbackend.summaries.json new file mode 100644 index 00000000..62c162bf --- /dev/null +++ b/tests/fixtures/summaries/typestate_ef_orderbackend.summaries.json @@ -0,0 +1,165 @@ +{ + "degraded": null, + "module": "Extracted", + "ownir_version": 1, + "summaries": [ + { + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "line": 0, + "method": "OrderBackend.Domain.ApprovedOrder.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "line": 0, + "method": "OrderBackend.Domain.DraftOrder.Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs", + "line": 0, + "method": "OrderBackend.Domain.SubmittedOrder.Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "token", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "entity", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 0, + "method": "OrderBackend.OrderHandlers.Create", + "params": [ + { + "disposable": true, + "index": 0, + "name": "db", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 0, + "method": "OrderBackend.OrderHandlers.Get", + "params": [ + { + "disposable": true, + "index": 0, + "name": "db", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 0, + "method": "OrderBackend.OrderHandlers.Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "db", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 0, + "method": "OrderBackend.OrderHandlers.Ship$protocol", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 0, + "method": "OrderBackend.OrderHandlers.SubmitAndApprove", + "params": [ + { + "disposable": true, + "index": 0, + "name": "db", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs", + "line": 0, + "method": "OrderBackend.OrderHandlers.SubmitAndApprove$protocol", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_g6_capability_not_spent.summaries.json b/tests/fixtures/summaries/typestate_g6_capability_not_spent.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_g6_capability_not_spent.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_l1_stale_capability.summaries.json b/tests/fixtures/summaries/typestate_l1_stale_capability.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_l1_stale_capability.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_l2_copied_capability.summaries.json b/tests/fixtures/summaries/typestate_l2_copied_capability.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_l2_copied_capability.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_l3_raw_entity_mutation.summaries.json b/tests/fixtures/summaries/typestate_l3_raw_entity_mutation.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_l3_raw_entity_mutation.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_l3b_second_region.summaries.json b/tests/fixtures/summaries/typestate_l3b_second_region.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_l3b_second_region.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_l4_raw_entity_unknown_call.summaries.json b/tests/fixtures/summaries/typestate_l4_raw_entity_unknown_call.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_l4_raw_entity_unknown_call.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_l4b_raw_entity_shared_call.summaries.json b/tests/fixtures/summaries/typestate_l4b_raw_entity_shared_call.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_l4b_raw_entity_shared_call.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_l4c_raw_entity_consumed.summaries.json b/tests/fixtures/summaries/typestate_l4c_raw_entity_consumed.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_l4c_raw_entity_consumed.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_l4d_token_unknown_call.summaries.json b/tests/fixtures/summaries/typestate_l4d_token_unknown_call.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_l4d_token_unknown_call.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_l5_normal_transition.summaries.json b/tests/fixtures/summaries/typestate_l5_normal_transition.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_l5_normal_transition.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_l5b_two_transitions.summaries.json b/tests/fixtures/summaries/typestate_l5b_two_transitions.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_l5b_two_transitions.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_l5c_read_after_region.summaries.json b/tests/fixtures/summaries/typestate_l5c_read_after_region.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_l5c_read_after_region.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_n1_token_spent_on_other_entity.summaries.json b/tests/fixtures/summaries/typestate_n1_token_spent_on_other_entity.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_n1_token_spent_on_other_entity.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_n2_two_tokens_one_region.summaries.json b/tests/fixtures/summaries/typestate_n2_two_tokens_one_region.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_n2_two_tokens_one_region.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_n3_raw_mutation_inferred_effect.summaries.json b/tests/fixtures/summaries/typestate_n3_raw_mutation_inferred_effect.summaries.json new file mode 100644 index 00000000..2d29cfac --- /dev/null +++ b/tests/fixtures/summaries/typestate_n3_raw_mutation_inferred_effect.summaries.json @@ -0,0 +1,178 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Touch", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_n4_token_outlives_region.summaries.json b/tests/fixtures/summaries/typestate_n4_token_outlives_region.summaries.json new file mode 100644 index 00000000..08d2f970 --- /dev/null +++ b/tests/fixtures/summaries/typestate_n4_token_outlives_region.summaries.json @@ -0,0 +1,161 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Orders.cs", + "line": 0, + "method": "Approve", + "params": [ + { + "disposable": true, + "index": 0, + "name": "s", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelDraft", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "CancelOrder", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Handler.Run", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Ship", + "params": [ + { + "disposable": true, + "index": 0, + "name": "a", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "order", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Orders.cs", + "line": 0, + "method": "Submit", + "params": [ + { + "disposable": true, + "index": 0, + "name": "d", + "transfer": "must" + }, + { + "disposable": true, + "index": 1, + "name": "order", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_w1_loan_wordings.summaries.json b/tests/fixtures/summaries/typestate_w1_loan_wordings.summaries.json new file mode 100644 index 00000000..bc5f4df3 --- /dev/null +++ b/tests/fixtures/summaries/typestate_w1_loan_wordings.summaries.json @@ -0,0 +1,119 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Wordings.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "o", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "Reborrow", + "params": [ + { + "disposable": true, + "index": 0, + "name": "o", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "o", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Consumed", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Moved", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Reborrowed", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Released", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Shared", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Used", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/typestate_w2_loan_wordings_pooled.summaries.json b/tests/fixtures/summaries/typestate_w2_loan_wordings_pooled.summaries.json new file mode 100644 index 00000000..bc5f4df3 --- /dev/null +++ b/tests/fixtures/summaries/typestate_w2_loan_wordings_pooled.summaries.json @@ -0,0 +1,119 @@ +{ + "degraded": null, + "module": "TypestateGate", + "ownir_version": 1, + "summaries": [ + { + "file": "Wordings.cs", + "line": 0, + "method": "Peek", + "params": [ + { + "disposable": true, + "index": 0, + "name": "o", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "Reborrow", + "params": [ + { + "disposable": true, + "index": 0, + "name": "o", + "transfer": "no" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "Store", + "params": [ + { + "disposable": true, + "index": 0, + "name": "o", + "transfer": "must" + } + ], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Consumed", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Moved", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Reborrowed", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Released", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Shared", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + }, + { + "file": "Wordings.cs", + "line": 0, + "method": "W.Used", + "params": [], + "returns": { + "owned": "none" + }, + "source": "inferred" + } + ], + "unresolved": [] +} diff --git a/tests/fixtures/summaries/vocab_unknown_op.summaries.json b/tests/fixtures/summaries/vocab_unknown_op.summaries.json index 20400fcc..4394ccd5 100644 --- a/tests/fixtures/summaries/vocab_unknown_op.summaries.json +++ b/tests/fixtures/summaries/vocab_unknown_op.summaries.json @@ -1,7 +1,7 @@ { "degraded": null, "module": "Vocab", - "ownir_version": 0, + "ownir_version": 1, "summaries": [ { "file": "F.cs", diff --git a/tests/fixtures/verdict_renders/render_anchorless.facts.json b/tests/fixtures/verdict_renders/render_anchorless.facts.json index 8b0367ed..18d56b29 100644 --- a/tests/fixtures/verdict_renders/render_anchorless.facts.json +++ b/tests/fixtures/verdict_renders/render_anchorless.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Anchorless", "_doc": [ "OWN052 is anchorless by nature (file '?', line 0), so its SARIF location", diff --git a/tests/fixtures/verdict_renders/render_anchorless.renders.json b/tests/fixtures/verdict_renders/render_anchorless.renders.json index c5f938bf..107cd829 100644 --- a/tests/fixtures/verdict_renders/render_anchorless.renders.json +++ b/tests/fixtures/verdict_renders/render_anchorless.renders.json @@ -51,7 +51,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, @@ -98,7 +98,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, diff --git a/tests/fixtures/verdict_renders/render_columns.facts.json b/tests/fixtures/verdict_renders/render_columns.facts.json index 4e43e2ca..f2d8d920 100644 --- a/tests/fixtures/verdict_renders/render_columns.facts.json +++ b/tests/fixtures/verdict_renders/render_columns.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Columns", "_doc": [ "A reported column rides into SARIF as region.startColumn, and only ever", diff --git a/tests/fixtures/verdict_renders/render_columns.renders.json b/tests/fixtures/verdict_renders/render_columns.renders.json index 8dce3f63..2dbd8259 100644 --- a/tests/fixtures/verdict_renders/render_columns.renders.json +++ b/tests/fixtures/verdict_renders/render_columns.renders.json @@ -59,7 +59,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, @@ -137,7 +137,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, diff --git a/tests/fixtures/verdict_renders/render_empty.facts.json b/tests/fixtures/verdict_renders/render_empty.facts.json index 2f926557..d615097a 100644 --- a/tests/fixtures/verdict_renders/render_empty.facts.json +++ b/tests/fixtures/verdict_renders/render_empty.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Empty", "_doc": [ "A clean document: every line-per-finding surface is an empty list and the", diff --git a/tests/fixtures/verdict_renders/render_empty.renders.json b/tests/fixtures/verdict_renders/render_empty.renders.json index 7acbab5a..e188a4c9 100644 --- a/tests/fixtures/verdict_renders/render_empty.renders.json +++ b/tests/fixtures/verdict_renders/render_empty.renders.json @@ -28,7 +28,7 @@ "informationUri": "https://github.com/PhysShell/Own.NET", "rules": [], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, @@ -47,7 +47,7 @@ "informationUri": "https://github.com/PhysShell/Own.NET", "rules": [], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, diff --git a/tests/fixtures/verdict_renders/render_escaping.facts.json b/tests/fixtures/verdict_renders/render_escaping.facts.json index 98ac6d01..eeec9268 100644 --- a/tests/fixtures/verdict_renders/render_escaping.facts.json +++ b/tests/fixtures/verdict_renders/render_escaping.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Escaping", "_doc": [ "Every workflow-command escaping rule in one document. The component file", diff --git a/tests/fixtures/verdict_renders/render_escaping.renders.json b/tests/fixtures/verdict_renders/render_escaping.renders.json index 2c68acae..db52add2 100644 --- a/tests/fixtures/verdict_renders/render_escaping.renders.json +++ b/tests/fixtures/verdict_renders/render_escaping.renders.json @@ -51,7 +51,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, @@ -103,7 +103,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, diff --git a/tests/fixtures/verdict_renders/render_evidence_slices.facts.json b/tests/fixtures/verdict_renders/render_evidence_slices.facts.json index 60ff49ab..be179c81 100644 --- a/tests/fixtures/verdict_renders/render_evidence_slices.facts.json +++ b/tests/fixtures/verdict_renders/render_evidence_slices.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Evidence", "_doc": [ "The two evidence projections: `related` -> relatedLocations and the ordered", diff --git a/tests/fixtures/verdict_renders/render_evidence_slices.renders.json b/tests/fixtures/verdict_renders/render_evidence_slices.renders.json index 2f1b79c2..a246d37d 100644 --- a/tests/fixtures/verdict_renders/render_evidence_slices.renders.json +++ b/tests/fixtures/verdict_renders/render_evidence_slices.renders.json @@ -59,7 +59,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, @@ -244,7 +244,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, diff --git a/tests/fixtures/verdict_renders/render_protocol_family.facts.json b/tests/fixtures/verdict_renders/render_protocol_family.facts.json index e1ec9058..a5b24fb1 100644 --- a/tests/fixtures/verdict_renders/render_protocol_family.facts.json +++ b/tests/fixtures/verdict_renders/render_protocol_family.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Protocols", "_doc": [ "BR-V9 over the OBL family (#259 checkpoint 4b), two surfaces in one", diff --git a/tests/fixtures/verdict_renders/render_protocol_family.renders.json b/tests/fixtures/verdict_renders/render_protocol_family.renders.json index b8544fd0..ced801e2 100644 --- a/tests/fixtures/verdict_renders/render_protocol_family.renders.json +++ b/tests/fixtures/verdict_renders/render_protocol_family.renders.json @@ -87,7 +87,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, @@ -342,7 +342,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, diff --git a/tests/fixtures/verdict_renders/render_refusal.facts.json b/tests/fixtures/verdict_renders/render_refusal.facts.json index 0b3751c0..65d18472 100644 --- a/tests/fixtures/verdict_renders/render_refusal.facts.json +++ b/tests/fixtures/verdict_renders/render_refusal.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Refusal", "_doc": [ "A document the bridge refuses has no findings to render, so the rendered", diff --git a/tests/fixtures/verdict_renders/render_rule_catalogue.facts.json b/tests/fixtures/verdict_renders/render_rule_catalogue.facts.json index e7a64734..1b509668 100644 --- a/tests/fixtures/verdict_renders/render_rule_catalogue.facts.json +++ b/tests/fixtures/verdict_renders/render_rule_catalogue.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Catalogue", "_doc": [ "The SARIF log's TWO orderings, in a document where they disagree. The", diff --git a/tests/fixtures/verdict_renders/render_rule_catalogue.renders.json b/tests/fixtures/verdict_renders/render_rule_catalogue.renders.json index 584e1d69..aaaa1b79 100644 --- a/tests/fixtures/verdict_renders/render_rule_catalogue.renders.json +++ b/tests/fixtures/verdict_renders/render_rule_catalogue.renders.json @@ -73,7 +73,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, @@ -181,7 +181,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, diff --git a/tests/fixtures/verdict_renders/render_tiers_and_levels.facts.json b/tests/fixtures/verdict_renders/render_tiers_and_levels.facts.json index 31ef0c70..ea10fbf1 100644 --- a/tests/fixtures/verdict_renders/render_tiers_and_levels.facts.json +++ b/tests/fixtures/verdict_renders/render_tiers_and_levels.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Tiers", "_doc": [ "One document carrying all three SARIF levels and both suppression states:", diff --git a/tests/fixtures/verdict_renders/render_tiers_and_levels.renders.json b/tests/fixtures/verdict_renders/render_tiers_and_levels.renders.json index 24753c2f..61e5451c 100644 --- a/tests/fixtures/verdict_renders/render_tiers_and_levels.renders.json +++ b/tests/fixtures/verdict_renders/render_tiers_and_levels.renders.json @@ -81,7 +81,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, @@ -220,7 +220,7 @@ } ], "properties": { - "ownirSchemaVersion": 0 + "ownirSchemaVersion": 1 } } }, diff --git a/tests/fixtures/verdicts/typestate_cs_a1_alias_of_entity.verdicts.json b/tests/fixtures/verdicts/typestate_cs_a1_alias_of_entity.verdicts.json new file mode 100644 index 00000000..f200db6e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_a1_alias_of_entity.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "frontend/roslyn/protocol-samples/cases/A1_alias_of_entity.cs", + "line": 15, + "code": "OWN013", + "component": "Own.Protocols.Cases.A1AliasOfEntity.Run", + "event": "order", + "handler": "", + "message": "IDisposable local 'order' is used directly while an exclusive borrow of it is live", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_cs_a2_entity_through_indexer.verdicts.json b/tests/fixtures/verdicts/typestate_cs_a2_entity_through_indexer.verdicts.json new file mode 100644 index 00000000..9d29c4d2 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_a2_entity_through_indexer.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "frontend/roslyn/protocol-samples/cases/A2_entity_through_indexer.cs", + "line": 15, + "code": "OWN013", + "component": "Own.Protocols.Cases.A2EntityThroughIndexer.Run", + "event": "order", + "handler": "", + "message": "IDisposable local 'order' is used directly while an exclusive borrow of it is live", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_cs_g6_capability_not_spent.verdicts.json b/tests/fixtures/verdicts/typestate_cs_g6_capability_not_spent.verdicts.json new file mode 100644 index 00000000..4dfc0355 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_g6_capability_not_spent.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "frontend/roslyn/protocol-samples/cases/G6_capability_not_spent.cs", + "line": 13, + "code": "OWN001", + "component": "Own.Protocols.Cases.G6CapabilityNotSpent.Run", + "event": "approved", + "handler": "", + "message": "IDisposable local 'approved' is never disposed (leak)", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l10_region_inside_try.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l10_region_inside_try.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l10_region_inside_try.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l11_region_beside_disposable.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l11_region_beside_disposable.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l11_region_beside_disposable.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l12_region_beside_leaked_disposable.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l12_region_beside_leaked_disposable.verdicts.json new file mode 100644 index 00000000..9c24b643 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l12_region_beside_leaked_disposable.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs", + "line": 14, + "code": "OWN001", + "component": "Own.Protocols.Cases.L12RegionBesideLeakedDisposable.Run", + "event": "log", + "handler": "", + "message": "IDisposable local 'log' is never disposed (leak)", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": 13 + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l13_values_captured_into_region.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l13_values_captured_into_region.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l13_values_captured_into_region.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l1_stale_capability.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l1_stale_capability.verdicts.json new file mode 100644 index 00000000..662c6cba --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l1_stale_capability.verdicts.json @@ -0,0 +1,32 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs", + "line": 13, + "code": "OWN002", + "component": "Own.Protocols.Cases.L1StaleCapability.Run", + "event": "draft", + "handler": "", + "message": "IDisposable local 'draft' is used after it is disposed", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [ + [ + "frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs", + 13, + "acquired 'draft' here" + ], + [ + "frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs", + 16, + "used here after it was released/returned" + ] + ], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l2_copied_capability.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l2_copied_capability.verdicts.json new file mode 100644 index 00000000..278c2d18 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l2_copied_capability.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L2_copied_capability.cs", + "line": 13, + "code": "OWN005", + "component": "Own.Protocols.Cases.L2CopiedCapability.Run", + "event": "draft", + "handler": "", + "message": "IDisposable local 'draft' is used after its ownership was moved", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l3_raw_mutation.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l3_raw_mutation.verdicts.json new file mode 100644 index 00000000..03f13c69 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l3_raw_mutation.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L3_raw_mutation.cs", + "line": 13, + "code": "OWN013", + "component": "Own.Protocols.Cases.L3RawMutation.Run", + "event": "order", + "handler": "", + "message": "IDisposable local 'order' is used directly while an exclusive borrow of it is live", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l4_raw_escape.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l4_raw_escape.verdicts.json new file mode 100644 index 00000000..ecaa04e6 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l4_raw_escape.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L4_raw_escape.cs", + "line": 13, + "code": "OWN013", + "component": "Own.Protocols.Cases.L4RawEscape.Run", + "event": "order", + "handler": "", + "message": "IDisposable local 'order' is used directly while an exclusive borrow of it is live", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l5_normal_transition.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l5_normal_transition.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l5_normal_transition.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l6_two_transitions.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l6_two_transitions.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l6_two_transitions.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l6b_chained_transitions.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l6b_chained_transitions.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l6b_chained_transitions.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l7_read_after_region.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l7_read_after_region.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l7_read_after_region.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l8_branch_inside_region.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l8_branch_inside_region.verdicts.json new file mode 100644 index 00000000..4c7bfd16 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l8_branch_inside_region.verdicts.json @@ -0,0 +1,32 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "frontend/roslyn/protocol-samples/cases/L8_branch_inside_region.cs", + "line": 12, + "code": "OWN009", + "component": "Own.Protocols.Cases.L8BranchInsideRegion.Run", + "event": "approved", + "handler": "", + "message": "IDisposable local 'approved' may be used after disposal on some path", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [ + [ + "frontend/roslyn/protocol-samples/cases/L8_branch_inside_region.cs", + 12, + "acquired 'approved' here" + ], + [ + "frontend/roslyn/protocol-samples/cases/L8_branch_inside_region.cs", + 18, + "may be used here after release on some path" + ] + ], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_cs_l9_opaque_calls_around_region.verdicts.json b/tests/fixtures/verdicts/typestate_cs_l9_opaque_calls_around_region.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_l9_opaque_calls_around_region.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_cs_x1_nested_region_same_entity.verdicts.json b/tests/fixtures/verdicts/typestate_cs_x1_nested_region_same_entity.verdicts.json new file mode 100644 index 00000000..ead91307 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_cs_x1_nested_region_same_entity.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "frontend/roslyn/protocol-samples/cases/X1_nested_region_same_entity.cs", + "line": 13, + "code": "OWN011", + "component": "Own.Protocols.Cases.X1NestedRegionSameEntity.Run", + "event": "order", + "handler": "", + "message": "IDisposable local 'order' is exclusively borrowed while an exclusive borrow of it is live", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_ef_orderbackend.verdicts.json b/tests/fixtures/verdicts/typestate_ef_orderbackend.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_ef_orderbackend.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_g6_capability_not_spent.verdicts.json b/tests/fixtures/verdicts/typestate_g6_capability_not_spent.verdicts.json new file mode 100644 index 00000000..51cabe9b --- /dev/null +++ b/tests/fixtures/verdicts/typestate_g6_capability_not_spent.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "Orders.cs", + "line": 12, + "code": "OWN001", + "component": "Handler.Run", + "event": "a", + "handler": "", + "message": "IDisposable local 'a' is never disposed (leak)", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_l1_stale_capability.verdicts.json b/tests/fixtures/verdicts/typestate_l1_stale_capability.verdicts.json new file mode 100644 index 00000000..40549b88 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_l1_stale_capability.verdicts.json @@ -0,0 +1,32 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "Orders.cs", + "line": 12, + "code": "OWN002", + "component": "Handler.Run", + "event": "d", + "handler": "", + "message": "IDisposable local 'd' is used after it is disposed", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [ + [ + "Orders.cs", + 12, + "acquired 'd' here" + ], + [ + "Orders.cs", + 14, + "used here after it was released/returned" + ] + ], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_l2_copied_capability.verdicts.json b/tests/fixtures/verdicts/typestate_l2_copied_capability.verdicts.json new file mode 100644 index 00000000..5d6c8959 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_l2_copied_capability.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "Orders.cs", + "line": 12, + "code": "OWN005", + "component": "Handler.Run", + "event": "d1", + "handler": "", + "message": "IDisposable local 'd1' is used after its ownership was moved", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_l3_raw_entity_mutation.verdicts.json b/tests/fixtures/verdicts/typestate_l3_raw_entity_mutation.verdicts.json new file mode 100644 index 00000000..a5ec0756 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_l3_raw_entity_mutation.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "Orders.cs", + "line": 10, + "code": "OWN011", + "component": "Handler.Run", + "event": "order", + "handler": "", + "message": "IDisposable local 'order' is exclusively borrowed while an exclusive borrow of it is live", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_l3b_second_region.verdicts.json b/tests/fixtures/verdicts/typestate_l3b_second_region.verdicts.json new file mode 100644 index 00000000..a5ec0756 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_l3b_second_region.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "Orders.cs", + "line": 10, + "code": "OWN011", + "component": "Handler.Run", + "event": "order", + "handler": "", + "message": "IDisposable local 'order' is exclusively borrowed while an exclusive borrow of it is live", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_l4_raw_entity_unknown_call.verdicts.json b/tests/fixtures/verdicts/typestate_l4_raw_entity_unknown_call.verdicts.json new file mode 100644 index 00000000..1ca7a132 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_l4_raw_entity_unknown_call.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "error": "OwnIR call to 'Unknown' inside a borrow_mut region passes tracked local 'order', but the callee has no ownership contract (Orders.cs:13) — refused: an exclusive region must not hand its entity or tokens to unanalysed code" +} diff --git a/tests/fixtures/verdicts/typestate_l4b_raw_entity_shared_call.verdicts.json b/tests/fixtures/verdicts/typestate_l4b_raw_entity_shared_call.verdicts.json new file mode 100644 index 00000000..2f02421e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_l4b_raw_entity_shared_call.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "Orders.cs", + "line": 10, + "code": "OWN012", + "component": "Handler.Run", + "event": "order", + "handler": "", + "message": "IDisposable local 'order' is borrowed while an exclusive borrow of it is live", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_l4c_raw_entity_consumed.verdicts.json b/tests/fixtures/verdicts/typestate_l4c_raw_entity_consumed.verdicts.json new file mode 100644 index 00000000..3de030bd --- /dev/null +++ b/tests/fixtures/verdicts/typestate_l4c_raw_entity_consumed.verdicts.json @@ -0,0 +1,21 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "Orders.cs", + "line": 10, + "code": "OWN007", + "component": "Handler.Run", + "event": "order", + "handler": "", + "message": "IDisposable local 'order' is consumed or returned while it is borrowed", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_l4d_token_unknown_call.verdicts.json b/tests/fixtures/verdicts/typestate_l4d_token_unknown_call.verdicts.json new file mode 100644 index 00000000..25d4e12f --- /dev/null +++ b/tests/fixtures/verdicts/typestate_l4d_token_unknown_call.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "error": "OwnIR call to 'Unknown' inside a borrow_mut region passes tracked local 'a', but the callee has no ownership contract (Orders.cs:13) — refused: an exclusive region must not hand its entity or tokens to unanalysed code" +} diff --git a/tests/fixtures/verdicts/typestate_l5_normal_transition.verdicts.json b/tests/fixtures/verdicts/typestate_l5_normal_transition.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_l5_normal_transition.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_l5b_two_transitions.verdicts.json b/tests/fixtures/verdicts/typestate_l5b_two_transitions.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_l5b_two_transitions.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_l5c_read_after_region.verdicts.json b/tests/fixtures/verdicts/typestate_l5c_read_after_region.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_l5c_read_after_region.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_n1_token_spent_on_other_entity.verdicts.json b/tests/fixtures/verdicts/typestate_n1_token_spent_on_other_entity.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_n1_token_spent_on_other_entity.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_n2_two_tokens_one_region.verdicts.json b/tests/fixtures/verdicts/typestate_n2_two_tokens_one_region.verdicts.json new file mode 100644 index 00000000..9454b37e --- /dev/null +++ b/tests/fixtures/verdicts/typestate_n2_two_tokens_one_region.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "findings": [] +} diff --git a/tests/fixtures/verdicts/typestate_n3_raw_mutation_inferred_effect.verdicts.json b/tests/fixtures/verdicts/typestate_n3_raw_mutation_inferred_effect.verdicts.json new file mode 100644 index 00000000..a327e8d5 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_n3_raw_mutation_inferred_effect.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "error": "OwnIR call to 'Touch' inside a borrow_mut region passes tracked local 'order' at position 0, which carries no ownership effect (Orders.cs:13) — refused: inside an exclusive region every position that receives the entity or a token must state what it does with it" +} diff --git a/tests/fixtures/verdicts/typestate_n4_token_outlives_region.verdicts.json b/tests/fixtures/verdicts/typestate_n4_token_outlives_region.verdicts.json new file mode 100644 index 00000000..1c289081 --- /dev/null +++ b/tests/fixtures/verdicts/typestate_n4_token_outlives_region.verdicts.json @@ -0,0 +1,4 @@ +{ + "verdicts_version": 1, + "error": "internal: the core reported [OWN030] on the lowered facts that the bridge cannot map back to a C# subscription (subject=None, message=\"undefined name 'loc_13'\"). The OwnIR lowering has drifted from the core; teach the bridge this diagnostic rather than dropping the finding." +} diff --git a/tests/fixtures/verdicts/typestate_w1_loan_wordings.verdicts.json b/tests/fixtures/verdicts/typestate_w1_loan_wordings.verdicts.json new file mode 100644 index 00000000..84bbcccd --- /dev/null +++ b/tests/fixtures/verdicts/typestate_w1_loan_wordings.verdicts.json @@ -0,0 +1,101 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "Wordings.cs", + "line": 1, + "code": "OWN005", + "component": "W.Moved", + "event": "b", + "handler": "", + "message": "IDisposable local 'b' is used after its ownership was moved", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + }, + { + "file": "Wordings.cs", + "line": 11, + "code": "OWN007", + "component": "W.Consumed", + "event": "o", + "handler": "", + "message": "IDisposable local 'o' is consumed or returned while it is borrowed", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + }, + { + "file": "Wordings.cs", + "line": 21, + "code": "OWN008", + "component": "W.Released", + "event": "o", + "handler": "", + "message": "IDisposable local 'o' is released while it is borrowed", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + }, + { + "file": "Wordings.cs", + "line": 31, + "code": "OWN011", + "component": "W.Reborrowed", + "event": "o", + "handler": "", + "message": "IDisposable local 'o' is exclusively borrowed while an exclusive borrow of it is live", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + }, + { + "file": "Wordings.cs", + "line": 41, + "code": "OWN012", + "component": "W.Shared", + "event": "o", + "handler": "", + "message": "IDisposable local 'o' is borrowed while an exclusive borrow of it is live", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + }, + { + "file": "Wordings.cs", + "line": 51, + "code": "OWN013", + "component": "W.Used", + "event": "o", + "handler": "", + "message": "IDisposable local 'o' is used directly while an exclusive borrow of it is live", + "kind": "disposable", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/typestate_w2_loan_wordings_pooled.verdicts.json b/tests/fixtures/verdicts/typestate_w2_loan_wordings_pooled.verdicts.json new file mode 100644 index 00000000..0502e3ea --- /dev/null +++ b/tests/fixtures/verdicts/typestate_w2_loan_wordings_pooled.verdicts.json @@ -0,0 +1,101 @@ +{ + "verdicts_version": 1, + "findings": [ + { + "file": "Wordings.cs", + "line": 1, + "code": "OWN005", + "component": "W.Moved", + "event": "b", + "handler": "", + "message": "pooled buffer 'b' is used after its ownership was moved", + "kind": "pooled buffer", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + }, + { + "file": "Wordings.cs", + "line": 11, + "code": "OWN007", + "component": "W.Consumed", + "event": "o", + "handler": "", + "message": "pooled buffer 'o' is consumed or returned while it is borrowed", + "kind": "pooled buffer", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + }, + { + "file": "Wordings.cs", + "line": 21, + "code": "OWN008", + "component": "W.Released", + "event": "o", + "handler": "", + "message": "pooled buffer 'o' is released while it is borrowed", + "kind": "pooled buffer", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + }, + { + "file": "Wordings.cs", + "line": 31, + "code": "OWN011", + "component": "W.Reborrowed", + "event": "o", + "handler": "", + "message": "pooled buffer 'o' is exclusively borrowed while an exclusive borrow of it is live", + "kind": "pooled buffer", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + }, + { + "file": "Wordings.cs", + "line": 41, + "code": "OWN012", + "component": "W.Shared", + "event": "o", + "handler": "", + "message": "pooled buffer 'o' is borrowed while an exclusive borrow of it is live", + "kind": "pooled buffer", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + }, + { + "file": "Wordings.cs", + "line": 51, + "code": "OWN013", + "component": "W.Used", + "event": "o", + "handler": "", + "message": "pooled buffer 'o' is used directly while an exclusive borrow of it is live", + "kind": "pooled buffer", + "advisory": false, + "severity": null, + "related": [], + "flow": [], + "ignore_reason": null, + "column": null + } + ] +} diff --git a/tests/fixtures/verdicts/verdict_boundary_effect_line_negative.facts.json b/tests/fixtures/verdicts/verdict_boundary_effect_line_negative.facts.json index 3737429c..7db44663 100644 --- a/tests/fixtures/verdicts/verdict_boundary_effect_line_negative.facts.json +++ b/tests/fixtures/verdicts/verdict_boundary_effect_line_negative.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Bound", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_boundary_line_above_u32.facts.json b/tests/fixtures/verdicts/verdict_boundary_line_above_u32.facts.json index 21795e6c..000653c1 100644 --- a/tests/fixtures/verdicts/verdict_boundary_line_above_u32.facts.json +++ b/tests/fixtures/verdicts/verdict_boundary_line_above_u32.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Bound", "_doc": [ "The upper edge of the same promoted control: an acquire at 2^32 is outside", diff --git a/tests/fixtures/verdicts/verdict_boundary_line_negative.facts.json b/tests/fixtures/verdicts/verdict_boundary_line_negative.facts.json index 98d68d2d..fabde6a7 100644 --- a/tests/fixtures/verdicts/verdict_boundary_line_negative.facts.json +++ b/tests/fixtures/verdicts/verdict_boundary_line_negative.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Bound", "_doc": [ "A promoted #259 coordinate-domain control: a negative param line. Before", diff --git a/tests/fixtures/verdicts/verdict_boundary_service_line_negative.facts.json b/tests/fixtures/verdicts/verdict_boundary_service_line_negative.facts.json index 89253f3b..5861b2a7 100644 --- a/tests/fixtures/verdicts/verdict_boundary_service_line_negative.facts.json +++ b/tests/fixtures/verdicts/verdict_boundary_service_line_negative.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Bound", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_column_sort_order.facts.json b/tests/fixtures/verdicts/verdict_column_sort_order.facts.json index 53caa314..daf8a879 100644 --- a/tests/fixtures/verdicts/verdict_column_sort_order.facts.json +++ b/tests/fixtures/verdicts/verdict_column_sort_order.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Cols", "_doc": [ "BR-V8's column member: two leaks on the SAME line are minted in the order b", diff --git a/tests/fixtures/verdicts/verdict_dedup_key_members.facts.json b/tests/fixtures/verdicts/verdict_dedup_key_members.facts.json index cf625c8e..c836f39c 100644 --- a/tests/fixtures/verdicts/verdict_dedup_key_members.facts.json +++ b/tests/fixtures/verdicts/verdict_dedup_key_members.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DedupKeyMembers", "_doc": [ "BR-V7 key members that survive only because they are IN the key, now that", diff --git a/tests/fixtures/verdicts/verdict_dedup_needs_the_message.facts.json b/tests/fixtures/verdicts/verdict_dedup_needs_the_message.facts.json index 6247bfa0..3ceb93d3 100644 --- a/tests/fixtures/verdicts/verdict_dedup_needs_the_message.facts.json +++ b/tests/fixtures/verdicts/verdict_dedup_needs_the_message.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DedupNeedsTheMessage", "_doc": [ "BR-V7's `message` member, which cp4 could not carry and argued was", diff --git a/tests/fixtures/verdicts/verdict_degenerate_metadata.facts.json b/tests/fixtures/verdicts/verdict_degenerate_metadata.facts.json index aa0bacff..c9fb182f 100644 --- a/tests/fixtures/verdicts/verdict_degenerate_metadata.facts.json +++ b/tests/fixtures/verdicts/verdict_degenerate_metadata.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DegenerateMetadata", "_doc": [ "Metadata that is PRESENT and degenerate, which is not the same as absent.", diff --git a/tests/fixtures/verdicts/verdict_di004_call_site_anchor.facts.json b/tests/fixtures/verdicts/verdict_di004_call_site_anchor.facts.json index 903a5bd9..3d275348 100644 --- a/tests/fixtures/verdicts/verdict_di004_call_site_anchor.facts.json +++ b/tests/fixtures/verdicts/verdict_di004_call_site_anchor.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Di004", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_di005_cache_site_anchor.facts.json b/tests/fixtures/verdicts/verdict_di005_cache_site_anchor.facts.json index 7d612df5..54e94aff 100644 --- a/tests/fixtures/verdicts/verdict_di005_cache_site_anchor.facts.json +++ b/tests/fixtures/verdicts/verdict_di005_cache_site_anchor.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Di005", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_di_duplicate_sites_last_wins.facts.json b/tests/fixtures/verdicts/verdict_di_duplicate_sites_last_wins.facts.json index e10b8921..5246092b 100644 --- a/tests/fixtures/verdicts/verdict_di_duplicate_sites_last_wins.facts.json +++ b/tests/fixtures/verdicts/verdict_di_duplicate_sites_last_wins.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DiDup", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_di_evidence_degradations.facts.json b/tests/fixtures/verdicts/verdict_di_evidence_degradations.facts.json index 745e2373..dd351b50 100644 --- a/tests/fixtures/verdicts/verdict_di_evidence_degradations.facts.json +++ b/tests/fixtures/verdicts/verdict_di_evidence_degradations.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DiEvidenceDegradations", "_doc": [ "Two BR-V5 degradations on the DI families. `App` records a consuming ctor", diff --git a/tests/fixtures/verdicts/verdict_di_graph_families.facts.json b/tests/fixtures/verdicts/verdict_di_graph_families.facts.json index 50121f85..5a60b17c 100644 --- a/tests/fixtures/verdicts/verdict_di_graph_families.facts.json +++ b/tests/fixtures/verdicts/verdict_di_graph_families.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DiGraph", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_di_tolerant_typed_coercions.facts.json b/tests/fixtures/verdicts/verdict_di_tolerant_typed_coercions.facts.json index 9cfa7340..a835a566 100644 --- a/tests/fixtures/verdicts/verdict_di_tolerant_typed_coercions.facts.json +++ b/tests/fixtures/verdicts/verdict_di_tolerant_typed_coercions.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "DiTolerant", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_domain_tolerant_readers.facts.json b/tests/fixtures/verdicts/verdict_domain_tolerant_readers.facts.json index b946ab0a..3b92f3a5 100644 --- a/tests/fixtures/verdicts/verdict_domain_tolerant_readers.facts.json +++ b/tests/fixtures/verdicts/verdict_domain_tolerant_readers.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Dom", "_doc": [ "The tolerant coordinate readers, where nothing else reaches them. The four", diff --git a/tests/fixtures/verdicts/verdict_door_effect_deps_not_strings.facts.json b/tests/fixtures/verdicts/verdict_door_effect_deps_not_strings.facts.json index e3510dc1..9a10a556 100644 --- a/tests/fixtures/verdicts/verdict_door_effect_deps_not_strings.facts.json +++ b/tests/fixtures/verdicts/verdict_door_effect_deps_not_strings.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Door", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_door_service_unknown_lifetime.facts.json b/tests/fixtures/verdicts/verdict_door_service_unknown_lifetime.facts.json index 4ecfd778..6e429306 100644 --- a/tests/fixtures/verdicts/verdict_door_service_unknown_lifetime.facts.json +++ b/tests/fixtures/verdicts/verdict_door_service_unknown_lifetime.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Door", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_eff001_slice_dropped.facts.json b/tests/fixtures/verdicts/verdict_eff001_slice_dropped.facts.json index ee73b2f8..42a64a6a 100644 --- a/tests/fixtures/verdicts/verdict_eff001_slice_dropped.facts.json +++ b/tests/fixtures/verdicts/verdict_eff001_slice_dropped.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Eff001NoSlice", "_doc": [ "BR-V5's line-guard on the effect slice: the storm is a verdict either way,", diff --git a/tests/fixtures/verdicts/verdict_eff001_storm_and_memo.facts.json b/tests/fixtures/verdicts/verdict_eff001_storm_and_memo.facts.json index ff9889b9..020b0c07 100644 --- a/tests/fixtures/verdicts/verdict_eff001_storm_and_memo.facts.json +++ b/tests/fixtures/verdicts/verdict_eff001_storm_and_memo.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Fx", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_flow_local_wordings.facts.json b/tests/fixtures/verdicts/verdict_flow_local_wordings.facts.json index e983201b..3ddb770b 100644 --- a/tests/fixtures/verdicts/verdict_flow_local_wordings.facts.json +++ b/tests/fixtures/verdicts/verdict_flow_local_wordings.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "FlowLocalWordings", "_doc": [ "BR-V4's flow-local wordings the swept corpora never mint, one function each", diff --git a/tests/fixtures/verdicts/verdict_multi_file_ordering.facts.json b/tests/fixtures/verdicts/verdict_multi_file_ordering.facts.json index d61e0c4a..3639865d 100644 --- a/tests/fixtures/verdicts/verdict_multi_file_ordering.facts.json +++ b/tests/fixtures/verdicts/verdict_multi_file_ordering.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Order", "_doc": [ "BR-V8 across families and files: five findings on line 7 in five files sort", diff --git a/tests/fixtures/verdicts/verdict_own014_escape_slice_dropped.facts.json b/tests/fixtures/verdicts/verdict_own014_escape_slice_dropped.facts.json index 5377c85b..94630572 100644 --- a/tests/fixtures/verdicts/verdict_own014_escape_slice_dropped.facts.json +++ b/tests/fixtures/verdicts/verdict_own014_escape_slice_dropped.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Own014NoSlice", "_doc": [ "BR-V5's 'a slice shorter than two steps is dropped', on the OWN014 escape", diff --git a/tests/fixtures/verdicts/verdict_own014_wordings.facts.json b/tests/fixtures/verdicts/verdict_own014_wordings.facts.json index fba21931..52f38c4b 100644 --- a/tests/fixtures/verdicts/verdict_own014_wordings.facts.json +++ b/tests/fixtures/verdicts/verdict_own014_wordings.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Own014Wordings", "_doc": [ "The OWN014 wordings the swept corpora never mint. `Cache` is a DI SCOPED", diff --git a/tests/fixtures/verdicts/verdict_own051_gates.facts.json b/tests/fixtures/verdicts/verdict_own051_gates.facts.json index a9ee0a1e..d0c8b018 100644 --- a/tests/fixtures/verdicts/verdict_own051_gates.facts.json +++ b/tests/fixtures/verdicts/verdict_own051_gates.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Gates", "_doc": [ "BR-L8 / INF-A5 advisories: an owned local dropped after a may-call is silent", diff --git a/tests/fixtures/verdicts/verdict_pool_view_anchor.facts.json b/tests/fixtures/verdicts/verdict_pool_view_anchor.facts.json index f5cd45f4..eb7874a1 100644 --- a/tests/fixtures/verdicts/verdict_pool_view_anchor.facts.json +++ b/tests/fixtures/verdicts/verdict_pool_view_anchor.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "PoolView", "_doc": [ "OWN025 anchors at the VIEW site (the core's line) with column null ON", diff --git a/tests/fixtures/verdicts/verdict_protocol_absent_rules_are_silent.facts.json b/tests/fixtures/verdicts/verdict_protocol_absent_rules_are_silent.facts.json index b2df7e99..a17e3ffd 100644 --- a/tests/fixtures/verdicts/verdict_protocol_absent_rules_are_silent.facts.json +++ b/tests/fixtures/verdicts/verdict_protocol_absent_rules_are_silent.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Silent", "components": [ { diff --git a/tests/fixtures/verdicts/verdict_protocol_barrier_maybe_two_step_slice.facts.json b/tests/fixtures/verdicts/verdict_protocol_barrier_maybe_two_step_slice.facts.json index cef39096..ca356c42 100644 --- a/tests/fixtures/verdicts/verdict_protocol_barrier_maybe_two_step_slice.facts.json +++ b/tests/fixtures/verdicts/verdict_protocol_barrier_maybe_two_step_slice.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Docs", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_protocol_beside_other_families.facts.json b/tests/fixtures/verdicts/verdict_protocol_beside_other_families.facts.json index ce94f4ef..f281b134 100644 --- a/tests/fixtures/verdicts/verdict_protocol_beside_other_families.facts.json +++ b/tests/fixtures/verdicts/verdict_protocol_beside_other_families.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Mixed", "components": [ { diff --git a/tests/fixtures/verdicts/verdict_protocol_exit_codes_and_wordings.facts.json b/tests/fixtures/verdicts/verdict_protocol_exit_codes_and_wordings.facts.json index 27f2c254..e693c343 100644 --- a/tests/fixtures/verdicts/verdict_protocol_exit_codes_and_wordings.facts.json +++ b/tests/fixtures/verdicts/verdict_protocol_exit_codes_and_wordings.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Batch", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_protocol_identity_and_line_guards.facts.json b/tests/fixtures/verdicts/verdict_protocol_identity_and_line_guards.facts.json index 4848b8ae..d43aafe8 100644 --- a/tests/fixtures/verdicts/verdict_protocol_identity_and_line_guards.facts.json +++ b/tests/fixtures/verdicts/verdict_protocol_identity_and_line_guards.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Names", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_protocol_obl005_dead_rule.facts.json b/tests/fixtures/verdicts/verdict_protocol_obl005_dead_rule.facts.json index 19387b25..29baa75e 100644 --- a/tests/fixtures/verdicts/verdict_protocol_obl005_dead_rule.facts.json +++ b/tests/fixtures/verdicts/verdict_protocol_obl005_dead_rule.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Ghosts", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_protocol_tolerant_door_rules.facts.json b/tests/fixtures/verdicts/verdict_protocol_tolerant_door_rules.facts.json index 0e9a0039..2d53b212 100644 --- a/tests/fixtures/verdicts/verdict_protocol_tolerant_door_rules.facts.json +++ b/tests/fixtures/verdicts/verdict_protocol_tolerant_door_rules.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Tolerant", "components": [], "_doc": [ diff --git a/tests/fixtures/verdicts/verdict_same_site_distinct_events.facts.json b/tests/fixtures/verdicts/verdict_same_site_distinct_events.facts.json index a6a741ad..a9045b0c 100644 --- a/tests/fixtures/verdicts/verdict_same_site_distinct_events.facts.json +++ b/tests/fixtures/verdicts/verdict_same_site_distinct_events.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "SameSite", "_doc": [ "BR-V7's key, one pair per member: records on the same line that differ in", diff --git a/tests/fixtures/verdicts/verdict_severity_tiers_and_suppression.facts.json b/tests/fixtures/verdicts/verdict_severity_tiers_and_suppression.facts.json index 1264ecd0..2ba04fde 100644 --- a/tests/fixtures/verdicts/verdict_severity_tiers_and_suppression.facts.json +++ b/tests/fixtures/verdicts/verdict_severity_tiers_and_suppression.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Tiers", "_doc": [ "BR-V6: an injected-source `+=` or ignored `.Subscribe()` is the warning tier;", diff --git a/tests/fixtures/verdicts/verdict_skip_list_artifacts.facts.json b/tests/fixtures/verdicts/verdict_skip_list_artifacts.facts.json index 9d532fd5..2dc4b0cb 100644 --- a/tests/fixtures/verdicts/verdict_skip_list_artifacts.facts.json +++ b/tests/fixtures/verdicts/verdict_skip_list_artifacts.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Skip", "_doc": [ "BR-V2, the CLOSED skip list, one control per member the tree can reach:", diff --git a/tests/fixtures/verdicts/verdict_subscription_lambda_note.facts.json b/tests/fixtures/verdicts/verdict_subscription_lambda_note.facts.json index e1caee3c..f6f9e6ca 100644 --- a/tests/fixtures/verdicts/verdict_subscription_lambda_note.facts.json +++ b/tests/fixtures/verdicts/verdict_subscription_lambda_note.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "SubscriptionLambdaNote", "_doc": [ "The inline-lambda note on the INJECTED plain-subscription wording. The", diff --git a/tests/fixtures/verdicts/verdict_unknown_line_guards.facts.json b/tests/fixtures/verdicts/verdict_unknown_line_guards.facts.json index 51faa6de..d0412497 100644 --- a/tests/fixtures/verdicts/verdict_unknown_line_guards.facts.json +++ b/tests/fixtures/verdicts/verdict_unknown_line_guards.facts.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "UnknownLineGuards", "_doc": [ "BR-V5's line guards, one per slice builder, each on a record that IS a", diff --git a/tests/goldens/fix_candidates_off.golden.json b/tests/goldens/fix_candidates_off.golden.json index 9110c56c..c60de9bc 100644 --- a/tests/goldens/fix_candidates_off.golden.json +++ b/tests/goldens/fix_candidates_off.golden.json @@ -1,5 +1,5 @@ { - "ownir_version": 0, + "ownir_version": 1, "module": "Extracted", "components": [ { diff --git a/tests/test_effects.py b/tests/test_effects.py index ed13aff7..885072d4 100644 --- a/tests/test_effects.py +++ b/tests/test_effects.py @@ -99,7 +99,7 @@ def check(cond: bool, msg: str) -> None: # ---- the OwnIR bridge ---- facts = { - "ownir_version": 0, "module": "D", "components": [], + "ownir_version": 1, "module": "D", "components": [], "effects": [ {"component": "Dashboard", "file": "Dashboard.tsx", "line": 33, "io": True, "deps": ["filters"], @@ -123,11 +123,11 @@ def check(cond: bool, msg: str) -> None: check("EFF001" in rules and "storm" in rules["EFF001"], "EFF001 must appear in the SARIF rules") # malformed effects degrade gracefully (additive/optional, never a crash). - check(check_facts({"ownir_version": 0, "components": [], "effects": "nope"}) == [], + check(check_facts({"ownir_version": 1, "components": [], "effects": "nope"}) == [], "a malformed effects block must not crash check_facts") # a per-entry malformed effect is SKIPPED, not coerced: `deps: "a"` must NOT # become `("a",)` and emit a spurious EFF001 on the direct check_facts() path. - coerce = check_facts({"ownir_version": 0, "components": [], "effects": [ + coerce = check_facts({"ownir_version": 1, "components": [], "effects": [ {"component": "X", "file": "X.tsx", "line": 1, "io": True, "deps": "a", "bindings": [{"name": "a", "init": "object", "refs": [], "line": 1}]}]}) check(coerce == [], f"a malformed deps='a' entry must be skipped, got {coerce}") diff --git a/tests/test_fix_candidates.py b/tests/test_fix_candidates.py index c5b905a3..a724cf29 100644 --- a/tests/test_fix_candidates.py +++ b/tests/test_fix_candidates.py @@ -63,7 +63,7 @@ def _facts(subs: list[dict], qn: str = "N.C", file: str = "N/C.cs", comp = {"name": qn.rsplit(".", 1)[-1], "qualified_name": qn, "is_partial": is_partial, "is_nested": is_nested, "declaration_count": 1, "is_generated": is_generated, "file": file, "subscriptions": subs} - facts: dict = {"ownir_version": 0, "components": [comp, *(extra or [])]} + facts: dict = {"ownir_version": 1, "components": [comp, *(extra or [])]} if version is not _OMIT: facts["fix_candidates_version"] = version return facts diff --git a/tests/test_obligations.py b/tests/test_obligations.py index 0bf069cf..05b50054 100644 --- a/tests/test_obligations.py +++ b/tests/test_obligations.py @@ -355,7 +355,7 @@ def rejects(fn: object, raw: object, why: str) -> None: # exit leak through the bridge: OBL003 anchored at the open site, and the # SARIF rules catalogue knows the code. leak = check_facts({ - "ownir_version": 0, "module": "S", + "ownir_version": 1, "module": "S", "protocols": [{ "name": "Suppress", "opens": {"kind": "assign", "target": "_suppress", "value": True}, @@ -377,7 +377,7 @@ def rejects(fn: object, raw: object, why: str) -> None: # OBL005: a scoped protocol matching no reported method is an advisory. dead = check_facts({ - "ownir_version": 0, "module": "S", + "ownir_version": 1, "module": "S", "protocols": [{ "name": "Ghost", "opens": {"kind": "assign", "target": "x", "value": False}, @@ -391,10 +391,10 @@ def rejects(fn: object, raw: object, why: str) -> None: # malformed blocks degrade gracefully on the direct check_facts path # (load() fail-louds; embedders/tests may skip it). - check(check_facts({"ownir_version": 0, "components": [], + check(check_facts({"ownir_version": 1, "components": [], "protocols": "nope"}) == [], "a malformed protocols block must not crash check_facts") - check(check_facts({"ownir_version": 0, "components": [], + check(check_facts({"ownir_version": 1, "components": [], "protocols": [{"name": "P"}], "protocol_functions": [{"name": "m"}]}) == [], "a malformed protocol entry is skipped on the direct path") @@ -403,7 +403,7 @@ def rejects(fn: object, raw: object, why: str) -> None: # On the direct path the first wins deterministically (the second is # skipped — never a mixed message or a dedup-collapsed pair of findings)... dup = check_facts({ - "ownir_version": 0, "module": "S", + "ownir_version": 1, "module": "S", "protocols": [ {"name": "Dup", "opens": {"kind": "assign", "target": "A", "value": False}, @@ -441,14 +441,14 @@ def load_rejects(doc: dict[str, object], why: str) -> None: finally: os.unlink(tmp) - load_rejects({"ownir_version": 0, "module": "S", + load_rejects({"ownir_version": 1, "module": "S", "protocol_functions": [{"name": "m", "events": [{"ev": "goto", "line": 1}]}]}, "an unknown protocol event") _p = {"name": "Dup", "opens": {"kind": "assign", "target": "A", "value": False}, "closes": {"kind": "assign", "target": "A", "value": True}} - load_rejects({"ownir_version": 0, "module": "S", "protocols": [_p, dict(_p)]}, + load_rejects({"ownir_version": 1, "module": "S", "protocols": [_p, dict(_p)]}, "a duplicate protocol name") # both fixtures pass the real load() gate (shape-valid on disk). diff --git a/tests/test_ownir.py b/tests/test_ownir.py index 75a3d589..b9bddfe4 100644 --- a/tests/test_ownir.py +++ b/tests/test_ownir.py @@ -553,10 +553,15 @@ def _sub(source: str | None) -> list[Finding]: # lower (unknown-op OR the declared-but-unhandled internal raise) is a # phantom authority entry — the set claims an op the lowerer cannot handle. _node = {"op": _op, "line": 1} - if _op in ("acquire", "release", "use", "overspan", "alias_join"): + if _op in ("acquire", "release", "use", "overspan", "alias_join", "move"): _node["var"] = "x" - if _op == "alias_join": + if _op in ("alias_join", "move"): _node["src"] = "x" + if _op == "borrow_mut": + # a well-formed region needs a tracked owner; the smoke body + # here has none, so the lowerer refuses it — which is a + # handled op refusing its input, not an unknown one. + _node.update({"owner": "x", "binding": "r", "body": []}) if _op == "call": _node["callee"] = "f" _facts = {"ownir_version": OWNIR_VERSION, "module": "S", @@ -909,7 +914,7 @@ def _sub(source: str | None) -> list[Finding]: fails.append(f"DI003 message missing consuming-constructor anchor: " f"{cache3.message if cache3 else None!r}") # bridge: DI003 surfaces as a WARNING-severity finding; `disposable` is parsed. - di3facts = {"ownir_version": 0, "module": "X", "components": [], "functions": [], + di3facts = {"ownir_version": 1, "module": "X", "components": [], "functions": [], "services": [ {"name": "Cache", "lifetime": "singleton", "deps": ["Conn"], "file": "S.cs", "line": 7}, @@ -960,7 +965,7 @@ def _sub(source: str | None) -> list[Finding]: if not di2 or "WeakReference" not in di2[0].message: fails.append("DI002 message missing 'WeakReference'") # bridge: DI002 surfaces as a WARNING; `weak_deps` is parsed and kept off DI001. - di2facts = {"ownir_version": 0, "module": "X", "components": [], "functions": [], + di2facts = {"ownir_version": 1, "module": "X", "components": [], "functions": [], "services": [ {"name": "WeakCache", "lifetime": "singleton", "deps": [], "weak_deps": ["Db"], "file": "S.cs", "line": 9}, @@ -1032,7 +1037,7 @@ def _sub(source: str | None) -> list[Finding]: # bridge: DI004 surfaces as a WARNING, anchored at the CALL SITE (R.cs:42) — its real # consumer (Codex) — with the REGISTRATION (S.cs:5) as the Finding.related secondary and # named in the message tail. (registration site S.cs:5 differs from the call site R.cs:42.) - di4facts = {"ownir_version": 0, "module": "X", "components": [], "functions": [], + di4facts = {"ownir_version": 1, "module": "X", "components": [], "functions": [], "services": [ {"name": "Resolver", "lifetime": "singleton", "deps": [], "root_resolves": ["Conn"], "file": "S.cs", "line": 5, @@ -1117,7 +1122,7 @@ def _sub(source: str | None) -> list[Finding]: f"{(trans5.cached_file, trans5.cached_line) if trans5 else None}") # bridge: DI005 surfaces as a WARNING anchored at the STORE site (C.cs:21), with the # REGISTRATION (S.cs:7) as the related secondary and named in the message tail. - di5facts = {"ownir_version": 0, "module": "X", "components": [], "functions": [], + di5facts = {"ownir_version": 1, "module": "X", "components": [], "functions": [], "services": [ {"name": "Cacher", "lifetime": "singleton", "deps": [], "scope_cached": ["Db"], "file": "S.cs", "line": 7, @@ -1217,7 +1222,7 @@ def _sub(source: str | None) -> list[Finding]: fails.append(f"DI001 ReportService transitive flow wrong: {rs.flow if rs else None!r}") checks += 1 # a DI001 whose ctor location is UNKNOWN degrades cleanly — no suffix, no related. - nolocf = check_facts({"ownir_version": 0, "module": "X", "components": [], "functions": [], + nolocf = check_facts({"ownir_version": 1, "module": "X", "components": [], "functions": [], "services": [ {"name": "Cap", "lifetime": "singleton", "deps": ["Sc"], "file": "S.cs", "line": 3}, @@ -1231,7 +1236,7 @@ def _sub(source: str | None) -> list[Finding]: # an INTERFACE registration (AddSingleton): the singleton is 'IBilling' # (no ctor) but the consuming ctor is 'Billing's, so the finding must name the IMPL Billing, # never the interface (Codex). ctor_type carries the impl through the fact. - ifacef = check_facts({"ownir_version": 0, "module": "X", "components": [], "functions": [], + ifacef = check_facts({"ownir_version": 1, "module": "X", "components": [], "functions": [], "services": [ {"name": "IBilling", "lifetime": "singleton", "deps": ["Db"], "file": "Startup.cs", "line": 8, "ctor_file": "Billing.cs", @@ -1471,7 +1476,7 @@ def _sub(source: str | None) -> list[Finding]: # subscribe site -> where the longer-lived source service was registered (its lifetime # is *why* the subscriber escapes). The source hop comes from the services graph. checks += 1 - esc = check_facts({"ownir_version": 0, "module": "M", "functions": [], + esc = check_facts({"ownir_version": 1, "module": "M", "functions": [], "components": [{"name": "Vm", "file": "VM.cs", "subscriptions": [ {"event": "bus.Tick", "handler": "OnTick", "line": 11, "released": False, "resource": "subscription", "source": "injected", "source_type": "IBus"}]}], diff --git a/tests/test_ownir_column.py b/tests/test_ownir_column.py index b9857f45..a9c16d26 100644 --- a/tests/test_ownir_column.py +++ b/tests/test_ownir_column.py @@ -105,7 +105,7 @@ def _subscription_facts(column: int | None = None) -> dict: "line": 12, "released": False} if column is not None: sub["column"] = column - return {"ownir_version": 0, "module": "WpfApp", + return {"ownir_version": 1, "module": "WpfApp", "components": [{"name": "CustomerViewModel", "file": "CustomerViewModel.cs", "subscriptions": [sub]}]} @@ -120,7 +120,7 @@ def _flow_acquire_facts(column: int | None = None) -> dict: acquire: dict = {"op": "acquire", "var": "tfLeak", "line": 105} if column is not None: acquire["column"] = column - return {"ownir_version": 0, "module": "Extracted", "components": [], + return {"ownir_version": 1, "module": "Extracted", "components": [], "functions": [{"name": "FlowLocalsSample.TryNeverDisposed", "file": "FlowLocalsSample.cs", "body": [acquire, @@ -223,11 +223,11 @@ def run() -> int: # the direct `acquire` would leave whole categories line-only, so each path # is exercised here rather than assumed from the code reading. def _fn(body): - return {"ownir_version": 0, "module": "M", "components": [], + return {"ownir_version": 1, "module": "M", "components": [], "functions": [{"name": "F.G", "file": "F.cs", "body": body}]} # a contract param that became an owned obligation (`_lower_fn_params`) - prm = check_facts({"ownir_version": 0, "module": "M", "components": [], + prm = check_facts({"ownir_version": 1, "module": "M", "components": [], "functions": [{"name": "F.G", "file": "F.cs", "params": [{"name": "owned", "line": 30, "column": 26, "effect": "consume"}], @@ -307,8 +307,9 @@ def _fn(body): "a re-indented construct must carry ITS column, not a remembered one") # ---- 8. THINGS THAT MUST NOT MOVE -------------------------------------- - check(OWNIR_VERSION == 0, - f"OWNIR_VERSION must stay 0 - an optional field with a safe default is " + check(OWNIR_VERSION == 1, + f"OWNIR_VERSION must stay 1 (the move/borrow_mut vocabulary) - an " + f"optional field with a safe default is " f"additive (spec/OwnIR.md §2), got {OWNIR_VERSION}") if old and new: for fmt in ("human", "github", "msbuild"): diff --git a/tests/test_ownir_defensive_limits.py b/tests/test_ownir_defensive_limits.py index 4dfa4f37..23603266 100644 --- a/tests/test_ownir_defensive_limits.py +++ b/tests/test_ownir_defensive_limits.py @@ -90,7 +90,7 @@ def _body(depth: int, key: str = "then") -> dict[str, Any]: node: dict[str, Any] = {"op": "acquire", "column": 1} for _ in range(depth): node = {"op": "if" if key != "body" else "while", key: [node]} - return {"ownir_version": 0, "functions": [{"body": [node]}]} + return {"ownir_version": 1, "functions": [{"body": [node]}]} def _events(depth: int, key: str = "then") -> dict[str, Any]: @@ -98,7 +98,7 @@ def _events(depth: int, key: str = "then") -> dict[str, Any]: node: dict[str, Any] = {"ev": "return", "line": 1} for _ in range(depth): node = {"ev": "if" if key != "body" else "while", "line": 1, key: [node]} - return {"ownir_version": 0, + return {"ownir_version": 1, "protocol_functions": [{"name": "M", "events": [node]}]} @@ -447,12 +447,12 @@ def _subschemas(node: Any, key: str, out: list[Any]) -> None: # Bounding depth is the kind of change that quietly turns that into a # rejection, so it is asserted rather than assumed. for label, document in ( - ("non-list body", {"ownir_version": 0, "functions": [{"body": 7}]}), + ("non-list body", {"ownir_version": 1, "functions": [{"body": 7}]}), ("body of scalars", - {"ownir_version": 0, "functions": [{"body": [1, "x", None]}]}), - ("absent sections", {"ownir_version": 0}), + {"ownir_version": 1, "functions": [{"body": [1, "x", None]}]}), + ("absent sections", {"ownir_version": 1}), ("null column", - {"ownir_version": 0, + {"ownir_version": 1, "components": [{"subscriptions": [{"column": None}]}]}), ): err = _load(document) @@ -468,18 +468,18 @@ def _subschemas(node: Any, key: str, out: list[Any]) -> None: # must not become `2^31 - 1` and `-1` must not become `1`, because a # clamped anchor points at a REAL line the producer did not mean. def _sub(line: Any) -> dict[str, Any]: - return {"ownir_version": 0, "module": "X", + return {"ownir_version": 1, "module": "X", "components": [{"name": "C", "file": "C.cs", "subscriptions": [ {"event": "e", "handler": "h", "line": line, "source": "static"}]}]} def _acquire(line: Any) -> dict[str, Any]: - return {"ownir_version": 0, "module": "X", "functions": [ + return {"ownir_version": 1, "module": "X", "functions": [ {"name": "F", "file": "F.cs", "body": [{"op": "acquire", "var": "x", "line": line}]}]} def _event(line: Any) -> dict[str, Any]: - return {"ownir_version": 0, "module": "X", + return {"ownir_version": 1, "module": "X", "protocols": [{"name": "P", "opens": {"kind": "assign", "target": "f", "value": True}, @@ -531,7 +531,7 @@ def _event(line: Any) -> dict[str, Any]: # only thing that pins the tolerant column bound is right here and the one # synthetic Layer 3 case written for it. def _sub_col(column: Any) -> dict[str, Any]: - return {"ownir_version": 0, "module": "X", + return {"ownir_version": 1, "module": "X", "components": [{"name": "C", "file": "C.cs", "subscriptions": [ {"event": "e", "handler": "h", "line": 7, "column": column, "source": "static"}]}]} diff --git a/tests/test_ownir_input_boundary.py b/tests/test_ownir_input_boundary.py index e333359b..0aecba16 100644 --- a/tests/test_ownir_input_boundary.py +++ b/tests/test_ownir_input_boundary.py @@ -140,7 +140,7 @@ def run() -> int: # its offset — rather than in the decoder's voice, which is what lets the # port report it identically. Both halves are asserted, because an offset # that silently became a character index would still "contain 0xff". - invalid_utf8 = b'{"ownir_version": 0, "components": [{"name": "\xff\xfe"}]}' + invalid_utf8 = b'{"ownir_version": 1, "components": [{"name": "\xff\xfe"}]}' assert invalid_utf8.index(b"\xff") == 46 failures += _refusal_case( invalid_utf8, check="invalid-utf8", @@ -151,7 +151,7 @@ def run() -> int: # This is the case an implementation that indexed past the valid prefix # would crash on. failures += _refusal_case( - b'{"ownir_version": 0}\xc3', check="invalid-utf8", + b'{"ownir_version": 1}\xc3', check="invalid-utf8", must_contain="is not valid UTF-8: byte 0xc3 at offset 20") # SCOPE: valid UTF-8 that is invalid JSON must still reach the JSON door, @@ -188,7 +188,7 @@ def run() -> int: # V1 anywhere else in the document is the same refusal: the constants are # refused by the PARSER, so the position cannot matter. failures += _refusal_case( - b'{"ownir_version": 0, "components": [{"line": NaN}]}', + b'{"ownir_version": 1, "components": [{"line": NaN}]}', check="v1-non-standard-constant", must_contain="is not valid JSON: NaN is not a JSON value") @@ -209,7 +209,7 @@ def run() -> int: # SCOPE 2 — a `-0` on any other field is not V2 at all and must not even # be refused: it is an ordinary zero, and the document still analyses. - ok_doc = (b'{"ownir_version": 0, "components": [{"name": "C", ' + ok_doc = (b'{"ownir_version": 1, "components": [{"name": "C", ' b'"file": "a.cs", "line": -0}]}') outcome, _ = _load_bytes(ok_doc) if outcome != "ok": @@ -239,7 +239,7 @@ def run() -> int: big = f"{sign}99999999999999999999999999" outcome, detail = _load_bytes( b'{"ownir_version": ' + big.encode() + b"}") - want = f"OwnIR facts are schema v{big}, but this core understands v0" + want = f"OwnIR facts are schema v{big}, but this core understands v1" if outcome != "refused" or not str(detail).startswith(want): failures += _fail( f"an oversized integral version must keep taking the Version " @@ -253,7 +253,7 @@ def run() -> int: # with ordinary integers is checked to still load and still carry them as # plain values. outcome, doc = _load_bytes( - b'{"ownir_version": 0, "components": [{"name": "C", "file": "a.cs", ' + b'{"ownir_version": 1, "components": [{"name": "C", "file": "a.cs", ' b'"line": 12, "column": 3}]}') if outcome != "ok": failures += _fail(f"an ordinary document must load, got {outcome}: " diff --git a/tests/test_repro_fixtures.py b/tests/test_repro_fixtures.py index f3900895..04859c8e 100644 --- a/tests/test_repro_fixtures.py +++ b/tests/test_repro_fixtures.py @@ -486,7 +486,7 @@ def raw_of_another_document(a: dict[str, Any]) -> None: # The link that makes `input.document` an observation: bytes that are # perfectly self-consistent (their own digest and length hold) and parse # to a DIFFERENT document than the artifact claims. - other = b'{"ownir_version": 0, "module": "SomethingElse"}' + other = b'{"ownir_version": 1, "module": "SomethingElse"}' a["input"]["raw"] = encode_raw(other) def drop_consumed(a: dict[str, Any]) -> None: diff --git a/tests/test_stage3_cancellation.py b/tests/test_stage3_cancellation.py index afd3b6f3..85640c76 100644 --- a/tests/test_stage3_cancellation.py +++ b/tests/test_stage3_cancellation.py @@ -101,7 +101,7 @@ def _write_workload(path: str, components: int) -> None: """A large but entirely ORDINARY document. Nothing hostile: the point is a run long enough to interrupt, not a parser stress case.""" with open(path, "w", encoding="utf-8") as f: - f.write('{"ownir_version": 0, "components": [') + f.write('{"ownir_version": 1, "components": [') for i in range(components): if i: f.write(",") diff --git a/tests/test_unreachable_branch_probe.py b/tests/test_unreachable_branch_probe.py index 1ce308c7..e942850c 100644 --- a/tests/test_unreachable_branch_probe.py +++ b/tests/test_unreachable_branch_probe.py @@ -84,7 +84,7 @@ def probe_all() -> dict[str, str]: "loc_1": _record(resource="flow-local", event="s", line=4, pool=True, ever_released=False), } - diags = [Diagnostic("OWN005", "moved 's' at A.cs:9", 9, subject=f"{h}#4") + diags = [Diagnostic("OWN015", "moved 's' at A.cs:9", 9, subject=f"{h}#4") for h in ("loc_0", "loc_1")] plain, pooled = _probe(handles, diags) out["flow_local_fallback_plain"] = plain diff --git a/tests/test_verify_delta.py b/tests/test_verify_delta.py index 0e40a8c2..27f1aea9 100644 --- a/tests/test_verify_delta.py +++ b/tests/test_verify_delta.py @@ -159,7 +159,7 @@ def _cfacts(subs: list) -> dict: comp = {"name": _CTYPE.rsplit(".", 1)[-1], "qualified_name": _CTYPE, "is_partial": False, "is_nested": False, "declaration_count": 1, "is_generated": False, "file": _CREL, "subscriptions": subs} - return {"ownir_version": 0, "fix_candidates_version": 1, "components": [comp]} + return {"ownir_version": 1, "fix_candidates_version": 1, "components": [comp]} def _mk_root(work: str) -> str: diff --git a/tests/verdict_surface_inventory.py b/tests/verdict_surface_inventory.py index e9ad88ee..47f926bd 100644 --- a/tests/verdict_surface_inventory.py +++ b/tests/verdict_surface_inventory.py @@ -132,7 +132,7 @@ def matches(self, f: dict[str, object]) -> bool: ) #: The disposition of the two fallback rows: see `Branch.note`. UNIT_PINNED = ( - "unreachable from any facts document (the nine-op flow vocabulary raises only codes " + "unreachable from any facts document (the eleven-op flow vocabulary raises only codes " "that HAVE a wording); pinned by a `verdict::tests` control driven through " "`map_core`, whose expected text is read from the recorded probe " "`tests/fixtures/unreachable_branches.json`. The core message it " @@ -173,6 +173,23 @@ def _flow_local(bid: str, code: str, kind: str, what: str, tail: str, _flow_local("flowlocal_own009_pool", "OWN009", "pooled buffer", "flow-local maybe-use-after-return", r" may be used after being returned on some path"), + # the move / loan codes, reachable since the flow vocabulary carries `move` + # and `borrow_mut`; one wording per code, per pool split. + *(_flow_local(f"flowlocal_{code.lower()}{suffix}", code, kind, + f"flow-local {what}{label}", tail) + for kind, suffix, label in (("disposable", "", ""), + ("pooled buffer", "_pool", " on a pooled buffer")) + for code, what, tail in ( + ("OWN005", "use after move", r" is used after its ownership was moved"), + ("OWN007", "consume/return while borrowed", + r" is consumed or returned while it is borrowed"), + ("OWN008", "release while borrowed", r" is released while it is borrowed"), + ("OWN011", "exclusive re-borrow", + r" is exclusively borrowed while an exclusive borrow of it is live"), + ("OWN012", "shared borrow under an exclusive one", + r" is borrowed while an exclusive borrow of it is live"), + ("OWN013", "direct use under an exclusive borrow", + r" is used directly while an exclusive borrow of it is live"))), # the two fallbacks: a flow-local code with no wording of its own keeps the # CORE diagnostic's message verbatim after a colon. _flow_local("flowlocal_fallback", "", "disposable", From 1b9be98c3aa327b6931a771d7f5ca57e1a79ea54 Mon Sep 17 00:00:00 2001 From: PhysShell <45852143+PhysShell@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:35:39 +0500 Subject: [PATCH 2/7] feat(roslyn): lower C# state protocols to OwnIR v1 (P-010 pillar 9, first slice) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With --flow-locals the extractor lowers a state-protocol surface into `move` / `borrow_mut`. The surface is two attributes matched by name, so a domain takes no dependency on Own.NET: a [ProtocolRegion] method `(entity, callback)` opens an exclusive region and hands out a [ProtocolToken] ref struct; a method on the token is a transition that spends it. It is a lowering, not an analysis — the verdicts are the core's (a stale token is OWN002, a copied one OWN005, a raw touch of the entity inside the region OWN013). It runs on the instance an ORM tracks. frontend/roslyn/protocol-samples/efcore is an ordinary ASP.NET Core minimal API over EF Core + SQLite: no base entity, no repository, no custom LINQ, no attribute in the handler file. Its acceptance runner drives real HTTP against a real database file and checks that the transition reaches the tracked instance, the ChangeTracker and the saved row. What is claimed is the local C# capabilities and aliases of an entity that already exists. Writes that bypass the entity's C# surface (ExecuteUpdate, a change-tracker metadata write, raw SQL) are outside it, and two of them are pinned under efcore/known-gaps so the limit is stated rather than forgotten. A recognised protocol construct that cannot be lowered safely is REFUSED for the whole scan (exit 2, no facts): * inside a region the rule is default-deny: transitions, token reads, `if`, locals and built-in operators are read; a call, a constructor, a property getter, a loop, `return` are refused. Around a region anything goes; * admission: the state a transition writes may have no PUBLIC mutator on the entity — that would be a transition nobody declared; * boundary: outside the protocol's own types, creating a token, calling a non-public method of the entity or the protocol, and writing their non-publicly-writable state are refused, in one assembly or two. `nameof`, `typeof` and reads are mentions, not operations; * source: the protocol and its entity must be in the scan as source; * binding: the scan does not read obj/, so a region whose entity only binds through IMPLICIT usings is refused, not silently dropped. scripts/protocol_gate.py ties every committed `typestate_cs_*` / `typestate_ef_*` fact back to the C# it came from: 18 programs lowered and judged, 14 refused, 5 rejected by the C# compiler, 5 one-assembly programs, 24 checks on the backend. A new CI job runs it on Linux and Windows, and on Linux also compares the two public CLIs on those documents. Outside --flow-locals, and for a scan with no protocol in it, the extractor's output is unchanged: 122 of 122 comparisons against main over every C# sample CI scans. The samples live under frontend/roslyn/ rather than examples/: examples/ is swept as one document by the #260 shadow sweep, and a backend whose references a directory walk does not have is, correctly, a refusal of the whole scan. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 54 + docs/ROADMAP.md | 2 +- docs/proposals/P-010-type-disciplines.md | 18 +- docs/proposals/README.md | 2 +- frontend/roslyn/OwnSharp.Extractor/Program.cs | 22 + .../OwnSharp.Extractor/ProtocolLowering.cs | 1180 +++++++++++++++++ frontend/roslyn/README.md | 69 + .../protocol-samples/Api/ProtocolApi.cs | 128 ++ .../cases/A1_alias_of_entity.cs | 22 + .../cases/A2_entity_through_indexer.cs | 22 + .../cases/G6_capability_not_spent.cs | 18 + .../cases/L10_region_inside_try.cs | 39 + .../cases/L11_region_beside_disposable.cs | 23 + .../L12_region_beside_leaked_disposable.cs | 21 + .../cases/L13_values_captured_into_region.cs | 28 + .../cases/L1_stale_capability.cs | 21 + .../cases/L2_copied_capability.cs | 21 + .../protocol-samples/cases/L3_raw_mutation.cs | 20 + .../protocol-samples/cases/L4_raw_escape.cs | 20 + .../cases/L5_normal_transition.cs | 18 + .../cases/L6_two_transitions.cs | 19 + .../cases/L6b_chained_transitions.cs | 15 + .../cases/L7_read_after_region.cs | 19 + .../cases/L8_branch_inside_region.cs | 22 + .../cases/L9_opaque_calls_around_region.cs | 26 + .../cases/X1_nested_region_same_entity.cs | 20 + .../protocol-samples/cases/expected.json | 40 + .../C1_construct_token.cs.txt | 14 + .../C2_capture_token_in_nested_lambda.cs.txt | 23 + .../C3_token_escapes_to_outer_local.cs.txt | 18 + .../does-not-compile/C4_token_in_field.cs.txt | 10 + .../does-not-compile/C5_async_region.cs.txt | 19 + .../does-not-compile/expected.json | 24 + .../roslyn/protocol-samples/efcore/.gitignore | 2 + .../efcore/Acceptance/Acceptance.csproj | 16 + .../efcore/Acceptance/Program.cs | 138 ++ .../efcore/OrderBackend/Data/AppDbContext.cs | 19 + .../efcore/OrderBackend/Domain/Order.cs | 55 + .../OrderBackend/Domain/OrderProtocol.cs | 102 ++ .../efcore/OrderBackend/OrderBackend.csproj | 15 + .../efcore/OrderBackend/OrderHandlers.cs | 91 ++ .../efcore/OrderBackend/Program.cs | 36 + .../efcore/accepted/MentionsOnly.cs.txt | 22 + .../efcore/accepted/expected.json | 3 + .../StateWrittenByBulkUpdate.cs.txt | 23 + .../StateWrittenThroughChangeTracker.cs.txt | 24 + .../efcore/known-gaps/expected.json | 4 + .../protocol-samples/efcore/nuget.config | 8 + .../efcore/refused/DirectMutator.cs.txt | 22 + .../efcore/refused/ForgedToken.cs.txt | 24 + .../efcore/refused/MutatorAsDelegate.cs.txt | 22 + .../efcore/refused/SaveInsideRegion.cs.txt | 27 + .../refused/ShipWithLogInsideRegion.cs.txt | 33 + .../efcore/refused/expected.json | 7 + .../R10_property_getter_inside_region.cs.txt | 20 + .../R11_constructor_inside_region.cs.txt | 19 + .../R12_default_token_temporary.cs.txt | 13 + ...blic_mutator_reaches_protocol_state.cs.txt | 44 + ...R14_public_setter_of_protocol_state.cs.txt | 28 + .../refused/R1_default_token.cs.txt | 21 + .../refused/R2_token_outside_region.cs.txt | 14 + .../refused/R3_return_inside_region.cs.txt | 22 + .../refused/R4_loop_inside_region.cs.txt | 22 + .../refused/R5_token_passed_to_helper.cs.txt | 18 + .../R6_region_body_not_a_lambda.cs.txt | 15 + .../refused/R7_entity_not_a_local.cs.txt | 16 + .../refused/R8_token_not_a_ref_struct.cs.txt | 32 + .../R9_opaque_call_inside_region.cs.txt | 25 + .../protocol-samples/refused/expected.json | 16 + .../same-assembly/S1_forged_token.cs.txt | 13 + .../same-assembly/S2_internal_setter.cs.txt | 15 + .../S3_object_initializer.cs.txt | 12 + .../same-assembly/S4_read_only_use.cs.txt | 15 + .../S5_mentions_are_not_operations.cs.txt | 14 + .../same-assembly/expected.json | 7 + scripts/protocol_gate.py | 519 ++++++++ 76 files changed, 3627 insertions(+), 3 deletions(-) create mode 100644 frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs create mode 100644 frontend/roslyn/protocol-samples/Api/ProtocolApi.cs create mode 100644 frontend/roslyn/protocol-samples/cases/A1_alias_of_entity.cs create mode 100644 frontend/roslyn/protocol-samples/cases/A2_entity_through_indexer.cs create mode 100644 frontend/roslyn/protocol-samples/cases/G6_capability_not_spent.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L10_region_inside_try.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L11_region_beside_disposable.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L13_values_captured_into_region.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L2_copied_capability.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L3_raw_mutation.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L4_raw_escape.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L5_normal_transition.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L6_two_transitions.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L6b_chained_transitions.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L7_read_after_region.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L8_branch_inside_region.cs create mode 100644 frontend/roslyn/protocol-samples/cases/L9_opaque_calls_around_region.cs create mode 100644 frontend/roslyn/protocol-samples/cases/X1_nested_region_same_entity.cs create mode 100644 frontend/roslyn/protocol-samples/cases/expected.json create mode 100644 frontend/roslyn/protocol-samples/does-not-compile/C1_construct_token.cs.txt create mode 100644 frontend/roslyn/protocol-samples/does-not-compile/C2_capture_token_in_nested_lambda.cs.txt create mode 100644 frontend/roslyn/protocol-samples/does-not-compile/C3_token_escapes_to_outer_local.cs.txt create mode 100644 frontend/roslyn/protocol-samples/does-not-compile/C4_token_in_field.cs.txt create mode 100644 frontend/roslyn/protocol-samples/does-not-compile/C5_async_region.cs.txt create mode 100644 frontend/roslyn/protocol-samples/does-not-compile/expected.json create mode 100644 frontend/roslyn/protocol-samples/efcore/.gitignore create mode 100644 frontend/roslyn/protocol-samples/efcore/Acceptance/Acceptance.csproj create mode 100644 frontend/roslyn/protocol-samples/efcore/Acceptance/Program.cs create mode 100644 frontend/roslyn/protocol-samples/efcore/OrderBackend/Data/AppDbContext.cs create mode 100644 frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/Order.cs create mode 100644 frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs create mode 100644 frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderBackend.csproj create mode 100644 frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs create mode 100644 frontend/roslyn/protocol-samples/efcore/OrderBackend/Program.cs create mode 100644 frontend/roslyn/protocol-samples/efcore/accepted/MentionsOnly.cs.txt create mode 100644 frontend/roslyn/protocol-samples/efcore/accepted/expected.json create mode 100644 frontend/roslyn/protocol-samples/efcore/known-gaps/StateWrittenByBulkUpdate.cs.txt create mode 100644 frontend/roslyn/protocol-samples/efcore/known-gaps/StateWrittenThroughChangeTracker.cs.txt create mode 100644 frontend/roslyn/protocol-samples/efcore/known-gaps/expected.json create mode 100644 frontend/roslyn/protocol-samples/efcore/nuget.config create mode 100644 frontend/roslyn/protocol-samples/efcore/refused/DirectMutator.cs.txt create mode 100644 frontend/roslyn/protocol-samples/efcore/refused/ForgedToken.cs.txt create mode 100644 frontend/roslyn/protocol-samples/efcore/refused/MutatorAsDelegate.cs.txt create mode 100644 frontend/roslyn/protocol-samples/efcore/refused/SaveInsideRegion.cs.txt create mode 100644 frontend/roslyn/protocol-samples/efcore/refused/ShipWithLogInsideRegion.cs.txt create mode 100644 frontend/roslyn/protocol-samples/efcore/refused/expected.json create mode 100644 frontend/roslyn/protocol-samples/refused/R10_property_getter_inside_region.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R11_constructor_inside_region.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R12_default_token_temporary.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R13_public_mutator_reaches_protocol_state.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R14_public_setter_of_protocol_state.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R1_default_token.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R2_token_outside_region.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R3_return_inside_region.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R4_loop_inside_region.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R5_token_passed_to_helper.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R6_region_body_not_a_lambda.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R7_entity_not_a_local.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R8_token_not_a_ref_struct.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/R9_opaque_call_inside_region.cs.txt create mode 100644 frontend/roslyn/protocol-samples/refused/expected.json create mode 100644 frontend/roslyn/protocol-samples/same-assembly/S1_forged_token.cs.txt create mode 100644 frontend/roslyn/protocol-samples/same-assembly/S2_internal_setter.cs.txt create mode 100644 frontend/roslyn/protocol-samples/same-assembly/S3_object_initializer.cs.txt create mode 100644 frontend/roslyn/protocol-samples/same-assembly/S4_read_only_use.cs.txt create mode 100644 frontend/roslyn/protocol-samples/same-assembly/S5_mentions_are_not_operations.cs.txt create mode 100644 frontend/roslyn/protocol-samples/same-assembly/expected.json create mode 100644 scripts/protocol_gate.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 200a4ff7..a2af56b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3279,6 +3279,60 @@ jobs: fi echo "OK: Tier B A/B — external event OWN050 (no ref) -> OWN001 (with --ref-dir)" + # P-010 pillar 9, first slice: C# state protocols over OwnIR v1 (`move`, + # `borrow_mut`). The Layer 2/3 ledgers freeze the FACTS the Roslyn lowering emits, + # and both engines replay them with zero dotnet — which proves the engines agree + # on those documents and nothing about where the documents came from. This job is + # the other half: the real extractor over the real C#, so a frozen fact cannot + # drift away from the program it claims to describe. + # + # It also carries what no frozen fact can: programs the extractor must REFUSE + # (code with no contract inside a region, a protocol with a public mutator, a + # token made by hand in the protocol's own assembly), programs the C# compiler + # must reject, and an ASP.NET Core + EF Core backend driven over real HTTP against + # a real SQLite file — the transition has to reach the instance EF tracks and the + # row it saves, not merely type-check. + # + # Both platforms: the fixtures carry repo-relative `file` paths, and a path that + # is only right with one separator is the kind of thing a single runner hides. + state-protocols: + name: state protocols (C# -> OwnIR v1, real ASP.NET Core + EF Core backend) + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: "3.13" + - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + with: + dotnet-version: "8.0.x" + - uses: dtolnay/rust-toolchain@fa04a1451ff1842e2626ccb99004d0195b455a88 # master, 2026-07-10 + with: + toolchain: stable + - name: Build own-cli (the second public engine) + if: matrix.os == 'ubuntu-latest' + working-directory: rust + run: cargo build -p own-cli + # One script, five questions; see its docstring. + - name: The protocol gate (cases, refusals, compiler rejections, one assembly, EF backend) + if: matrix.os != 'ubuntu-latest' + run: python scripts/protocol_gate.py + # On Linux it also compares the two PUBLIC CLIs on every C#-derived document: + # exit code, stdout and stderr, byte for byte. Linux only, for the reason + # #261 recorded: byte parity with the NATIVE-Windows Python reference is not + # claimed (it writes cp1252/CRLF), so that comparison would measure the + # console, not the engines. + - name: The protocol gate + both public CLIs on the C#-derived documents + if: matrix.os == 'ubuntu-latest' + run: python scripts/protocol_gate.py --rust "$PWD/rust/target/debug/own-cli" + # P-012 slice 1: score the checker against the labeled corpus on REAL C# — not # just the .own reduction tests/test_corpus.py checks. Per case: the bug must be # CAUGHT in before.cs (recall) and the fix must be SILENT in after.cs diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 8b6599fd..7913a2a6 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -318,7 +318,7 @@ own scan. Label them as estimates wherever they appear. | [P-007](proposals/P-007-arraypool-span.md) | ArrayPool / Span borrow-view | P1 | in progress (POOL001–003 built; 004/005 first slices) | | [P-008](proposals/P-008-effects-and-resources.md) | Effects & resources (`Own.Effects`) | P1/P2 | draft | | [P-009](proposals/P-009-nogc-regions.md) | No-GC / allocation-free regions | horizon | draft | -| [P-010](proposals/P-010-type-disciplines.md) | Richer type disciplines (`Own.Types`) | P2/horizon | draft | +| [P-010](proposals/P-010-type-disciplines.md) | Richer type disciplines (`Own.Types`) | P2/horizon | draft; pillar 9 first slice built | | [P-011](proposals/P-011-editor-tooling.md) | Editor tooling & syntax highlighting | side-track | draft | | [P-012](proposals/P-012-bug-corpus-mining.md) | Real-world bug corpus & mining | enabling | in progress (corpus benchmark + real-world cases, CI-gated) | | [P-013](proposals/P-013-distribution-surface.md) | Distribution surface (CI Action + dotnet tool) | enabling | v0 built | diff --git a/docs/proposals/P-010-type-disciplines.md b/docs/proposals/P-010-type-disciplines.md index 10222da3..6143e377 100644 --- a/docs/proposals/P-010-type-disciplines.md +++ b/docs/proposals/P-010-type-disciplines.md @@ -1,6 +1,8 @@ # P-010 — Richer type disciplines (`Own.Types`) -- **Status:** draft (horizon) +- **Status:** draft (horizon); pillar 9 (typestate / protocols) has a first slice built — + the C# state-protocol profile over OwnIR v1 (`move`, `borrow_mut`), see + [the extractor's README](../../frontend/roslyn/README.md#state-protocols---flow-locals-ownir-v1) - **Depends on:** `spec/OwnCore.md` (the ownership/affine core and its fact vocabulary), `spec/Lifetimes.md`; relates to P-005 (`IDisposable` typestate — the first concrete protocol), P-006 (DI lifetimes — a region contract, not a @@ -173,6 +175,20 @@ directly, rather than making every call site spell out a bespoke `union`. Typestate is also the generalization that subsumes **session types** (typed message-ordering protocols) as the special case where the object is a channel. + **First slice, built.** Not the `protocol` declaration above but its C# + surface: a state is a `[ProtocolToken]` ref struct over an ordinary entity, a + `[ProtocolRegion]` method opens an exclusive region and hands out the token, + and a transition is a method on the token. The Roslyn frontend lowers a + region to `borrow_mut` and a token copy to `move` (OwnIR v1, + [`spec/OwnIR.md` §5.3](../../spec/OwnIR.md)); the existing ownership core + gives the verdicts — a stale token is OWN002, a copied one OWN005, a raw + touch of the entity inside the region OWN013 — on both engines. It works on + the instance an ORM tracks, with no base class, repository or custom LINQ + (`frontend/roslyn/protocol-samples/efcore`). What the slice does **not** have: a declared + state graph (the transitions are whatever the tokens' methods are), a + generator for the tokens, the affine view of a token (an unspent one is + OWN001 today), and any claim over writes that bypass the entity's C# surface. + 10. **Owned / borrowed / must-dispose.** Already built as a standalone diagnostic in [P-005](P-005-idisposable-ownership.md) — Own.NET already treats `IDisposable` as typestate C# lacks. This pillar's job is *not* to diff --git a/docs/proposals/README.md b/docs/proposals/README.md index d1381252..e7d6fbe0 100644 --- a/docs/proposals/README.md +++ b/docs/proposals/README.md @@ -31,7 +31,7 @@ proposal is marked `done` with a pointer. | [P-007](P-007-arraypool-span.md) | ArrayPool / Span borrow-view | in progress (POOL001–003 built; 004/005 first slices) | | [P-008](P-008-effects-and-resources.md) | Effects & resources (`Own.Effects`) | draft | | [P-009](P-009-nogc-regions.md) | No-GC / allocation-free regions | draft | -| [P-010](P-010-type-disciplines.md) | Richer type disciplines (`Own.Types`) | draft | +| [P-010](P-010-type-disciplines.md) | Richer type disciplines (`Own.Types`) | draft; pillar 9 first slice built (C# state protocols over OwnIR v1) | | [P-011](P-011-editor-tooling.md) | Editor tooling & syntax highlighting | draft | | [P-012](P-012-bug-corpus-mining.md) | Real-world bug corpus & mining | in progress (corpus benchmark + real-world cases, CI-gated) | | [P-013](P-013-distribution-surface.md) | Distribution surface (how people run Own.NET) | v0 built (CI/Action + dotnet tool) | diff --git a/frontend/roslyn/OwnSharp.Extractor/Program.cs b/frontend/roslyn/OwnSharp.Extractor/Program.cs index 18f91faf..1a3ae309 100644 --- a/frontend/roslyn/OwnSharp.Extractor/Program.cs +++ b/frontend/roslyn/OwnSharp.Extractor/Program.cs @@ -5629,6 +5629,11 @@ static bool IsPublicCtor(SyntaxTokenList modifiers) var components = new List(); // P-016 B0b/B2: per-method flow bodies (only when --flow-locals). var flowFunctions = new List(); +// The names the IDisposable flow pass emitted a record for. The protocol lowering +// (ProtocolLowering.cs) reads it so that a method with BOTH readings keeps both under +// distinct names: the two are not merged, and two records under one name would read +// as an overload. +var flowRecordNames = new HashSet(StringComparer.Ordinal); // Parse every input into a syntax tree first (keeping the file path we report // it under), then build ONE compilation over all of them so the SemanticModel @@ -7175,6 +7180,7 @@ or ImplicitObjectCreationExpressionSyntax } init if (guardedFacts is not null) record["guarded_facts"] = guardedFacts; flowFunctions.Add(record); + flowRecordNames.Add(fname); } if (subs.Count > 0) @@ -7205,6 +7211,22 @@ or ImplicitObjectCreationExpressionSyntax } init // `stats` is additive coverage metadata — the core's load() ignores unknown keys. // P-006: the DI registration + ctor graph (empty when the scan has no // Add{Singleton,Scoped,Transient} calls). ownlang/di.py turns it into DI001. +// P-010 pillar 9: state-protocol regions -> `borrow_mut` / `move` flow records (OwnIR v1). A +// lowering, not an analysis (see ProtocolLowering.cs). A shape it cannot lower is a +// REFUSAL of the whole run (exit 2, the same tier as the guarded-fact self-check below): +// the alternative is a region that silently drops out of the facts and reads as clean. +if (flowLocals) +{ + var protocol = ProtocolLowering.Lower(compilation, parsed, flowRecordNames); + if (protocol.Refusals.Count > 0) + { + foreach (var refusal in protocol.Refusals) + Console.Error.WriteLine($"extractor: protocol lowering refused: {refusal}"); + return 2; + } + flowFunctions.AddRange(protocol.Functions); +} + var factServices = ExtractServices(parsed); var factStats = new { diff --git a/frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs b/frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs new file mode 100644 index 00000000..4a5fa1d3 --- /dev/null +++ b/frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs @@ -0,0 +1,1180 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +// State protocols (P-010, pillar 9): lowering a C# state-protocol surface into OwnIR `move` / +// `borrow_mut` (OwnIR v1, spec/OwnIR.md §5.3; the bridge's side is spec/Bridge.md BR-L12/L13). +// +// The surface is recognised by two attributes, matched by NAME (so an API carries no +// dependency on this project): +// +// [ProtocolRegion] a method `(entity, callback)`: the callback's body is an exclusive +// region over the entity, and its one parameter is the state token; +// [ProtocolToken] a ref struct: every instance METHOD is a transition that consumes the +// token (and borrows the entity for the call); a PROPERTY is a read. +// +// This is a LOWERING, not an analysis. It reads the syntax of one method at a time and looks +// symbols up; it never tracks state across statements, never follows aliases, and never +// decides whether a program is right. Everything it emits is checked by the core: +// +// region entry acquire ; borrow_mut as { acquire ; ... }; release +// token.Transition() call [, ] (+ acquire ) +// var copy = token move <- +// token.Property use +// a value of the entity's TYPE mentioned inside the region +// use (exclusivity by type) +// +// WHAT IS CLAIMED. The profile protects the local C# capabilities and aliases of an entity +// that already exists: inside a region nothing but its token changes it, and a token is spent +// once. It does not protect the persisted row from other ways of changing it — a set-based +// update, a change-tracker metadata write, raw SQL, another process. Those belong to +// concurrency tokens, constraints and transactions. +// +// OUTSIDE a region nothing protocol-relevant is live, and a region lowers to a self-contained +// unit (the entity is acquired and released around it). So whatever surrounds a region — +// `try`/`catch`, `using`, a loop, a branch, `await` — is walked through and ignored. +// +// INSIDE a region the rule is the opposite: default-deny. The region is exclusive, and code +// the lowering cannot read is code that may reach the entity some other way. So the body may +// contain transitions, token reads, copies of a token, `if`, locals, and expressions made of +// literals, locals, fields and built-in operators — and nothing else. Every other construct is +// REFUSED (the run exits 2 and writes no facts), most importantly anything that runs code with +// no stated contract: a call, a constructor, a property getter or setter, an indexer, a +// user-defined operator or conversion. The one exception is a construct that visibly touches +// the entity: that is lowered as a use of it, and the core rejects it with a verdict rather +// than a refusal. When effect summaries (P-036/P-037) can prove a call harmless, this rule is +// the one to relax; until then an unprovable call is not analysed as safe. +// +// Also refused, anywhere: a token with no lowerable origin (`default`, `new`), a token +// parameter or return type, a token type that is not a ref struct, a region whose body is not +// a lambda written in place, a region over something that is not a local or a parameter, and +// a region entry in a position the statement walk does not reach. Those keep the shapes the +// core cannot check (a token minted outside a region, two tokens for one entity, a token spent +// against another entity) from having any spelling that lowers. +// +// And refused across the whole scan, region or no region: a protocol that is not ADMITTED (a +// public mutator of the state its transitions own), and reaching past an admitted one (see +// `CheckBoundary`). A refusal is the whole scan's: a recognised protocol construct that +// cannot be lowered safely is not reported next to findings that would read as "the rest is +// checked". +// +// BINDING. The scan is not an MSBuild compilation: it does not read `obj/`, so the usings a +// project gets implicitly are not there. A region whose entity does not bind is refused, not +// skipped; a project that relies on implicit usings writes them out (or qualifies the API). +internal static class ProtocolLowering +{ + internal sealed record Result(List Functions, List Refusals); + + private sealed class Refused(string message) : Exception(message); + + private sealed class Region + { + public required ISymbol Entity { get; init; } + public required ITypeSymbol? EntityType { get; init; } + public required string Owner { get; init; } + public required string Binding { get; init; } + } + + private sealed class Ctx + { + public required SemanticModel Model { get; init; } + public required string File { get; init; } + public required Dictionary Files { get; init; } + public required SortedDictionary Contracts { get; init; } + public required ISet LegacyNames { get; init; } + public List Regions { get; } = new(); + public Dictionary Tokens { get; } = + new(SymbolEqualityComparer.Default); + public HashSet LoweredEntries { get; } = new(); + public int Counter; + /// How many times the scan has lowered a mention of a borrowed entity. A construct + /// that runs unknown code is refused UNLESS it moved this counter: then the core + /// already has a use to reject, and a verdict is better than a refusal. + public int EntityTouches; + } + + public static Result Lower(Compilation compilation, + IReadOnlyList<(string file, SyntaxTree tree)> parsed, + ISet legacyNames) + { + var functions = new List(); + var refusals = new List(); + var contracts = new SortedDictionary(StringComparer.Ordinal); + var files = new Dictionary(); + foreach (var (file, tree) in parsed) + files[tree] = file; + + CheckBoundary(compilation, parsed, files, refusals); + + foreach (var (file, tree) in parsed) + { + var model = compilation.GetSemanticModel(tree); + var root = tree.GetRoot(); + var loweredEntries = new HashSet(); + + foreach (var method in root.DescendantNodes().OfType()) + { + if (model.GetDeclaredSymbol(method) is not IMethodSymbol msym) + continue; + if (IsApiType(msym.ContainingType)) + continue; // the protocol API itself: its tokens' own bodies are not user code + try + { + foreach (var p in msym.Parameters) + if (IsToken(p.Type)) + throw new Refused(At(file, method, + $"protocol token parameter '{p.Name}': a token does not cross a method boundary in this profile")); + if (IsToken(msym.ReturnType)) + throw new Refused(At(file, method, + "a method returns a protocol token: a token does not cross a method boundary in this profile")); + + SyntaxNode? body = (SyntaxNode?)method.Body ?? method.ExpressionBody; + if (body is null) + continue; + var entries = body.DescendantNodes().OfType() + .Where(i => IsRegionEntry(MethodOf(model, i))).ToList(); + var tokenLocals = body.DescendantNodes().OfType() + .Where(v => model.GetDeclaredSymbol(v) is ILocalSymbol l && IsToken(l.Type)) + .ToList(); + if (entries.Count == 0 && tokenLocals.Count == 0) + continue; + if (method.Body is null) + throw new Refused(At(file, method, + "a method that opens a protocol region needs a block body")); + + // A method the IDisposable flow pass ALSO has a record for keeps both: the + // two track disjoint resources (a disposable captured by the callback is + // already an escape there), so they are two independent readings of one + // method. They cannot share a name — two records under one name read as + // an overload — so this one says which reading it is. + var name = $"{msym.ContainingType.ToDisplayString()}.{msym.Name}"; + if (legacyNames.Contains(name)) + name += "$protocol"; + + var ctx = new Ctx + { + Model = model, File = file, Files = files, Contracts = contracts, + LegacyNames = legacyNames, + }; + var ops = new List(); + foreach (var st in method.Body.Statements) + LowerOutside(ctx, st, ops); + + // Fail closed on anything the statement walk did not reach: a region entry + // or a token local it never saw would otherwise vanish from the facts. + foreach (var e in entries) + if (!ctx.LoweredEntries.Contains(e)) + throw new Refused(At(file, e, + "a protocol region entry in a position the lowering does not model (it must be a statement of its own, outside any lambda or local function)")); + foreach (var v in tokenLocals) + if (model.GetDeclaredSymbol(v) is ILocalSymbol l && !ctx.Tokens.ContainsKey(l)) + throw new Refused(At(file, v, + $"protocol token '{l.Name}' is declared outside a protocol region")); + loweredEntries.UnionWith(ctx.LoweredEntries); + + if (ops.Count > 0) + functions.Add(new Dictionary + { + ["name"] = name, ["file"] = file, ["body"] = ops, + }); + } + catch (Refused r) + { + refusals.Add(r.Message); + } + } + + // A region entry outside any method body (an accessor, a field initializer, a + // top-level statement) is not modelled either. + foreach (var inv in root.DescendantNodes().OfType()) + { + if (!IsRegionEntry(MethodOf(model, inv)) || loweredEntries.Contains(inv)) + continue; + if (inv.Ancestors().OfType().Any()) + continue; // inside a method: lowered above, or already refused there + refusals.Add(At(file, inv, + "a protocol region entry outside a method body is not modelled")); + } + } + + functions.AddRange(contracts.Values); + return new Result(functions, refusals); + } + + // ---- the protocol boundary ------------------------------------------------------------- + + /// What the region rules REST on: a token exists only because a region entry or a + /// transition handed it out, and the protocol's state changes only through a token. With + /// the protocol in its own assembly the C# compiler holds most of that through `internal`. + /// In ONE assembly — a minimal API with its domain beside the handlers — `internal` holds + /// nothing: `new ApprovedOrder(order).Ship()` and `order.MarkShipped()` compile in a method + /// with no region at all. A second assembly is not something this profile gets to demand, + /// so the scan holds the boundary itself, in two halves. + /// + /// ADMISSION — a property of the declarations. The protocol-owned state is what the + /// protocol's own types write on the entity, directly or through the entity methods they + /// call. An entity is admitted only if nothing PUBLIC on it writes that state: a public + /// setter or field, or a public method that reaches such a write. A public mutator is a + /// transition nobody declared, so the declaration is refused instead of the bypass being + /// allowed in silence. A write inside an object initializer is creation, not mutation. + /// + /// USE — a property of every other line of the scan. Outside the protocol's own types + /// three OPERATIONS are refused: creating a token value (`new`, `default`); invoking a + /// non-public method or constructor of the protocol or of the entity (a method group + /// counts — the delegate is the call); writing a field, property or event of either that + /// is not publicly writable. A MENTION is not an operation: `nameof`, `typeof`, a read. + /// + /// The entity types are read off the region entries' first parameter, with their base + /// classes (inherited state is state). Nothing is annotated. Both halves read bodies, so + /// the protocol and its entity must be in the scan as SOURCE: one that arrives only as a + /// compiled reference cannot be admitted, and a region over it is refused. The inside of a + /// region is left to the region's own scan, which already answers for every node there. + private static void CheckBoundary(Compilation compilation, + IReadOnlyList<(string file, SyntaxTree tree)> parsed, + Dictionary files, + List refusals) + { + var api = new HashSet(SymbolEqualityComparer.Default); + var entities = new HashSet(SymbolEqualityComparer.Default); + var regionBodies = new HashSet(); + + void AddToken(ITypeSymbol? type) + { + if (type?.OriginalDefinition is not INamedTypeSymbol named || !IsToken(named) + || !api.Add(named)) + return; + foreach (var m in named.GetMembers().OfType()) + AddToken(m.ReturnType); // what a transition hands back is a token too + } + + // Returns the protocol declarations this entry needs that are NOT in the scan as source. + List AddEntry(IMethodSymbol entry) + { + entry = (entry.ReducedFrom ?? entry).OriginalDefinition; + var compiled = new List(); + void NeedSource(ISymbol symbol) + { + if (symbol.DeclaringSyntaxReferences.IsEmpty) + compiled.Add(symbol.Name); + } + api.Add(entry.ContainingType.OriginalDefinition); + NeedSource(entry); + // the entity and what it inherits its state from + var first = true; + for (var entity = entry.Parameters.Length > 0 + ? entry.Parameters[0].Type.OriginalDefinition as INamedTypeSymbol : null; + entity is { TypeKind: not TypeKind.Error, SpecialType: not SpecialType.System_Object }; + entity = entity.BaseType?.OriginalDefinition, first = false) + { + entities.Add(entity); + if (first) + NeedSource(entity); + } + foreach (var p in entry.Parameters) + if (p.Type is INamedTypeSymbol { DelegateInvokeMethod: { } invoke }) + foreach (var q in invoke.Parameters) + { + AddToken(q.Type); + if (IsToken(q.Type)) + NeedSource(q.Type.OriginalDefinition); + } + return compiled; + } + + foreach (var (file, tree) in parsed) + { + var model = compilation.GetSemanticModel(tree); + foreach (var node in tree.GetRoot().DescendantNodes()) + { + switch (node) + { + case BaseTypeDeclarationSyntax type + when model.GetDeclaredSymbol(type) is INamedTypeSymbol declared: + AddToken(declared); + foreach (var m in declared.GetMembers().OfType()) + if (IsRegionEntry(m)) + AddEntry(m); + break; + case InvocationExpressionSyntax inv + when MethodOf(model, inv) is { } method && IsRegionEntry(method): + var compiled = AddEntry(method); + if (compiled.Count > 0) + refusals.Add(At(file, inv, + $"the state protocol behind this region is not in the scan as source ({string.Join(", ", compiled.Distinct())} come from a compiled reference): its admission cannot be checked, so the region is not analysed")); + foreach (var arg in inv.ArgumentList.Arguments) + if (Unparen(arg.Expression) is AnonymousFunctionExpressionSyntax body) + regionBodies.Add(body); + break; + } + } + } + if (api.Count == 0) + return; + + bool InFamily(ISymbol? member) => + member?.ContainingType?.OriginalDefinition is { } owner && entities.Contains(owner); + + string Where(ISymbol symbol) + { + var reference = symbol.DeclaringSyntaxReferences.FirstOrDefault(); + return reference is not null && files.TryGetValue(reference.SyntaxTree, out var f) + ? $"{f}:{Line(reference.GetSyntax())}" + : "?:0"; + } + + // ---- admission --------------------------------------------------------------------- + + // What one body does to the entity family: the state it writes and the family methods + // it reaches. Read from syntax, memoized, never across an object initializer. + var effects = new Dictionary Writes, HashSet Calls)>( + SymbolEqualityComparer.Default); + (HashSet Writes, HashSet Calls) Effects(IMethodSymbol method) + { + if (effects.TryGetValue(method, out var known)) + return known; + var writes = new HashSet(SymbolEqualityComparer.Default); + var calls = new HashSet(SymbolEqualityComparer.Default); + effects[method] = (writes, calls); + foreach (var reference in method.DeclaringSyntaxReferences) + { + var body = reference.GetSyntax(); + var model = compilation.GetSemanticModel(body.SyntaxTree); + foreach (var name in body.DescendantNodesAndSelf().OfType()) + { + if (InNameof(model, name)) + continue; + var symbol = Bound(model.GetSymbolInfo(name))?.OriginalDefinition; + if (!InFamily(symbol)) + continue; + switch (symbol) + { + case IMethodSymbol { MethodKind: not MethodKind.Constructor } callee: + calls.Add((callee.ReducedFrom ?? callee).OriginalDefinition); + break; + case IFieldSymbol or IPropertySymbol or IEventSymbol + when IsWrite(name) && !InObjectInitializer(name): + writes.Add(symbol); + if (symbol is IPropertySymbol { SetMethod: { } setter }) + calls.Add(setter.OriginalDefinition); // a setter with a body + break; + } + } + } + return (writes, calls); + } + + // Everything `root` writes, through the family methods it reaches. + HashSet Reach(IMethodSymbol root, List? unreadable = null) + { + var all = new HashSet(SymbolEqualityComparer.Default); + var seen = new HashSet(SymbolEqualityComparer.Default); + var queue = new Queue(); + queue.Enqueue(root); + while (queue.Count > 0) + { + var method = queue.Dequeue(); + if (!seen.Add(method)) + continue; + if (method.DeclaringSyntaxReferences.IsEmpty && !method.IsImplicitlyDeclared) + unreadable?.Add($"{method.ContainingType.Name}.{method.Name}"); + var (writes, calls) = Effects(method); + all.UnionWith(writes); + foreach (var callee in calls) + queue.Enqueue(callee); + } + return all; + } + + var owned = new HashSet(SymbolEqualityComparer.Default); + foreach (var type in api) + foreach (var member in type.GetMembers().OfType()) + { + var unreadable = new List(); + owned.UnionWith(Reach(member, unreadable)); + foreach (var name in unreadable.Distinct()) + refusals.Add($"{Where(member)}: the protocol calls '{name}', whose body is not in the scan: what it does to the entity cannot be read, so the protocol is not admitted"); + } + + string Open(ISymbol member, ISymbol state) => + $"{Where(member)}: '{member.ContainingType.Name}.{member.Name}' is public and writes '{state.Name}', state the protocol's transitions own: a public mutator is a transition nobody declared, and every token can be bypassed through it. Make it non-public, or make it a transition"; + + foreach (var entity in entities) + foreach (var member in entity.GetMembers()) + switch (member) + { + case IPropertySymbol property when owned.Contains(property) + && property.SetMethod is { DeclaredAccessibility: Accessibility.Public, IsInitOnly: false }: + refusals.Add(Open(property, property)); + break; + case IFieldSymbol { DeclaredAccessibility: Accessibility.Public, IsReadOnly: false, IsConst: false } field + when owned.Contains(field): + refusals.Add(Open(field, field)); + break; + case IMethodSymbol { DeclaredAccessibility: Accessibility.Public } method + when method.MethodKind is MethodKind.Ordinary or MethodKind.PropertySet + && !method.IsInitOnly + && Reach(method).FirstOrDefault(owned.Contains) is { } state: + // an auto-property's setter is the property case above + if (method.AssociatedSymbol is null || !owned.Contains(method.AssociatedSymbol)) + refusals.Add(Open(method.AssociatedSymbol ?? method, state)); + break; + } + + // ---- use --------------------------------------------------------------------------- + + // the names worth resolving: cheap, and exact enough to keep a large scan large + var guarded = new HashSet(StringComparer.Ordinal); + foreach (var type in api.Concat(entities)) + foreach (var member in type.GetMembers()) + if (member.DeclaredAccessibility != Accessibility.Public + || member is IPropertySymbol { SetMethod.DeclaredAccessibility: not Accessibility.Public }) + guarded.Add(member.Name); + + foreach (var (file, tree) in parsed) + { + var model = compilation.GetSemanticModel(tree); + foreach (var node in tree.GetRoot().DescendantNodes()) + { + var creates = node is BaseObjectCreationExpressionSyntax or DefaultExpressionSyntax + || node.IsKind(SyntaxKind.DefaultLiteralExpression); + var names = node is SimpleNameSyntax name && guarded.Contains(name.Identifier.Text); + if (!creates && !names) + continue; + if (node.Ancestors().Any(regionBodies.Contains)) + continue; // the region's own scan answers for this node + + var site = Enclosing(model, node); + bool Within(HashSet types) => site.Any(types.Contains); + // may code at this site reach a non-public member of `owner`? + bool Outside(INamedTypeSymbol? owner) + { + owner = owner?.OriginalDefinition; + if (owner is null) + return false; + if (api.Contains(owner)) + return !Within(api); + return entities.Contains(owner) && !Within(api) && !Within(entities); + } + + if (creates) + { + var info = model.GetTypeInfo(node); + var made = (node.IsKind(SyntaxKind.DefaultLiteralExpression) + ? info.ConvertedType : info.Type ?? info.ConvertedType)?.OriginalDefinition; + if (IsToken(made) && !Within(api)) + refusals.Add(At(file, node, + $"a protocol token '{made!.Name}' is created outside the protocol's own types: a token comes only from a region entry or a transition")); + else if (node is BaseObjectCreationExpressionSyntax + && Bound(model.GetSymbolInfo(node)) is IMethodSymbol + { DeclaredAccessibility: not Accessibility.Public } ctor + && Outside(ctor.ContainingType)) + refusals.Add(At(file, node, + Sealed(ctor.ContainingType, "the constructor", "it is invoked here"))); + continue; + } + if (InNameof(model, node)) + continue; // a mention, not an operation + + var member = Bound(model.GetSymbolInfo(node)); + member = member is IMethodSymbol method + ? (method.ReducedFrom ?? method).OriginalDefinition + : member?.OriginalDefinition; + var operation = member switch + { + IMethodSymbol { DeclaredAccessibility: not Accessibility.Public } => "it is called here", + IPropertySymbol property when IsWrite(node) + && (property.SetMethod?.DeclaredAccessibility ?? property.DeclaredAccessibility) + != Accessibility.Public => "it is written here", + IFieldSymbol or IEventSymbol + when IsWrite(node) && member.DeclaredAccessibility != Accessibility.Public + => "it is written here", + _ => null, // a read, or a public member: not this rule's business + }; + if (operation is not null && Outside(member!.ContainingType)) + refusals.Add(At(file, node, + Sealed(member.ContainingType, $"'{member.Name}'", operation))); + } + } + + static string Sealed(INamedTypeSymbol owner, string what, string operation) => + $"{what} of '{owner.Name}' is not public and belongs to a state protocol: {operation}, outside the protocol's own types, where it is reachable only through a token (the scan holds the protocol to the boundary a separate assembly would give it)"; + } + + private static ISymbol? Bound(SymbolInfo info) => + info.Symbol ?? (info.CandidateSymbols.Length == 1 ? info.CandidateSymbols[0] : null); + + /// The types `node` sits in, innermost first. + private static List Enclosing(SemanticModel model, SyntaxNode node) + { + var chain = new List(); + var symbol = model.GetEnclosingSymbol(node.SpanStart); + for (var type = symbol as INamedTypeSymbol ?? symbol?.ContainingType; + type is not null; type = type.ContainingType) + chain.Add(type.OriginalDefinition); + return chain; + } + + /// `nameof(x)` names a symbol and runs nothing. + private static bool InNameof(SemanticModel model, SyntaxNode node) => + node.Ancestors().OfType().Any(inv => + inv.Expression is IdentifierNameSyntax { Identifier.Text: "nameof" } + && model.GetConstantValue(inv).HasValue); + + /// The expression a name is the tail of: `a.b.Name` for `Name`, the name itself otherwise. + private static SyntaxNode Target(SyntaxNode name) + { + var target = name; + if (name.Parent is MemberAccessExpressionSyntax access && access.Name == name) + target = access; + while (target.Parent is ParenthesizedExpressionSyntax paren) + target = paren; + return target; + } + + /// Is this name the target of a write: an assignment (an object initializer's member is + /// one), `++`/`--`, or a `ref`/`out` argument. + private static bool IsWrite(SyntaxNode name) + { + var target = Target(name); + return target.Parent switch + { + AssignmentExpressionSyntax assignment => assignment.Left == target, + PrefixUnaryExpressionSyntax or PostfixUnaryExpressionSyntax => + target.Parent.IsKind(SyntaxKind.PreIncrementExpression) + || target.Parent.IsKind(SyntaxKind.PreDecrementExpression) + || target.Parent.IsKind(SyntaxKind.PostIncrementExpression) + || target.Parent.IsKind(SyntaxKind.PostDecrementExpression), + ArgumentSyntax argument => !argument.RefKindKeyword.IsKind(SyntaxKind.None) + && !argument.RefKindKeyword.IsKind(SyntaxKind.InKeyword), + _ => false, + }; + } + + /// A member assigned in `new T { Member = ... }` (or `with { ... }`): part of creating the + /// value, not a change to one that exists. + private static bool InObjectInitializer(SyntaxNode name) => + Target(name).Parent is AssignmentExpressionSyntax + { + Parent: InitializerExpressionSyntax initializer + } && (initializer.IsKind(SyntaxKind.ObjectInitializerExpression) + || initializer.IsKind(SyntaxKind.WithInitializerExpression)); + + // ---- recognition ---------------------------------------------------------------------- + + private static bool HasAttribute(ISymbol? symbol, string name) => + symbol is not null && symbol.GetAttributes().Any(a => + a.AttributeClass?.Name == name || a.AttributeClass?.Name == name + "Attribute"); + + private static bool IsToken(ITypeSymbol? type) => HasAttribute(type, "ProtocolToken"); + + private static bool IsRegionEntry(IMethodSymbol? method) => + method is not null && HasAttribute((method.ReducedFrom ?? method).OriginalDefinition, + "ProtocolRegion"); + + private static bool IsApiType(INamedTypeSymbol? type) => + type is not null && (IsToken(type) + || type.GetMembers().OfType().Any(IsRegionEntry)); + + /// The method an invocation names. When overload resolution FAILED (an argument did not + /// bind — typically a degraded compilation: a missing reference, a missing global using) + /// the single candidate still says which method was meant, and it is used: a region + /// entry that does not bind must not silently stop being a region entry. Whatever is + /// unresolved inside it is then refused by the region's default-deny scan. + private static IMethodSymbol? MethodOf(SemanticModel model, InvocationExpressionSyntax inv) + { + var info = model.GetSymbolInfo(inv); + if (info.Symbol is IMethodSymbol bound) + return bound; + var candidates = info.CandidateSymbols.OfType().ToList(); + return candidates.Count == 1 ? candidates[0] + : candidates.FirstOrDefault(c => IsRegionEntry(c) || IsToken(c.ContainingType)); + } + + private static ExpressionSyntax Unparen(ExpressionSyntax e) + { + while (e is ParenthesizedExpressionSyntax p) + e = p.Expression; + return e; + } + + private static int Line(SyntaxNode node) => + node.GetLocation().GetLineSpan().StartLinePosition.Line + 1; + + private static string At(string file, SyntaxNode node, string message) => + $"{file}:{Line(node)}: {message}"; + + private static Refused Refuse(Ctx ctx, SyntaxNode node, string message) => + new(At(ctx.File, node, message)); + + /// A type whose values carry no user code: formatting or concatenating one runs nothing + /// the lowering would have to read. + private static bool IsInert(ITypeSymbol? type) => + type is not null && (type.TypeKind == TypeKind.Enum || type.SpecialType is + SpecialType.System_Boolean or SpecialType.System_Char or SpecialType.System_String + or SpecialType.System_SByte or SpecialType.System_Byte + or SpecialType.System_Int16 or SpecialType.System_UInt16 + or SpecialType.System_Int32 or SpecialType.System_UInt32 + or SpecialType.System_Int64 or SpecialType.System_UInt64 + or SpecialType.System_Single or SpecialType.System_Double + or SpecialType.System_Decimal or SpecialType.System_DateTime); + + // ---- ops ------------------------------------------------------------------------------ + + private static Dictionary Acquire(string name, int line) => + new() { ["op"] = "acquire", ["var"] = name, ["line"] = line }; + + private static Dictionary Release(string name, int line) => + new() { ["op"] = "release", ["var"] = name, ["line"] = line }; + + private static Dictionary Use(string name, int line) => + new() { ["op"] = "use", ["var"] = name, ["line"] = line }; + + private static Dictionary Move(string name, string src, int line) => + new() { ["op"] = "move", ["var"] = name, ["src"] = src, ["line"] = line }; + + private static Dictionary Call(string callee, string[] args, int line) => + new() { ["op"] = "call", ["callee"] = callee, ["args"] = args, ["line"] = line }; + + private static void Touch(Ctx ctx, Region region, SyntaxNode at, List ops) + { + ops.Add(Use(region.Owner, Line(at))); + ctx.EntityTouches++; + } + + // ---- statements OUTSIDE a region ------------------------------------------------------ + + /// Outside a region nothing protocol-relevant is live, so the only statements that matter + /// are region entries — wherever they sit. A region is a self-contained unit, so the + /// control flow around it (a branch, a loop, `try`, `using`) changes no verdict inside + /// it, and the units are emitted in source order. + private static void LowerOutside(Ctx ctx, StatementSyntax st, List ops) + { + if (st is ExpressionStatementSyntax es + && Unparen(es.Expression) is InvocationExpressionSyntax entry + && IsRegionEntry(MethodOf(ctx.Model, entry))) + { + LowerRegion(ctx, entry, ops, st); + return; + } + if (st is LocalFunctionStatementSyntax) + return; // another body; an entry inside it is refused as "not reached" + if (st is LocalDeclarationStatementSyntax ld) + foreach (var v in ld.Declaration.Variables) + if (ctx.Model.GetDeclaredSymbol(v) is ILocalSymbol local && IsToken(local.Type)) + throw Refuse(ctx, v, + $"protocol token '{local.Name}' is declared outside a protocol region"); + foreach (var nested in NestedStatements(st)) + LowerOutside(ctx, nested, ops); + } + + /// The statements directly nested in `node` (through blocks, clauses and sections), in + /// source order, never crossing into a lambda or a local function. + private static IEnumerable NestedStatements(SyntaxNode node) + { + foreach (var child in node.ChildNodes()) + { + if (child is AnonymousFunctionExpressionSyntax) + continue; + if (child is StatementSyntax statement) + yield return statement; + else + foreach (var deeper in NestedStatements(child)) + yield return deeper; + } + } + + // ---- statements INSIDE a region ------------------------------------------------------- + + private static void LowerInside(Ctx ctx, StatementSyntax st, List ops) + { + switch (st) + { + case BlockSyntax block: + foreach (var inner in block.Statements) + LowerInside(ctx, inner, ops); + return; + case EmptyStatementSyntax: + return; + case ExpressionStatementSyntax es: + if (Unparen(es.Expression) is InvocationExpressionSyntax entry + && IsRegionEntry(MethodOf(ctx.Model, entry))) + { + LowerRegion(ctx, entry, ops, st); + return; + } + Scan(ctx, es.Expression, ops); + return; + case LocalDeclarationStatementSyntax ld: + if (!ld.UsingKeyword.IsKind(SyntaxKind.None) || !ld.AwaitKeyword.IsKind(SyntaxKind.None)) + throw Refuse(ctx, st, "a `using` declaration inside a protocol region is not modelled"); + foreach (var v in ld.Declaration.Variables) + { + var init = v.Initializer?.Value; + if (ctx.Model.GetDeclaredSymbol(v) is ILocalSymbol local && IsToken(local.Type)) + LowerTokenLocal(ctx, local, init, ops, v); + else if (init is not null) + Scan(ctx, init, ops); + } + return; + case IfStatementSyntax ifs: + Scan(ctx, ifs.Condition, ops); + var thenOps = new List(); + LowerInside(ctx, ifs.Statement, thenOps); + var elseOps = new List(); + if (ifs.Else is not null) + LowerInside(ctx, ifs.Else.Statement, elseOps); + ops.Add(new Dictionary + { + ["op"] = "if", ["line"] = Line(st), ["then"] = thenOps, ["else"] = elseOps, + }); + return; + case ReturnStatementSyntax: + throw Refuse(ctx, st, + "`return` inside a protocol region leaves the callback, not the method; not modelled"); + default: + throw Refuse(ctx, st, $"{st.Kind()} inside a protocol region is not modelled"); + } + } + + private static void LowerRegion(Ctx ctx, InvocationExpressionSyntax entry, List ops, + StatementSyntax st) + { + var args = entry.ArgumentList.Arguments; + if (args.Count != 2) + throw Refuse(ctx, entry, "a protocol region entry takes (entity, callback)"); + var entityExpr = Unparen(args[0].Expression); + var entity = ctx.Model.GetSymbolInfo(entityExpr).Symbol; + if (entityExpr is not IdentifierNameSyntax || entity is not (ILocalSymbol or IParameterSymbol)) + throw Refuse(ctx, entry, + "the entity of a protocol region must be a local or a parameter: that symbol is the resource identity"); + if (Unparen(args[1].Expression) is not LambdaExpressionSyntax lambda) + throw Refuse(ctx, entry, + "the body of a protocol region must be a lambda written in place"); + if (lambda.AsyncKeyword.IsKind(SyntaxKind.AsyncKeyword)) + throw Refuse(ctx, entry, "the body of a protocol region cannot be async"); + ParameterSyntax? parameter = lambda switch + { + SimpleLambdaExpressionSyntax s => s.Parameter, + ParenthesizedLambdaExpressionSyntax p when p.ParameterList.Parameters.Count == 1 => + p.ParameterList.Parameters[0], + _ => null, + }; + if (parameter is null + || ctx.Model.GetDeclaredSymbol(parameter) is not IParameterSymbol token + || !IsToken(token.Type)) + throw Refuse(ctx, entry, + "the callback of a protocol region takes exactly one protocol token"); + // A token that is not a ref struct can be captured by a nested lambda, stored in a + // field, or carried across an await — every one of which lets it reach ANOTHER + // region, the shape the core cannot check. The language forbids all three for a + // ref struct, so the lowering leans on that and refuses anything weaker. + if (!token.Type.IsRefLikeType) + throw Refuse(ctx, entry, + $"protocol token type '{token.Type.Name}' must be a ref struct"); + + var line = Line(st); + var entityType = TypeOf(entity); + // Exclusivity is BY TYPE, so an entity whose type does not resolve cannot be told + // from anything else that does not: the scan's compilation is degraded (a missing + // reference, an implicit using it never saw). Say so instead of guessing either way. + if (entityType is null or IErrorTypeSymbol) + throw Refuse(ctx, entry, + $"the type of '{entity.Name}' does not resolve, so the region cannot be lowered: the scan is missing a reference, or a `using` the project only gets implicitly (the scan does not read `obj/`: write the using out, or qualify the name)"); + var existing = ctx.Regions.LastOrDefault(r => + SymbolEqualityComparer.Default.Equals(r.Entity, entity)); + if (existing is null) + { + // exclusivity by type: another value of an already-borrowed entity's type + var clash = RegionByType(ctx, entityType); + if (clash is not null) + Touch(ctx, clash, entry, ops); + ops.Add(Acquire(entity.Name, line)); + } + var owner = existing?.Owner ?? entity.Name; + ctx.Counter++; + var region = new Region + { + Entity = entity, EntityType = entityType, Owner = owner, + Binding = $"{owner}.region{ctx.Counter}", + }; + var body = new List { Acquire(token.Name, line) }; + ctx.Tokens[token] = (token.Name, region); + ctx.Regions.Add(region); + if (lambda.Body is BlockSyntax block) + LowerInside(ctx, block, body); + else if (lambda.Body is ExpressionSyntax expression) + Scan(ctx, expression, body); + ctx.Regions.RemoveAt(ctx.Regions.Count - 1); + ops.Add(new Dictionary + { + ["op"] = "borrow_mut", ["owner"] = owner, ["binding"] = region.Binding, + ["line"] = line, ["body"] = body, + }); + if (existing is null) + ops.Add(Release(owner, line)); + ctx.LoweredEntries.Add(entry); + } + + private static void LowerTokenLocal(Ctx ctx, ILocalSymbol local, ExpressionSyntax? init, + List ops, SyntaxNode at) + { + if (init is not null) + { + var value = Unparen(init); + if (value is IdentifierNameSyntax id + && ctx.Model.GetSymbolInfo(id).Symbol is { } source + && ctx.Tokens.TryGetValue(source, out var live)) + { + // C# copies the struct; the model reads the copy as a MOVE. + ops.Add(Move(local.Name, live.Name, Line(at))); + ctx.Tokens[local] = (local.Name, live.Region); + return; + } + if (value is InvocationExpressionSyntax inv + && TryTransition(ctx, inv, ops, local.Name, out var region)) + { + ctx.Tokens[local] = (local.Name, region); + return; + } + } + throw Refuse(ctx, at, + $"protocol token '{local.Name}' has no lowerable origin: a token comes from a region entry, a transition, or a copy of a live token"); + } + + // ---- expressions ---------------------------------------------------------------------- + + /// A call on a token is a transition: it consumes the token and borrows the entity for + /// the call. `result`, when given, names the token the transition hands back. + private static bool TryTransition(Ctx ctx, InvocationExpressionSyntax inv, List ops, + string? result, out Region region) + { + region = null!; + if (inv.Expression is not MemberAccessExpressionSyntax access + || MethodOf(ctx.Model, inv) is not { IsStatic: false } method + || !IsToken(method.ContainingType)) + return false; + + string receiver; + var target = Unparen(access.Expression); + if (target is IdentifierNameSyntax id + && ctx.Model.GetSymbolInfo(id).Symbol is { } symbol + && ctx.Tokens.TryGetValue(symbol, out var live)) + { + receiver = live.Name; + region = live.Region; + } + else if (target is InvocationExpressionSyntax inner) + { + // a chain: `draft.Submit().Approve()` - the intermediate token is a temporary + ctx.Counter++; + receiver = $"$t{ctx.Counter}"; + if (!TryTransition(ctx, inner, ops, receiver, out region)) + throw Refuse(ctx, inv, + $"the receiver of transition '{method.Name}' is not a token this region minted"); + } + else + { + throw Refuse(ctx, inv, + $"the receiver of transition '{method.Name}' is not a token this region minted"); + } + + foreach (var arg in inv.ArgumentList.Arguments) + Scan(ctx, arg.Expression, ops); + + var callee = $"{method.ContainingType.ToDisplayString()}.{method.Name}"; + Contract(ctx, callee, method, inv); + ops.Add(Call(callee, new[] { receiver, region.Binding }, Line(inv))); + if (result is not null) + { + if (!IsToken(method.ReturnType)) + throw Refuse(ctx, inv, + $"transition '{method.Name}' does not return a protocol token"); + ops.Add(Acquire(result, Line(inv))); + } + return true; + } + + /// The contract of a transition, stated once per method: the token is consumed, the + /// entity is exclusively borrowed for the call. This is a FACT about the attribute's + /// meaning, not an inference from the method's body. + private static void Contract(Ctx ctx, string callee, IMethodSymbol method, SyntaxNode at) + { + if (ctx.Contracts.ContainsKey(callee)) + return; + if (ctx.LegacyNames.Contains(callee)) + throw Refuse(ctx, at, + $"transition '{callee}' already has a flow record; the two lowerings are not merged in this profile"); + var decl = method.DeclaringSyntaxReferences.FirstOrDefault(); + var file = decl is not null && ctx.Files.TryGetValue(decl.SyntaxTree, out var f) ? f : "?"; + var line = decl is not null ? Line(decl.GetSyntax()) : 0; + ctx.Contracts[callee] = new Dictionary + { + ["name"] = callee, + ["file"] = file, + ["params"] = new object[] + { + new Dictionary { ["name"] = "token", ["effect"] = "consume", ["line"] = line }, + new Dictionary { ["name"] = "entity", ["effect"] = "borrow_mut", ["line"] = line }, + }, + ["body"] = new object[] { Release("token", line) }, + }; + } + + private static ITypeSymbol? TypeOf(ISymbol symbol) => symbol switch + { + ILocalSymbol l => l.Type, + IParameterSymbol p => p.Type, + IFieldSymbol f => f.Type, + IPropertySymbol p => p.Type, + _ => null, + }; + + private static Region? RegionByType(Ctx ctx, ITypeSymbol? type) => + type is null ? null : ctx.Regions.LastOrDefault(r => + SymbolEqualityComparer.Default.Equals(r.EntityType, type)); + + private static Region? RegionFor(Ctx ctx, ISymbol symbol, ITypeSymbol? type) => + ctx.Regions.LastOrDefault(r => SymbolEqualityComparer.Default.Equals(r.Entity, symbol)) + ?? RegionByType(ctx, type); + + /// Exclusivity by type: an expression that PRODUCES a value of a borrowed entity's type + /// (a field read, a cast, an array element) may be that entity, so it counts as + /// touching it. + private static void NoteEntityTyped(Ctx ctx, ExpressionSyntax expression, List ops) + { + var region = RegionByType(ctx, ctx.Model.GetTypeInfo(expression).Type); + if (region is not null) + Touch(ctx, region, expression, ops); + } + + private static bool IsLiveToken(Ctx ctx, ExpressionSyntax expression, out string name) + { + name = ""; + if (Unparen(expression) is IdentifierNameSyntax id + && ctx.Model.GetSymbolInfo(id).Symbol is { } symbol + && ctx.Tokens.TryGetValue(symbol, out var token)) + { + name = token.Name; + return true; + } + return false; + } + + /// Code with no stated contract, inside an exclusive region. If lowering its operands + /// touched the entity the core has a use to reject; otherwise nothing says what the code + /// does, and the region is not analysed as safe. + private static void OpaqueCode(Ctx ctx, SyntaxNode node, string what, int touchesBefore) + { + if (ctx.EntityTouches > touchesBefore) + return; + throw Refuse(ctx, node, + $"{what} inside a protocol region runs code with no stated contract; an exclusive region admits only transitions and what the lowering can read"); + } + + /// An expression inside a region. DEFAULT-DENY: every kind is either read here or refused. + private static void Scan(Ctx ctx, SyntaxNode node, List ops) + { + if (node is ExpressionSyntax typed + && ctx.Model.GetConversion(typed).IsUserDefined) + throw Refuse(ctx, node, + "a user-defined conversion inside a protocol region runs code with no stated contract"); + + switch (node) + { + case LiteralExpressionSyntax: + case TypeOfExpressionSyntax: + case SizeOfExpressionSyntax: + case DefaultExpressionSyntax: + case PredefinedTypeSyntax: + return; + case ParenthesizedExpressionSyntax paren: + Scan(ctx, paren.Expression, ops); + return; + case CheckedExpressionSyntax @checked: + Scan(ctx, @checked.Expression, ops); + return; + case ThisExpressionSyntax or BaseExpressionSyntax: + NoteEntityTyped(ctx, (ExpressionSyntax)node, ops); + return; + case IdentifierNameSyntax id: + { + var symbol = ctx.Model.GetSymbolInfo(id).Symbol; + switch (symbol) + { + case null: + throw Refuse(ctx, id, + $"'{id.Identifier.Text}' does not resolve; an unresolved name inside a protocol region is not read as harmless"); + case INamespaceOrTypeSymbol: + return; + case ILocalSymbol or IParameterSymbol or IFieldSymbol: + if (ctx.Tokens.ContainsKey(symbol)) + throw Refuse(ctx, id, + $"protocol token '{id.Identifier.Text}' is used in a way the lowering does not model (transitions, property reads and copies are)"); + var region = RegionFor(ctx, symbol, TypeOf(symbol)); + if (region is not null) + Touch(ctx, region, id, ops); + return; + case IPropertySymbol property: + { + var before = ctx.EntityTouches; + var owner = RegionByType(ctx, property.Type); + if (owner is not null) + Touch(ctx, owner, id, ops); + OpaqueCode(ctx, id, $"property '{property.Name}'", before); + return; + } + default: + throw Refuse(ctx, id, + $"'{id.Identifier.Text}' ({symbol.Kind}) inside a protocol region is not modelled"); + } + } + case MemberAccessExpressionSyntax access: + { + if (IsLiveToken(ctx, access.Expression, out var token)) + { + ops.Add(Use(token, Line(access))); // a property read on the token + return; + } + var before = ctx.EntityTouches; + Scan(ctx, access.Expression, ops); + var member = ctx.Model.GetSymbolInfo(access).Symbol; + switch (member) + { + case IFieldSymbol: + NoteEntityTyped(ctx, access, ops); + return; + case INamespaceOrTypeSymbol: + return; + case IPropertySymbol property: + NoteEntityTyped(ctx, access, ops); + OpaqueCode(ctx, access, $"property '{property.Name}'", before); + return; + default: + throw Refuse(ctx, access, + $"'{access.Name.Identifier.Text}' inside a protocol region is not modelled"); + } + } + case InvocationExpressionSyntax inv: + { + // `nameof(x)` is a constant spelled as a call + if (ctx.Model.GetConstantValue(inv).HasValue) + return; + var method = MethodOf(ctx.Model, inv); + if (IsRegionEntry(method)) + throw Refuse(ctx, inv, + "a protocol region entry in a position the lowering does not model (it must be a statement of its own)"); + if (TryTransition(ctx, inv, ops, null, out _)) + return; + var before = ctx.EntityTouches; + foreach (var arg in inv.ArgumentList.Arguments) + { + if (IsLiveToken(ctx, arg.Expression, out var passed)) + throw Refuse(ctx, arg, + $"protocol token '{passed}' is handed to other code; a token does not leave its region"); + Scan(ctx, arg.Expression, ops); + } + // the receiver, never the method's own name + if (inv.Expression is MemberAccessExpressionSyntax target) + Scan(ctx, target.Expression, ops); + NoteEntityTyped(ctx, inv, ops); + var name = method is null + ? inv.Expression.ToString() + : $"{method.ContainingType.ToDisplayString()}.{method.Name}"; + OpaqueCode(ctx, inv, $"a call to '{name}'", before); + return; + } + case BaseObjectCreationExpressionSyntax creation: + { + var before = ctx.EntityTouches; + foreach (var arg in creation.ArgumentList?.Arguments ?? default) + Scan(ctx, arg.Expression, ops); + if (creation.Initializer is not null) + throw Refuse(ctx, creation, + "an object initializer inside a protocol region is not modelled"); + OpaqueCode(ctx, creation, "a constructor call", before); + return; + } + case ArrayCreationExpressionSyntax or ImplicitArrayCreationExpressionSyntax + or InitializerExpressionSyntax or TupleExpressionSyntax + or ArrayTypeSyntax or ArrayRankSpecifierSyntax or OmittedArraySizeExpressionSyntax + or ArgumentSyntax or ConditionalExpressionSyntax: + foreach (var child in node.ChildNodes()) + Scan(ctx, child, ops); + return; + case ElementAccessExpressionSyntax element: + { + var before = ctx.EntityTouches; + Scan(ctx, element.Expression, ops); + foreach (var arg in element.ArgumentList.Arguments) + Scan(ctx, arg.Expression, ops); + NoteEntityTyped(ctx, element, ops); + if (ctx.Model.GetTypeInfo(element.Expression).Type is not IArrayTypeSymbol) + OpaqueCode(ctx, element, "an indexer", before); + return; + } + case BinaryExpressionSyntax binary: + { + if (ctx.Model.GetSymbolInfo(binary).Symbol is IMethodSymbol + { MethodKind: not MethodKind.BuiltinOperator }) + throw Refuse(ctx, binary, + "a user-defined operator inside a protocol region runs code with no stated contract"); + // string concatenation formats its operands: only inert ones run no user code + if (binary.IsKind(SyntaxKind.AddExpression) + && ctx.Model.GetTypeInfo(binary).Type?.SpecialType == SpecialType.System_String + && !(IsInert(ctx.Model.GetTypeInfo(binary.Left).Type) + && IsInert(ctx.Model.GetTypeInfo(binary.Right).Type))) + throw Refuse(ctx, binary, + "string concatenation of a non-primitive value inside a protocol region calls its ToString(); not modelled"); + Scan(ctx, binary.Left, ops); + // the right operand of `is` / `as` is a type, not a value + if (!binary.IsKind(SyntaxKind.AsExpression) && !binary.IsKind(SyntaxKind.IsExpression)) + Scan(ctx, binary.Right, ops); + if (binary.IsKind(SyntaxKind.AsExpression)) + NoteEntityTyped(ctx, binary, ops); + return; + } + case PrefixUnaryExpressionSyntax or PostfixUnaryExpressionSyntax: + { + if (ctx.Model.GetSymbolInfo(node).Symbol is IMethodSymbol + { MethodKind: not MethodKind.BuiltinOperator }) + throw Refuse(ctx, node, + "a user-defined operator inside a protocol region runs code with no stated contract"); + foreach (var child in node.ChildNodes()) + Scan(ctx, child, ops); + return; + } + case AssignmentExpressionSyntax assignment: + { + if (ctx.Model.GetSymbolInfo(assignment).Symbol is IMethodSymbol + { MethodKind: not MethodKind.BuiltinOperator }) + throw Refuse(ctx, assignment, + "a user-defined operator inside a protocol region runs code with no stated contract"); + Scan(ctx, assignment.Left, ops); + Scan(ctx, assignment.Right, ops); + return; + } + case CastExpressionSyntax cast: + { + var target = ctx.Model.GetTypeInfo(cast.Type).Type; + if (target is null + || ctx.Model.ClassifyConversion(cast.Expression, target).IsUserDefined) + throw Refuse(ctx, cast, + "a user-defined conversion inside a protocol region runs code with no stated contract"); + Scan(ctx, cast.Expression, ops); + NoteEntityTyped(ctx, cast, ops); + return; + } + case InterpolatedStringExpressionSyntax interpolated: + foreach (var part in interpolated.Contents.OfType()) + { + if (!IsInert(ctx.Model.GetTypeInfo(part.Expression).Type)) + throw Refuse(ctx, part, + "interpolating a non-primitive value inside a protocol region calls its ToString(); not modelled"); + Scan(ctx, part.Expression, ops); + } + return; + default: + throw Refuse(ctx, node, $"{node.Kind()} inside a protocol region is not modelled"); + } + } +} diff --git a/frontend/roslyn/README.md b/frontend/roslyn/README.md index 12118e8e..1c56acb0 100644 --- a/frontend/roslyn/README.md +++ b/frontend/roslyn/README.md @@ -70,6 +70,75 @@ symbols instead of surfacing as OWN050. `--no-project-refs` opts out; an unbuilt project just contributes nothing. Run `dotnet run --project OwnSharp.Extractor -- --help` for the full option list. +## State protocols (`--flow-locals`, OwnIR v1) + +With `--flow-locals` the extractor also lowers a **state-protocol surface** — the +first slice of [P-010](../../docs/proposals/P-010-type-disciplines.md) pillar 9 — +into the OwnIR v1 ops `move` and `borrow_mut` ([OwnIR.md §5.3](../../spec/OwnIR.md)). +(These are *state* protocols — which state an entity is in. The *obligation* +protocols of P-025 / OwnIR §8 are a different mechanism with a similar name.) +It is recognised by two attributes matched by **name**, so a domain carries no +dependency on Own.NET: + +```csharp +[ProtocolToken] // a state: a ref struct over the entity +public readonly ref struct ApprovedOrder +{ + private readonly Order _order; + internal ApprovedOrder(Order order) => _order = order; + public void Ship(DateTime at) => _order.MarkShipped(at); // a transition: spends the token +} + +public static class OrderProtocol +{ + [ProtocolRegion] // a region entry: (entity, callback) + public static void WithApproved(Order order, ApprovedRegion body) { /* check, then */ body(new ApprovedOrder(order)); } +} + +// a handler: no attribute, no base class, no repository — the entity EF tracks +OrderProtocol.WithApproved(order, approved => +{ + approved.Ship(now); +}); +``` + +Inside the callback the entity is **exclusively borrowed**: only its token may +change it, a token is spent once (use after a transition is OWN002, a copy is a +move — OWN005), and any other mention of the entity is OWN013. The verdicts are +the core's; the extractor only lowers. A complete backend — ASP.NET Core minimal +API, EF Core, SQLite — is in +[`protocol-samples/efcore`](protocol-samples/efcore), and +`python scripts/protocol_gate.py` ties every committed fact back to the C# it came +from. + +**What is claimed.** The profile protects the local C# capabilities and aliases of +an entity that already exists. It does **not** protect the persisted row from +other ways of changing it: `ExecuteUpdate`, a change-tracker metadata write, raw +SQL, another process. Those belong to concurrency tokens, constraints and +transactions (two of them are pinned as stated limits under +`protocol-samples/efcore/known-gaps`). + +**Refusals.** A recognised protocol construct that cannot be lowered safely is +not skipped: the extractor exits `2` and writes no facts, for the whole scan. + +- *Inside a region the rule is default-deny.* Transitions, token reads, `if`, + locals and built-in operators are read; a call, a constructor, a property + getter, an indexer, a loop, `return` are refused — nothing states what such code + does to the entity. Read what you need (`DateTime.UtcNow`, a logger call) before + the region. Around a region anything goes: `try`/`catch`, `await using`, loops. +- *Admission.* The state a transition writes must have no **public** mutator on + the entity (a public setter, or a public method reaching the same write): that + would be a transition nobody declared. +- *Boundary.* Outside the protocol's own types, creating a token (`new`, + `default`), calling a non-public method of the entity or the protocol, and + writing their non-publicly-writable state are refused — one assembly or two. + `nameof`, `typeof` and reads are mentions, not operations. +- *Source.* The protocol and its entity must be in the scan as source; one that + arrives only as a compiled reference cannot be admitted. +- *Binding.* The scan does not read `obj/`, so usings a project only gets + **implicitly** are not there. A region whose entity does not bind is refused; + write the usings out in the files that open regions (or qualify the names). + ## Use it on a real repo / in CI (P-013) The two stages are chained by one orchestrator script, so you don't run them by diff --git a/frontend/roslyn/protocol-samples/Api/ProtocolApi.cs b/frontend/roslyn/protocol-samples/Api/ProtocolApi.cs new file mode 100644 index 00000000..f4e3b030 --- /dev/null +++ b/frontend/roslyn/protocol-samples/Api/ProtocolApi.cs @@ -0,0 +1,128 @@ +using System; +using System.Threading.Tasks; + +namespace Own.Protocols.Sample; + +// The sample state protocol behind `protocol-samples/cases` (C# -> OwnIR v1 lowering, +// see frontend/roslyn/README.md). Deliberately minimal: one entity, three state tokens, +// two region entries, no persistence. It is a fixture, not an API proposal — the +// realistic shape, on an entity EF Core tracks, is `protocol-samples/efcore`. What it +// has to be is honestly lowerable: +// +// * a region entry is a method marked [ProtocolRegion]: (entity, callback); +// * a state token is a `ref struct` marked [ProtocolToken]; every instance METHOD +// on it is a transition and consumes the token, a PROPERTY is a read; +// * a token can only be obtained from a region entry or from a transition, so the +// shapes the core cannot check (a token minted outside a region, two tokens for +// one entity, a token spent against another entity) have no spelling here. +// +// The two attributes are matched by NAME, so this file has no dependency on Own.NET. + +[AttributeUsage(AttributeTargets.Struct)] +public sealed class ProtocolTokenAttribute : Attribute { } + +[AttributeUsage(AttributeTargets.Method)] +public sealed class ProtocolRegionAttribute : Attribute { } + +public enum OrderStatus { Draft, Submitted, Approved, Shipped, Cancelled } + +public sealed class Order +{ + public int Id { get; internal set; } + public OrderStatus Status { get; internal set; } + public string Note { get; private set; } = ""; + + public static Order Load(int id) => new() { Id = id }; + + // Ordinary public behaviour that is NOT protocol state: free outside a region, and a raw + // access the region must exclude inside one. (A public member that wrote `Status` would + // be a transition nobody declared; the protocol would not be admitted with it.) + public void Annotate(string note) => Note = note; +} + +[ProtocolToken] +public readonly ref struct DraftOrder +{ + private readonly Order _order; + internal DraftOrder(Order order) => _order = order; + + public int Id => _order.Id; + + public SubmittedOrder Submit() + { + _order.Status = OrderStatus.Submitted; + return new SubmittedOrder(_order); + } + + public void Cancel() => _order.Status = OrderStatus.Cancelled; +} + +[ProtocolToken] +public readonly ref struct SubmittedOrder +{ + private readonly Order _order; + internal SubmittedOrder(Order order) => _order = order; + + public int Id => _order.Id; + + // Returns nothing: the token is spent and no further capability is handed out. + public void Approve() => _order.Status = OrderStatus.Approved; +} + +[ProtocolToken] +public readonly ref struct ApprovedOrder +{ + private readonly Order _order; + internal ApprovedOrder(Order order) => _order = order; + + public int Id => _order.Id; + + public void Ship() => _order.Status = OrderStatus.Shipped; +} + +public delegate void DraftRegion(DraftOrder draft); + +public delegate void ApprovedRegion(ApprovedOrder approved); + +public static class Protocol +{ + [ProtocolRegion] + public static void WithDraft(Order order, DraftRegion body) + { + if (order.Status != OrderStatus.Draft) + throw new InvalidOperationException("the order is not a draft"); + body(new DraftOrder(order)); + } + + [ProtocolRegion] + public static void WithApproved(Order order, ApprovedRegion body) + { + if (order.Status != OrderStatus.Approved) + throw new InvalidOperationException("the order is not approved"); + body(new ApprovedOrder(order)); + } +} + +// Stand-ins so the cases look like a handler without pulling EF Core into the gate. +public sealed class Db +{ + public Task SingleAsync(int id) => Task.FromResult(Order.Load(id)); + + public Task SaveChangesAsync() => Task.FromResult(1); +} + +public static class Audit +{ + public static void Record(Order order) { } +} + +// Code that reaches an entity WITHOUT naming it at the call site: the shape an exclusive +// region cannot be allowed to wave through. +public static class Backdoor +{ + private static Order? _last; + + public static void Remember(Order order) => _last = order; + + public static void AnnotateLast() => _last?.Annotate("behind the call"); +} diff --git a/frontend/roslyn/protocol-samples/cases/A1_alias_of_entity.cs b/frontend/roslyn/protocol-samples/cases/A1_alias_of_entity.cs new file mode 100644 index 00000000..6beffdc9 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/A1_alias_of_entity.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// An alias made BEFORE the region, used inside it -> reject. +// No alias analysis: inside a region every value of the entity's type is treated as +// the borrowed entity (exclusivity by type). +public static class A1AliasOfEntity +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + var same = order; + Protocol.WithApproved(order, approved => + { + same.Annotate("x"); // MUST FAIL + approved.Ship(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/A2_entity_through_indexer.cs b/frontend/roslyn/protocol-samples/cases/A2_entity_through_indexer.cs new file mode 100644 index 00000000..bf7ee652 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/A2_entity_through_indexer.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// The borrowed entity reached through an indexer -> reject. +// Still no alias analysis: `all[0]` PRODUCES a value of the entity's type, and inside a +// region every such value is treated as the borrowed entity. +public static class A2EntityThroughIndexer +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + var all = new[] { order }; + Protocol.WithApproved(order, approved => + { + all[0].Annotate("x"); // MUST FAIL + approved.Ship(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/G6_capability_not_spent.cs b/frontend/roslyn/protocol-samples/cases/G6_capability_not_spent.cs new file mode 100644 index 00000000..8442fc86 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/G6_capability_not_spent.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// KNOWN GAP (not part of this gate): a capability that is never spent. +// Today: OWN001. Expected once tokens are affine: clean. +public static class G6CapabilityNotSpent +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithApproved(order, approved => + { + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L10_region_inside_try.cs b/frontend/roslyn/protocol-samples/cases/L10_region_inside_try.cs new file mode 100644 index 00000000..0f711b86 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L10_region_inside_try.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// Whatever SURROUNDS a region is walked through: try/catch/finally, a loop, a branch, +// a `return` after it. A region is a self-contained unit. +public static class L10RegionInsideTry +{ + public static async Task Run(Db db, int[] ids) + { + foreach (var id in ids) + { + var order = await db.SingleAsync(id); + try + { + if (order.Id > 0) + { + Protocol.WithApproved(order, approved => + { + approved.Ship(); + }); + } + } + catch (InvalidOperationException) + { + return false; + } + finally + { + Audit.Record(order); + } + } + await db.SaveChangesAsync(); + return true; + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L11_region_beside_disposable.cs b/frontend/roslyn/protocol-samples/cases/L11_region_beside_disposable.cs new file mode 100644 index 00000000..97057bfd --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L11_region_beside_disposable.cs @@ -0,0 +1,23 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A method that opens a region AND owns an IDisposable. The two lowerings are independent +// readings of one method (they track disjoint resources), so each gets its own record. +// The stream here is disposed, so both are clean. +public static class L11RegionBesideDisposable +{ + public static void Run(Order order) + { + var log = new MemoryStream(); + log.WriteByte(1); + Protocol.WithApproved(order, approved => + { + approved.Ship(); + }); + log.Dispose(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs b/frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs new file mode 100644 index 00000000..0de6646f --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L12_region_beside_leaked_disposable.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// The same shape with the stream LEAKED: the IDisposable reading still reports it, which +// is the proof that a region in the method does not switch the other analysis off. +public static class L12RegionBesideLeakedDisposable +{ + public static void Run(Order order) + { + var log = new MemoryStream(); + log.WriteByte(1); + Protocol.WithApproved(order, approved => + { + approved.Ship(); + }); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L13_values_captured_into_region.cs b/frontend/roslyn/protocol-samples/cases/L13_values_captured_into_region.cs new file mode 100644 index 00000000..fcb20c69 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L13_values_captured_into_region.cs @@ -0,0 +1,28 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// What a region MAY contain besides transitions: locals captured from outside, literals, +// built-in operators, token reads, `if`. Everything that needs a call is computed before. +public static class L13ValuesCapturedIntoRegion +{ + public static void Run(Order order, int limit) + { + var threshold = limit * 2; + Protocol.WithApproved(order, approved => + { + var over = approved.Id > threshold; + if (over && limit != 0) + { + approved.Ship(); + } + else + { + approved.Ship(); + } + }); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs b/frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs new file mode 100644 index 00000000..581aa5fd --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L1_stale_capability.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// 1. stale capability -> reject. +// `draft` is spent by Submit(); using it again is a use of a consumed token. +public static class L1StaleCapability +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithDraft(order, draft => + { + var submitted = draft.Submit(); + draft.Cancel(); // MUST FAIL + submitted.Approve(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L2_copied_capability.cs b/frontend/roslyn/protocol-samples/cases/L2_copied_capability.cs new file mode 100644 index 00000000..37364b39 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L2_copied_capability.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// 2. copied capability -> reject. +// C# copies a struct; the lowering reads the copy as a MOVE, so the source is dead. +public static class L2CopiedCapability +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithDraft(order, draft => + { + var copy = draft; + draft.Cancel(); // MUST FAIL + copy.Cancel(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L3_raw_mutation.cs b/frontend/roslyn/protocol-samples/cases/L3_raw_mutation.cs new file mode 100644 index 00000000..aabe5f63 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L3_raw_mutation.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// 3. raw mutation -> reject. +// The entity is exclusively borrowed for the region; touching it directly is a violation. +public static class L3RawMutation +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithApproved(order, approved => + { + order.Annotate("x"); // MUST FAIL + approved.Ship(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L4_raw_escape.cs b/frontend/roslyn/protocol-samples/cases/L4_raw_escape.cs new file mode 100644 index 00000000..d587cbd8 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L4_raw_escape.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// 4. raw escape -> refuse/reject. +// Handing the raw entity to other code inside the region is the same violation. +public static class L4RawEscape +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithApproved(order, approved => + { + Audit.Record(order); // MUST FAIL + approved.Ship(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L5_normal_transition.cs b/frontend/roslyn/protocol-samples/cases/L5_normal_transition.cs new file mode 100644 index 00000000..fa9dcb71 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L5_normal_transition.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// 5. normal transition -> clean. +public static class L5NormalTransition +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithApproved(order, approved => + { + approved.Ship(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L6_two_transitions.cs b/frontend/roslyn/protocol-samples/cases/L6_two_transitions.cs new file mode 100644 index 00000000..7c244e0f --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L6_two_transitions.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// 6. two transitions -> clean. Draft -> Submitted -> (approved, token spent). +public static class L6TwoTransitions +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithDraft(order, draft => + { + var submitted = draft.Submit(); + submitted.Approve(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L6b_chained_transitions.cs b/frontend/roslyn/protocol-samples/cases/L6b_chained_transitions.cs new file mode 100644 index 00000000..0e7a7ed0 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L6b_chained_transitions.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// The same two transitions written as a chain: the intermediate token is a temporary. +public static class L6bChainedTransitions +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithDraft(order, draft => draft.Submit().Approve()); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L7_read_after_region.cs b/frontend/roslyn/protocol-samples/cases/L7_read_after_region.cs new file mode 100644 index 00000000..4ca067d0 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L7_read_after_region.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// The entity is free again once the region closes: `return Ok(order)` territory. +public static class L7ReadAfterRegion +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithApproved(order, approved => + { + approved.Ship(); + }); + Audit.Record(order); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L8_branch_inside_region.cs b/frontend/roslyn/protocol-samples/cases/L8_branch_inside_region.cs new file mode 100644 index 00000000..2fb5ebd9 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L8_branch_inside_region.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A transition on one branch only, then again after the merge -> reject (maybe-consumed). +public static class L8BranchInsideRegion +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithApproved(order, approved => + { + if (approved.Id > 0) + { + approved.Ship(); + } + approved.Ship(); // MUST FAIL on the path that already shipped + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/L9_opaque_calls_around_region.cs b/frontend/roslyn/protocol-samples/cases/L9_opaque_calls_around_region.cs new file mode 100644 index 00000000..bebea825 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/L9_opaque_calls_around_region.cs @@ -0,0 +1,26 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// The SAME opaque calls that are refused inside a region are ordinary code outside it: +// before the region the entity is not borrowed, after it the borrow is over. +public static class L9OpaqueCallsAroundRegion +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Backdoor.Remember(order); + Backdoor.AnnotateLast(); + var now = DateTime.UtcNow; + Protocol.WithApproved(order, approved => + { + approved.Ship(); + }); + Backdoor.AnnotateLast(); + Console.WriteLine(now); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/X1_nested_region_same_entity.cs b/frontend/roslyn/protocol-samples/cases/X1_nested_region_same_entity.cs new file mode 100644 index 00000000..0d2231c6 --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/X1_nested_region_same_entity.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A second region on the entity that is already borrowed -> reject. +// This is the only way the surface can ASK for a second capability. +public static class X1NestedRegionSameEntity +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithApproved(order, approved => + { + Protocol.WithDraft(order, draft => draft.Cancel()); // MUST FAIL + approved.Ship(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/cases/expected.json b/frontend/roslyn/protocol-samples/cases/expected.json new file mode 100644 index 00000000..15a8cb6e --- /dev/null +++ b/frontend/roslyn/protocol-samples/cases/expected.json @@ -0,0 +1,40 @@ +{ + "A1_alias_of_entity": [ + "OWN013" + ], + "A2_entity_through_indexer": [ + "OWN013" + ], + "G6_capability_not_spent": [ + "OWN001" + ], + "L10_region_inside_try": [], + "L11_region_beside_disposable": [], + "L12_region_beside_leaked_disposable": [ + "OWN001" + ], + "L13_values_captured_into_region": [], + "L1_stale_capability": [ + "OWN002" + ], + "L2_copied_capability": [ + "OWN005" + ], + "L3_raw_mutation": [ + "OWN013" + ], + "L4_raw_escape": [ + "OWN013" + ], + "L5_normal_transition": [], + "L6_two_transitions": [], + "L6b_chained_transitions": [], + "L7_read_after_region": [], + "L8_branch_inside_region": [ + "OWN009" + ], + "L9_opaque_calls_around_region": [], + "X1_nested_region_same_entity": [ + "OWN011" + ] +} diff --git a/frontend/roslyn/protocol-samples/does-not-compile/C1_construct_token.cs.txt b/frontend/roslyn/protocol-samples/does-not-compile/C1_construct_token.cs.txt new file mode 100644 index 00000000..4cde527c --- /dev/null +++ b/frontend/roslyn/protocol-samples/does-not-compile/C1_construct_token.cs.txt @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// N2: a second token cannot be minted - the constructor is internal to the API. +public static class C1ConstructToken +{ + public static void Run(Order order) + { + var forged = new ApprovedOrder(order); + forged.Ship(); + } +} diff --git a/frontend/roslyn/protocol-samples/does-not-compile/C2_capture_token_in_nested_lambda.cs.txt b/frontend/roslyn/protocol-samples/does-not-compile/C2_capture_token_in_nested_lambda.cs.txt new file mode 100644 index 00000000..a2644493 --- /dev/null +++ b/frontend/roslyn/protocol-samples/does-not-compile/C2_capture_token_in_nested_lambda.cs.txt @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// N1: a token cannot be carried into ANOTHER region - a ref struct cannot be captured. +public static class C2CaptureToken +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + var other = await db.SingleAsync(id + 1); + Protocol.WithApproved(order, approved => + { + Protocol.WithDraft(other, draft => + { + approved.Ship(); // a token of `order` inside the region of `other` + draft.Cancel(); + }); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/does-not-compile/C3_token_escapes_to_outer_local.cs.txt b/frontend/roslyn/protocol-samples/does-not-compile/C3_token_escapes_to_outer_local.cs.txt new file mode 100644 index 00000000..0bcd4976 --- /dev/null +++ b/frontend/roslyn/protocol-samples/does-not-compile/C3_token_escapes_to_outer_local.cs.txt @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A token cannot outlive its region through an outer variable. +public static class C3TokenEscapes +{ + public static void Run(Order order) + { + ApprovedOrder saved = default; + Protocol.WithApproved(order, approved => + { + saved = approved; + }); + saved.Ship(); + } +} diff --git a/frontend/roslyn/protocol-samples/does-not-compile/C4_token_in_field.cs.txt b/frontend/roslyn/protocol-samples/does-not-compile/C4_token_in_field.cs.txt new file mode 100644 index 00000000..67d6545e --- /dev/null +++ b/frontend/roslyn/protocol-samples/does-not-compile/C4_token_in_field.cs.txt @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A token cannot be stored in an object. +public sealed class C4TokenInField +{ + private ApprovedOrder _kept; +} diff --git a/frontend/roslyn/protocol-samples/does-not-compile/C5_async_region.cs.txt b/frontend/roslyn/protocol-samples/does-not-compile/C5_async_region.cs.txt new file mode 100644 index 00000000..41111b12 --- /dev/null +++ b/frontend/roslyn/protocol-samples/does-not-compile/C5_async_region.cs.txt @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A token cannot survive an await: the callback cannot be async. +public static class C5AsyncRegion +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithApproved(order, async approved => + { + await db.SaveChangesAsync(); + approved.Ship(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/does-not-compile/expected.json b/frontend/roslyn/protocol-samples/does-not-compile/expected.json new file mode 100644 index 00000000..455f8401 --- /dev/null +++ b/frontend/roslyn/protocol-samples/does-not-compile/expected.json @@ -0,0 +1,24 @@ +{ + "C1_construct_token": [ + "CS1729", + "CS0122" + ], + "C2_capture_token_in_nested_lambda": [ + "CS8175", + "CS9108" + ], + "C3_token_escapes_to_outer_local": [ + "CS8175", + "CS9108", + "CS4012" + ], + "C4_token_in_field": [ + "CS8345" + ], + "C5_async_region": [ + "CS4012", + "CS1988", + "CS9202", + "CS8177" + ] +} diff --git a/frontend/roslyn/protocol-samples/efcore/.gitignore b/frontend/roslyn/protocol-samples/efcore/.gitignore new file mode 100644 index 00000000..cd42ee34 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/.gitignore @@ -0,0 +1,2 @@ +bin/ +obj/ diff --git a/frontend/roslyn/protocol-samples/efcore/Acceptance/Acceptance.csproj b/frontend/roslyn/protocol-samples/efcore/Acceptance/Acceptance.csproj new file mode 100644 index 00000000..773569a9 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/Acceptance/Acceptance.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + 12 + enable + enable + OrderBackend.Acceptance + + + + + + + diff --git a/frontend/roslyn/protocol-samples/efcore/Acceptance/Program.cs b/frontend/roslyn/protocol-samples/efcore/Acceptance/Program.cs new file mode 100644 index 00000000..4f023cee --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/Acceptance/Program.cs @@ -0,0 +1,138 @@ +using System.Net; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using OrderBackend; +using OrderBackend.Data; +using OrderBackend.Domain; + +// Backend acceptance: the real backend, over a real HTTP listener and a real SQLite file. +// Exit code 0 only when every check holds. + +var failures = 0; +void Check(string id, bool holds, string detail) +{ + Console.WriteLine($"{(holds ? "ok" : "FAIL")}[{id}]: {detail}"); + if (!holds) + failures++; +} + +var dbPath = Path.Combine(Path.GetTempPath(), $"own-protocol-{Guid.NewGuid():N}.db"); +var sql = new List(); +void Database(DbContextOptionsBuilder options) => options + .UseSqlite($"Data Source={dbPath}") + .LogTo(text => { lock (sql) sql.Add(text); }, + new[] { RelationalEventId.CommandExecuted }); + +AppDbContext FreshContext() +{ + var options = new DbContextOptionsBuilder(); + Database(options); + return new AppDbContext(options.Options); +} + +var app = Backend.Build(new[] { "--urls", "http://127.0.0.1:0" }, Database); +await using (var setup = FreshContext()) + await setup.Database.EnsureCreatedAsync(); +await app.StartAsync(); +try +{ + using var http = new HttpClient { BaseAddress = new Uri(app.Urls.First()) }; + + async Task Create(string customer) + { + var response = await http.PostAsync($"/orders?customer={customer}", null); + response.EnsureSuccessStatusCode(); + return int.Parse(response.Headers.Location!.OriginalString.Split('/')[^1]); + } + + async Task StoredStatus(int id) + { + // a SEPARATE context and connection: what this reads is what was committed + await using var db = FreshContext(); + return (await db.Orders.AsNoTracking().SingleAsync(o => o.Id == id)).Status; + } + + // ---- 1. the happy path over HTTP: Draft -> (Submit, Approve) -> Ship ---------------- + var a = await Create("alice"); + Check("http-created-draft", await StoredStatus(a) == OrderStatus.Draft, + "POST /orders stores a Draft"); + var step = await http.PostAsync($"/orders/{a}/submit-and-approve", null); + Check("http-two-transitions", step.StatusCode == HttpStatusCode.OK + && await StoredStatus(a) == OrderStatus.Approved, + "two transitions inside one region are saved: Draft -> Approved"); + step = await http.PostAsync($"/orders/{a}/ship", null); + Check("http-ship", step.StatusCode == HttpStatusCode.OK + && await StoredStatus(a) == OrderStatus.Shipped, + "POST /orders/{id}/ship commits Shipped inside the handler's transaction"); + + // ---- 2. the runtime refinement refuses the wrong state, and nothing is written ------- + var b = await Create("bob"); + step = await http.PostAsync($"/orders/{b}/ship", null); + Check("http-ship-refused", step.StatusCode == HttpStatusCode.Conflict + && await StoredStatus(b) == OrderStatus.Draft, + "shipping a Draft is 409 (the region entry's check), and the row is untouched"); + step = await http.PostAsync($"/orders/{a}/submit-and-approve", null); + Check("http-resubmit-refused", step.StatusCode == HttpStatusCode.Conflict + && await StoredStatus(a) == OrderStatus.Shipped, + "submitting a Shipped order is 409, and the row is untouched"); + step = await http.PostAsync("/orders/999999/ship", null); + Check("http-missing", step.StatusCode == HttpStatusCode.NotFound, "an unknown id is 404"); + + // ---- 3. the token mutates the instance EF tracks ----------------------------------- + var c = await Create("carol"); + (await http.PostAsync($"/orders/{c}/submit-and-approve", null)).EnsureSuccessStatusCode(); + await using (var db = FreshContext()) + { + var order = await db.Orders.SingleAsync(o => o.Id == c); + var entry = db.ChangeTracker.Entries().Single(); + Check("ef-same-instance", ReferenceEquals(entry.Entity, order), + "the queried Order IS the instance the ChangeTracker holds"); + Check("ef-unchanged-before", entry.State == EntityState.Unchanged, + "it is Unchanged before the region"); + + var now = DateTime.UtcNow; + OrderProtocol.WithApproved(order, approved => + { + approved.Ship(now); + }); + + db.ChangeTracker.DetectChanges(); + Check("ef-change-tracked", entry.State == EntityState.Modified + && entry.Property(o => o.Status).IsModified + && entry.Property(o => o.ShippedAt).IsModified + && !entry.Property(o => o.Customer).IsModified, + "the transition is visible to the ChangeTracker as Status + ShippedAt, nothing else"); + Check("ef-no-reattach", db.ChangeTracker.Entries().Count() == 1, + "no second entry appeared: nothing was attached, copied or re-materialized"); + var written = await db.SaveChangesAsync(); + Check("ef-saved", written == 1 && await StoredStatus(c) == OrderStatus.Shipped, + "SaveChangesAsync writes one row and a separate context reads Shipped"); + } + + // ---- 4. plain EF queries: translated by the provider, nothing custom ---------------- + await using (var db = FreshContext()) + { + var text = db.Orders.Where(o => o.Id == c).ToQueryString(); + Check("ef-query-translated", text.Contains("WHERE", StringComparison.Ordinal) + && text.Contains("\"Id\"", StringComparison.Ordinal), + "the handler's predicate is a server-side WHERE on Id"); + } + string[] executed; + lock (sql) + executed = sql.ToArray(); + Check("ef-update-emitted", executed.Any(t => t.Contains("UPDATE \"Orders\" SET", StringComparison.Ordinal) + && t.Contains("\"Status\"", StringComparison.Ordinal)), + "the transitions reach the database as ordinary UPDATE statements"); +} +finally +{ + await app.StopAsync(); + await app.DisposeAsync(); + Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools(); + try { File.Delete(dbPath); } catch (IOException) { } +} + +Console.WriteLine(failures == 0 + ? "backend acceptance: all checks hold" + : $"backend acceptance: {failures} check(s) FAILED"); +return failures == 0 ? 0 : 1; diff --git a/frontend/roslyn/protocol-samples/efcore/OrderBackend/Data/AppDbContext.cs b/frontend/roslyn/protocol-samples/efcore/OrderBackend/Data/AppDbContext.cs new file mode 100644 index 00000000..f2c39a86 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/OrderBackend/Data/AppDbContext.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore; +using OrderBackend.Domain; + +namespace OrderBackend.Data; + +/// A plain DbContext: no custom base, no repository, no interceptor. +public sealed class AppDbContext(DbContextOptions options) : DbContext(options) +{ + public DbSet Orders => Set(); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(order => + { + order.HasKey(o => o.Id); + order.Property(o => o.Status).HasConversion(); + }); + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/Order.cs b/frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/Order.cs new file mode 100644 index 00000000..b04e0bf2 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/Order.cs @@ -0,0 +1,55 @@ +using System; + +namespace OrderBackend.Domain; + +public enum OrderStatus +{ + Draft, + Submitted, + Approved, + Shipped, +} + +/// An ordinary EF Core entity: no base class, no interface, no attribute. EF materializes it +/// through the private constructor and tracks THIS instance; the state tokens in +/// OrderProtocol.cs wrap the same reference and mutate it in place. +public sealed class Order +{ + private Order() + { + } + + public int Id { get; private set; } + + public string Customer { get; private set; } = ""; + + public OrderStatus Status { get; private set; } + + public DateTime? SubmittedAt { get; private set; } + + public DateTime? ApprovedAt { get; private set; } + + public DateTime? ShippedAt { get; private set; } + + public static Order NewDraft(string customer) => + new() { Customer = customer, Status = OrderStatus.Draft }; + + // The transitions themselves. Reachable only through a state token (same assembly). + internal void MarkSubmitted(DateTime at) + { + Status = OrderStatus.Submitted; + SubmittedAt = at; + } + + internal void MarkApproved(DateTime at) + { + Status = OrderStatus.Approved; + ApprovedAt = at; + } + + internal void MarkShipped(DateTime at) + { + Status = OrderStatus.Shipped; + ShippedAt = at; + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs b/frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs new file mode 100644 index 00000000..12c45d76 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/OrderBackend/Domain/OrderProtocol.cs @@ -0,0 +1,102 @@ +using System; + +namespace OrderBackend.Domain; + +// The state protocol of Order, written by hand (a source generator could emit this file; +// none exists yet, and the profile does not need one). +// +// No reflection, no base class, no runtime library: two marker attributes, three ref +// structs, three delegates, three region entries. + +[AttributeUsage(AttributeTargets.Struct)] +public sealed class ProtocolTokenAttribute : Attribute +{ +} + +[AttributeUsage(AttributeTargets.Method)] +public sealed class ProtocolRegionAttribute : Attribute +{ +} + +/// The runtime half of the refinement: the fact came from the database, so it is checked +/// once, at the region entry. +public sealed class InvalidOrderStateException(int id, OrderStatus actual, OrderStatus required) + : InvalidOperationException($"order {id} is {actual}, not {required}") +{ +} + +[ProtocolToken] +public readonly ref struct DraftOrder +{ + private readonly Order _order; + + internal DraftOrder(Order order) => _order = order; + + public int Id => _order.Id; + + public SubmittedOrder Submit(DateTime at) + { + _order.MarkSubmitted(at); + return new SubmittedOrder(_order); + } +} + +[ProtocolToken] +public readonly ref struct SubmittedOrder +{ + private readonly Order _order; + + internal SubmittedOrder(Order order) => _order = order; + + public int Id => _order.Id; + + public void Approve(DateTime at) => _order.MarkApproved(at); +} + +[ProtocolToken] +public readonly ref struct ApprovedOrder +{ + private readonly Order _order; + + internal ApprovedOrder(Order order) => _order = order; + + public int Id => _order.Id; + + public void Ship(DateTime at) => _order.MarkShipped(at); +} + +public delegate void DraftRegion(DraftOrder draft); + +public delegate void SubmittedRegion(SubmittedOrder submitted); + +public delegate void ApprovedRegion(ApprovedOrder approved); + +public static class OrderProtocol +{ + [ProtocolRegion] + public static void WithDraft(Order order, DraftRegion body) + { + Require(order, OrderStatus.Draft); + body(new DraftOrder(order)); + } + + [ProtocolRegion] + public static void WithSubmitted(Order order, SubmittedRegion body) + { + Require(order, OrderStatus.Submitted); + body(new SubmittedOrder(order)); + } + + [ProtocolRegion] + public static void WithApproved(Order order, ApprovedRegion body) + { + Require(order, OrderStatus.Approved); + body(new ApprovedOrder(order)); + } + + private static void Require(Order order, OrderStatus required) + { + if (order.Status != required) + throw new InvalidOrderStateException(order.Id, order.Status, required); + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderBackend.csproj b/frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderBackend.csproj new file mode 100644 index 00000000..e7903e27 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderBackend.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + 12 + enable + enable + OrderBackend + + + + + + + diff --git a/frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs b/frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs new file mode 100644 index 00000000..9508e731 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/OrderBackend/OrderHandlers.cs @@ -0,0 +1,91 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.EntityFrameworkCore; +using OrderBackend.Data; +using OrderBackend.Domain; + +namespace OrderBackend; + +/// The endpoints. Ordinary ASP.NET Core + EF Core code: there is no annotation in this file, +/// and nothing here knows that an analyzer exists. +public static class OrderHandlers +{ + public static async Task Create(string customer, AppDbContext db, CancellationToken ct) + { + var order = Order.NewDraft(customer); + db.Orders.Add(order); + await db.SaveChangesAsync(ct); + return Results.Created($"/orders/{order.Id}", new { order.Id, Status = order.Status.ToString() }); + } + + public static async Task Ship( + int id, + AppDbContext db, + ILogger logger, + CancellationToken ct) + { + await using var tx = await db.Database.BeginTransactionAsync(ct); + + var order = await db.Orders.SingleOrDefaultAsync(x => x.Id == id, ct); + if (order is null) + return Results.NotFound(); + + // Everything that needs a call is done OUTSIDE the region: the clock is read here + // and captured, the log lines sit before and after. + var now = DateTime.UtcNow; + logger.LogInformation("shipping order {OrderId}", order.Id); + try + { + OrderProtocol.WithApproved(order, approved => + { + approved.Ship(now); + }); + } + catch (InvalidOrderStateException refused) + { + return Results.Conflict(refused.Message); + } + + await db.SaveChangesAsync(ct); + await tx.CommitAsync(ct); + logger.LogInformation("shipped order {OrderId}", order.Id); + + return Results.Ok(new { order.Id, Status = order.Status.ToString() }); + } + + public static async Task SubmitAndApprove(int id, AppDbContext db, CancellationToken ct) + { + var order = await db.Orders.SingleOrDefaultAsync(x => x.Id == id, ct); + if (order is null) + return Results.NotFound(); + + var now = DateTime.UtcNow; + try + { + // two transitions inside ONE region: Draft -> Submitted -> Approved + OrderProtocol.WithDraft(order, draft => + { + var submitted = draft.Submit(now); + submitted.Approve(now); + }); + } + catch (InvalidOrderStateException refused) + { + return Results.Conflict(refused.Message); + } + + await db.SaveChangesAsync(ct); + return Results.Ok(new { order.Id, Status = order.Status.ToString() }); + } + + public static async Task Get(int id, AppDbContext db, CancellationToken ct) + { + var order = await db.Orders.AsNoTracking().SingleOrDefaultAsync(x => x.Id == id, ct); + return order is null + ? Results.NotFound() + : Results.Ok(new { order.Id, Status = order.Status.ToString(), order.ShippedAt }); + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/OrderBackend/Program.cs b/frontend/roslyn/protocol-samples/efcore/OrderBackend/Program.cs new file mode 100644 index 00000000..f6677b8c --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/OrderBackend/Program.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore; +using OrderBackend; +using OrderBackend.Data; + +var app = Backend.Build(args, options => + options.UseSqlite("Data Source=orders.db")); + +using (var scope = app.Services.CreateScope()) + scope.ServiceProvider.GetRequiredService().Database.EnsureCreated(); + +app.Run(); + +public partial class Program +{ +} + +namespace OrderBackend +{ + /// The composition root, shared by `Program` and the acceptance runner so both serve + /// exactly the same endpoints over a real HTTP listener. + public static class Backend + { + public static WebApplication Build(string[] args, Action database) + { + var builder = WebApplication.CreateBuilder(args); + builder.Services.AddDbContext(database); + + var app = builder.Build(); + app.MapPost("/orders", OrderHandlers.Create); + app.MapGet("/orders/{id:int}", OrderHandlers.Get); + app.MapPost("/orders/{id:int}/submit-and-approve", OrderHandlers.SubmitAndApprove); + app.MapPost("/orders/{id:int}/ship", OrderHandlers.Ship); + return app; + } + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/accepted/MentionsOnly.cs.txt b/frontend/roslyn/protocol-samples/efcore/accepted/MentionsOnly.cs.txt new file mode 100644 index 00000000..fcf2e12f --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/accepted/MentionsOnly.cs.txt @@ -0,0 +1,22 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using OrderBackend.Data; +using OrderBackend.Domain; + +namespace OrderBackend.Accepted; + +// The boundary refuses OPERATIONS, not mentions. Naming the internal mutator, naming the +// token type and reading the state change nothing, so nothing here is refused. +public static class MentionsOnly +{ + public static async Task Describe(int id, AppDbContext db, CancellationToken ct) + { + var order = await db.Orders.AsNoTracking().SingleAsync(x => x.Id == id, ct); + var transition = nameof(Order.MarkShipped); + var token = typeof(ApprovedOrder).Name; + return Results.Ok(new { order.Id, order.Status, transition, token }); + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/accepted/expected.json b/frontend/roslyn/protocol-samples/efcore/accepted/expected.json new file mode 100644 index 00000000..13c59475 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/accepted/expected.json @@ -0,0 +1,3 @@ +{ + "MentionsOnly": "nameof / typeof / a read of the state: mentions, not operations" +} diff --git a/frontend/roslyn/protocol-samples/efcore/known-gaps/StateWrittenByBulkUpdate.cs.txt b/frontend/roslyn/protocol-samples/efcore/known-gaps/StateWrittenByBulkUpdate.cs.txt new file mode 100644 index 00000000..47842942 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/known-gaps/StateWrittenByBulkUpdate.cs.txt @@ -0,0 +1,23 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using OrderBackend.Data; +using OrderBackend.Domain; + +namespace OrderBackend.KnownGaps; + +// KNOWN GAP, pinned: a set-based UPDATE never materializes an Order at all, so there is no +// instance for a token to guard. The row changes state in the database. +public static class StateWrittenByBulkUpdate +{ + public static async Task Ship(int id, AppDbContext db, CancellationToken ct) + { + await db.Orders + .Where(o => o.Id == id) + .ExecuteUpdateAsync(s => s.SetProperty(o => o.Status, OrderStatus.Shipped), ct); + return Results.Ok(); + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/known-gaps/StateWrittenThroughChangeTracker.cs.txt b/frontend/roslyn/protocol-samples/efcore/known-gaps/StateWrittenThroughChangeTracker.cs.txt new file mode 100644 index 00000000..12ab7577 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/known-gaps/StateWrittenThroughChangeTracker.cs.txt @@ -0,0 +1,24 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using OrderBackend.Data; +using OrderBackend.Domain; + +namespace OrderBackend.KnownGaps; + +// KNOWN GAP, pinned so it cannot be forgotten: EF Core's own metadata API writes the state +// property by NAME, through its public surface. No member of Order is touched in source, no +// token exists, no region is open — and a Draft is saved as Shipped. The protocol constrains +// the entity's C# API, not every path to the row. +public static class StateWrittenThroughChangeTracker +{ + public static async Task Ship(int id, AppDbContext db, CancellationToken ct) + { + var order = await db.Orders.SingleAsync(x => x.Id == id, ct); + db.Entry(order).Property(o => o.Status).CurrentValue = OrderStatus.Shipped; + await db.SaveChangesAsync(ct); + return Results.Ok(); + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/known-gaps/expected.json b/frontend/roslyn/protocol-samples/efcore/known-gaps/expected.json new file mode 100644 index 00000000..7b47eab3 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/known-gaps/expected.json @@ -0,0 +1,4 @@ +{ + "StateWrittenByBulkUpdate": "ExecuteUpdate: no entity instance exists for a token to guard", + "StateWrittenThroughChangeTracker": "EF metadata API writes the state property by name through public surface" +} diff --git a/frontend/roslyn/protocol-samples/efcore/nuget.config b/frontend/roslyn/protocol-samples/efcore/nuget.config new file mode 100644 index 00000000..88397e92 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/nuget.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/roslyn/protocol-samples/efcore/refused/DirectMutator.cs.txt b/frontend/roslyn/protocol-samples/efcore/refused/DirectMutator.cs.txt new file mode 100644 index 00000000..4dc11fd7 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/refused/DirectMutator.cs.txt @@ -0,0 +1,22 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using OrderBackend.Data; +using OrderBackend.Domain; + +namespace OrderBackend.Refused; + +// The transition's own implementation, called past every token: `internal` is open inside +// the project. A Draft becomes Shipped and EF saves it. +public static class DirectMutator +{ + public static async Task Ship(int id, AppDbContext db, CancellationToken ct) + { + var order = await db.Orders.SingleAsync(x => x.Id == id, ct); + order.MarkShipped(DateTime.UtcNow); + await db.SaveChangesAsync(ct); + return Results.Ok(); + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/refused/ForgedToken.cs.txt b/frontend/roslyn/protocol-samples/efcore/refused/ForgedToken.cs.txt new file mode 100644 index 00000000..8c8ca8ac --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/refused/ForgedToken.cs.txt @@ -0,0 +1,24 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using OrderBackend.Data; +using OrderBackend.Domain; + +namespace OrderBackend.Refused; + +// The domain sits in the SAME project as the handlers, so the token's `internal` constructor +// is reachable: this ships an order whose state nobody checked, in a handler with no region. +// The compiler accepts it; the scan must not. +public static class ForgedToken +{ + public static async Task Ship(int id, AppDbContext db, CancellationToken ct) + { + var order = await db.Orders.SingleAsync(x => x.Id == id, ct); + var now = DateTime.UtcNow; + new ApprovedOrder(order).Ship(now); + await db.SaveChangesAsync(ct); + return Results.Ok(); + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/refused/MutatorAsDelegate.cs.txt b/frontend/roslyn/protocol-samples/efcore/refused/MutatorAsDelegate.cs.txt new file mode 100644 index 00000000..401fe9ba --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/refused/MutatorAsDelegate.cs.txt @@ -0,0 +1,22 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using OrderBackend.Data; +using OrderBackend.Domain; + +namespace OrderBackend.Refused; + +// The same bypass with no call written at the entity: the mutator leaves as a delegate. +public static class MutatorAsDelegate +{ + public static async Task Ship(int id, AppDbContext db, CancellationToken ct) + { + var order = await db.Orders.SingleAsync(x => x.Id == id, ct); + Action ship = order.MarkShipped; + ship(DateTime.UtcNow); + await db.SaveChangesAsync(ct); + return Results.Ok(); + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/refused/SaveInsideRegion.cs.txt b/frontend/roslyn/protocol-samples/efcore/refused/SaveInsideRegion.cs.txt new file mode 100644 index 00000000..e0ae047f --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/refused/SaveInsideRegion.cs.txt @@ -0,0 +1,27 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using OrderBackend.Data; +using OrderBackend.Domain; + +namespace OrderBackend.Refused; + +// Saving from INSIDE the region: `SaveChanges` runs the whole EF pipeline (interceptors, +// change detection over the entity the region holds exclusively). Nothing states what that +// does to the entity, so the region is refused; the same call after the region is ordinary. +public static class SaveInsideRegion +{ + public static async Task Ship(int id, AppDbContext db, CancellationToken ct) + { + var order = await db.Orders.SingleAsync(x => x.Id == id, ct); + var now = DateTime.UtcNow; + OrderProtocol.WithApproved(order, approved => + { + approved.Ship(now); + db.SaveChanges(); + }); + return Results.Ok(); + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/refused/ShipWithLogInsideRegion.cs.txt b/frontend/roslyn/protocol-samples/efcore/refused/ShipWithLogInsideRegion.cs.txt new file mode 100644 index 00000000..2d10fe16 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/refused/ShipWithLogInsideRegion.cs.txt @@ -0,0 +1,33 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.EntityFrameworkCore; +using OrderBackend.Data; +using OrderBackend.Domain; + +namespace OrderBackend.Refused; + +// The same logging call that is fine before and after the region (see OrderHandlers.Ship) +// is REFUSED inside it: nothing states what `LogInformation` does, and an exclusive region +// does not analyse unprovable code as safe. +public static class ShipWithLogInsideRegion +{ + public static async Task Ship( + int id, + AppDbContext db, + ILogger logger, + CancellationToken ct) + { + var order = await db.Orders.SingleAsync(x => x.Id == id, ct); + var now = DateTime.UtcNow; + OrderProtocol.WithApproved(order, approved => + { + logger.LogInformation("shipping inside the region"); + approved.Ship(now); + }); + await db.SaveChangesAsync(ct); + return Results.Ok(); + } +} diff --git a/frontend/roslyn/protocol-samples/efcore/refused/expected.json b/frontend/roslyn/protocol-samples/efcore/refused/expected.json new file mode 100644 index 00000000..8d1af494 --- /dev/null +++ b/frontend/roslyn/protocol-samples/efcore/refused/expected.json @@ -0,0 +1,7 @@ +{ + "DirectMutator": "'MarkShipped' of 'Order' is not public and belongs to a state protocol", + "ForgedToken": "a protocol token 'ApprovedOrder' is created outside the protocol's own types", + "MutatorAsDelegate": "'MarkShipped' of 'Order' is not public and belongs to a state protocol", + "SaveInsideRegion": "a call to 'Microsoft.EntityFrameworkCore.DbContext.SaveChanges' inside a protocol region runs code with no stated contract", + "ShipWithLogInsideRegion": "a call to 'Microsoft.Extensions.Logging.LoggerExtensions.LogInformation' inside a protocol region runs code with no stated contract" +} diff --git a/frontend/roslyn/protocol-samples/refused/R10_property_getter_inside_region.cs.txt b/frontend/roslyn/protocol-samples/refused/R10_property_getter_inside_region.cs.txt new file mode 100644 index 00000000..a4beb2dc --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R10_property_getter_inside_region.cs.txt @@ -0,0 +1,20 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A property getter is a call too: `DateTime.UtcNow` runs code the lowering cannot read. +// Compute it BEFORE the region and capture the value. +public static class R10PropertyGetterInsideRegion +{ + public static void Run(Order order) + { + Protocol.WithApproved(order, approved => + { + var now = DateTime.UtcNow; + approved.Ship(); + }); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R11_constructor_inside_region.cs.txt b/frontend/roslyn/protocol-samples/refused/R11_constructor_inside_region.cs.txt new file mode 100644 index 00000000..84ffef28 --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R11_constructor_inside_region.cs.txt @@ -0,0 +1,19 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A constructor is a call as well. +public static class R11ConstructorInsideRegion +{ + public static void Run(Order order) + { + Protocol.WithApproved(order, approved => + { + var buffer = new MemoryStream(); + approved.Ship(); + }); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R12_default_token_temporary.cs.txt b/frontend/roslyn/protocol-samples/refused/R12_default_token_temporary.cs.txt new file mode 100644 index 00000000..ea8e2c9d --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R12_default_token_temporary.cs.txt @@ -0,0 +1,13 @@ +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// No region, no token local: the forged token is a temporary. `default` compiles from any +// assembly, so only the lowering can refuse it — in a method it otherwise has no reason to read. +public static class R12DefaultTokenTemporary +{ + public static void Run(Order order) + { + default(ApprovedOrder).Ship(); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R13_public_mutator_reaches_protocol_state.cs.txt b/frontend/roslyn/protocol-samples/refused/R13_public_mutator_reaches_protocol_state.cs.txt new file mode 100644 index 00000000..cb3ed4fc --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R13_public_mutator_reaches_protocol_state.cs.txt @@ -0,0 +1,44 @@ +using System; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// ADMISSION. The token's transition and a PUBLIC method of the entity reach the same write. +// The public one needs no token, so every guarantee of the region is optional: the protocol +// is refused as declared, wherever it is (or is not) used. +public sealed class Doc +{ + public bool Published { get; private set; } + + internal void PublishCore() => Published = true; + + public void Publish() => PublishCore(); // the open door +} + +[ProtocolToken] +public readonly ref struct DraftDoc +{ + private readonly Doc _doc; + internal DraftDoc(Doc doc) => _doc = doc; + + public void Publish() => _doc.PublishCore(); +} + +public delegate void DraftDocRegion(DraftDoc draft); + +public static class DocProtocol +{ + [ProtocolRegion] + public static void WithDraft(Doc doc, DraftDocRegion body) => body(new DraftDoc(doc)); +} + +public static class R13PublicMutatorReachesProtocolState +{ + public static void Run(Doc doc) + { + DocProtocol.WithDraft(doc, draft => + { + draft.Publish(); + }); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R14_public_setter_of_protocol_state.cs.txt b/frontend/roslyn/protocol-samples/refused/R14_public_setter_of_protocol_state.cs.txt new file mode 100644 index 00000000..aad4c58c --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R14_public_setter_of_protocol_state.cs.txt @@ -0,0 +1,28 @@ +using System; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// ADMISSION, the plain form: the state a transition writes has a public setter. Nothing in +// this file even opens a region; the declaration alone is what is refused. +public sealed class Ticket +{ + public int State { get; set; } // the open door +} + +[ProtocolToken] +public readonly ref struct OpenTicket +{ + private readonly Ticket _ticket; + internal OpenTicket(Ticket ticket) => _ticket = ticket; + + public void Close() => _ticket.State = 2; +} + +public delegate void OpenTicketRegion(OpenTicket open); + +public static class TicketProtocol +{ + [ProtocolRegion] + public static void WithOpen(Ticket ticket, OpenTicketRegion body) => body(new OpenTicket(ticket)); +} diff --git a/frontend/roslyn/protocol-samples/refused/R1_default_token.cs.txt b/frontend/roslyn/protocol-samples/refused/R1_default_token.cs.txt new file mode 100644 index 00000000..adbe9bf9 --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R1_default_token.cs.txt @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// `default` compiles for any struct, so the language cannot stop this forgery. +// The lowering must: a token may only come from a region entry, a transition, or a copy. +public static class R1DefaultToken +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithApproved(order, approved => + { + ApprovedOrder forged = default; + forged.Ship(); + approved.Ship(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R2_token_outside_region.cs.txt b/frontend/roslyn/protocol-samples/refused/R2_token_outside_region.cs.txt new file mode 100644 index 00000000..4dd96130 --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R2_token_outside_region.cs.txt @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A token-typed local outside any region has no region to belong to. +public static class R2TokenOutsideRegion +{ + public static void Run(Order order) + { + ApprovedOrder forged = default; + forged.Ship(); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R3_return_inside_region.cs.txt b/frontend/roslyn/protocol-samples/refused/R3_return_inside_region.cs.txt new file mode 100644 index 00000000..425092b9 --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R3_return_inside_region.cs.txt @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// `return` inside the callback leaves the region, not the method; not modelled -> refused. +public static class R3ReturnInsideRegion +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithApproved(order, approved => + { + if (approved.Id == 0) + { + return; + } + approved.Ship(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R4_loop_inside_region.cs.txt b/frontend/roslyn/protocol-samples/refused/R4_loop_inside_region.cs.txt new file mode 100644 index 00000000..4349dfe8 --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R4_loop_inside_region.cs.txt @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A loop inside a region is not modelled -> refused, never silently skipped. +public static class R4LoopInsideRegion +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Protocol.WithApproved(order, approved => + { + for (var i = 0; i < 2; i++) + { + Audit.Record(new Order()); + } + approved.Ship(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R5_token_passed_to_helper.cs.txt b/frontend/roslyn/protocol-samples/refused/R5_token_passed_to_helper.cs.txt new file mode 100644 index 00000000..0c075e68 --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R5_token_passed_to_helper.cs.txt @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A token handed to another method: tokens do not cross method boundaries in this profile. +public static class R5TokenPassedToHelper +{ + private static void Finish(ApprovedOrder approved) => approved.Ship(); + + public static void Run(Order order) + { + Protocol.WithApproved(order, approved => + { + Finish(approved); + }); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R6_region_body_not_a_lambda.cs.txt b/frontend/roslyn/protocol-samples/refused/R6_region_body_not_a_lambda.cs.txt new file mode 100644 index 00000000..2a127c1d --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R6_region_body_not_a_lambda.cs.txt @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// The region body must be written in place, or there is no block to lower. +public static class R6RegionBodyNotALambda +{ + private static void Body(ApprovedOrder approved) => approved.Ship(); + + public static void Run(Order order) + { + Protocol.WithApproved(order, Body); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R7_entity_not_a_local.cs.txt b/frontend/roslyn/protocol-samples/refused/R7_entity_not_a_local.cs.txt new file mode 100644 index 00000000..35a45863 --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R7_entity_not_a_local.cs.txt @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// The entity must be a local or a parameter: that symbol IS the resource identity. +public static class R7EntityNotALocal +{ + public static void Run(Db db) + { + Protocol.WithApproved(db.SingleAsync(1).Result, approved => + { + approved.Ship(); + }); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R8_token_not_a_ref_struct.cs.txt b/frontend/roslyn/protocol-samples/refused/R8_token_not_a_ref_struct.cs.txt new file mode 100644 index 00000000..4d6ac175 --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R8_token_not_a_ref_struct.cs.txt @@ -0,0 +1,32 @@ +using System; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A token type that is NOT a ref struct could be captured, stored or awaited across, and so +// reach another region. The language only forbids that for ref structs; the lowering refuses +// anything weaker. +[ProtocolToken] +public readonly struct LooseToken +{ + public void Spend() { } +} + +public delegate void LooseRegion(LooseToken token); + +public static class LooseProtocol +{ + [ProtocolRegion] + public static void With(Order order, LooseRegion body) => body(default); +} + +public static class R8TokenNotARefStruct +{ + public static void Run(Order order) + { + LooseProtocol.With(order, token => + { + token.Spend(); + }); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/R9_opaque_call_inside_region.cs.txt b/frontend/roslyn/protocol-samples/refused/R9_opaque_call_inside_region.cs.txt new file mode 100644 index 00000000..ce673b80 --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R9_opaque_call_inside_region.cs.txt @@ -0,0 +1,25 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// A call inside the region that never mentions the entity, and mutates it anyway. +// Nothing at the call site can be lowered, so nothing proves the call harmless - and an +// exclusive region does not analyse unprovable code as safe. REFUSED, never clean. +// (When effect summaries can prove a callee harmless, this is the rule they relax.) +public static class R9OpaqueCallInsideRegion +{ + public static async Task Run(Db db, int id) + { + var order = await db.SingleAsync(id); + Backdoor.Remember(order); + Protocol.WithApproved(order, approved => + { + Backdoor.AnnotateLast(); // mutates `order`; nothing here says so + approved.Ship(); + }); + await db.SaveChangesAsync(); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/expected.json b/frontend/roslyn/protocol-samples/refused/expected.json new file mode 100644 index 00000000..87ce6617 --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/expected.json @@ -0,0 +1,16 @@ +{ + "R10_property_getter_inside_region": "property 'UtcNow' inside a protocol region runs code with no stated contract", + "R11_constructor_inside_region": "a constructor call inside a protocol region runs code with no stated contract", + "R12_default_token_temporary": "a protocol token 'ApprovedOrder' is created outside the protocol's own types", + "R13_public_mutator_reaches_protocol_state": "'Doc.Publish' is public and writes 'Published', state the protocol's transitions own", + "R14_public_setter_of_protocol_state": "'Ticket.State' is public and writes 'State', state the protocol's transitions own", + "R1_default_token": "has no lowerable origin", + "R2_token_outside_region": "is declared outside a protocol region", + "R3_return_inside_region": "`return` inside a protocol region", + "R4_loop_inside_region": "ForStatement inside a protocol region is not modelled", + "R5_token_passed_to_helper": "a token does not cross a method boundary", + "R6_region_body_not_a_lambda": "must be a lambda written in place", + "R7_entity_not_a_local": "must be a local or a parameter", + "R8_token_not_a_ref_struct": "must be a ref struct", + "R9_opaque_call_inside_region": "a call to 'Own.Protocols.Sample.Backdoor.AnnotateLast' inside a protocol region runs code with no stated contract" +} diff --git a/frontend/roslyn/protocol-samples/same-assembly/S1_forged_token.cs.txt b/frontend/roslyn/protocol-samples/same-assembly/S1_forged_token.cs.txt new file mode 100644 index 00000000..5f0e7362 --- /dev/null +++ b/frontend/roslyn/protocol-samples/same-assembly/S1_forged_token.cs.txt @@ -0,0 +1,13 @@ +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// In the protocol's OWN assembly the `internal` constructor is reachable: a token for a state +// nobody checked, spent in a method with no region at all. +public static class S1ForgedToken +{ + public static void Run(Order order) + { + new ApprovedOrder(order).Ship(); + } +} diff --git a/frontend/roslyn/protocol-samples/same-assembly/S2_internal_setter.cs.txt b/frontend/roslyn/protocol-samples/same-assembly/S2_internal_setter.cs.txt new file mode 100644 index 00000000..f0f27831 --- /dev/null +++ b/frontend/roslyn/protocol-samples/same-assembly/S2_internal_setter.cs.txt @@ -0,0 +1,15 @@ +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// The state itself, written past every token: `internal set` is open inside the assembly. +// Reading the same property stays ordinary. +public static class S2InternalSetter +{ + public static bool Run(Order order) + { + var wasDraft = order.Status == OrderStatus.Draft; + order.Status = OrderStatus.Shipped; + return wasDraft; + } +} diff --git a/frontend/roslyn/protocol-samples/same-assembly/S3_object_initializer.cs.txt b/frontend/roslyn/protocol-samples/same-assembly/S3_object_initializer.cs.txt new file mode 100644 index 00000000..22976f6e --- /dev/null +++ b/frontend/roslyn/protocol-samples/same-assembly/S3_object_initializer.cs.txt @@ -0,0 +1,12 @@ +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// An entity born in a state no transition produced. +public static class S3ObjectInitializer +{ + public static Order Run() + { + return new Order { Status = OrderStatus.Shipped }; + } +} diff --git a/frontend/roslyn/protocol-samples/same-assembly/S4_read_only_use.cs.txt b/frontend/roslyn/protocol-samples/same-assembly/S4_read_only_use.cs.txt new file mode 100644 index 00000000..f9f408bc --- /dev/null +++ b/frontend/roslyn/protocol-samples/same-assembly/S4_read_only_use.cs.txt @@ -0,0 +1,15 @@ +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// The control: the same assembly, the same entity, only its PUBLIC surface — reads of the +// state, the public factory, the public `Annotate`. Nothing to refuse. +public static class S4ReadOnlyUse +{ + public static bool Run(int id) + { + var order = Order.Load(id); + order.Annotate("seen"); + return order.Status == OrderStatus.Draft && order.Id == id && order.Note == "seen"; + } +} diff --git a/frontend/roslyn/protocol-samples/same-assembly/S5_mentions_are_not_operations.cs.txt b/frontend/roslyn/protocol-samples/same-assembly/S5_mentions_are_not_operations.cs.txt new file mode 100644 index 00000000..05ee5442 --- /dev/null +++ b/frontend/roslyn/protocol-samples/same-assembly/S5_mentions_are_not_operations.cs.txt @@ -0,0 +1,14 @@ +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// The control for the other direction: the same assembly, and the protocol's non-public +// surface only MENTIONED — a name, a type, a read. Nothing changes state, nothing is refused. +public static class S5MentionsAreNotOperations +{ + public static string Run(Order order) + { + var state = order.Status; + return nameof(Order.Status) + nameof(ApprovedOrder) + typeof(DraftOrder).Name + state; + } +} diff --git a/frontend/roslyn/protocol-samples/same-assembly/expected.json b/frontend/roslyn/protocol-samples/same-assembly/expected.json new file mode 100644 index 00000000..f3ce9414 --- /dev/null +++ b/frontend/roslyn/protocol-samples/same-assembly/expected.json @@ -0,0 +1,7 @@ +{ + "S1_forged_token": "a protocol token 'ApprovedOrder' is created outside the protocol's own types", + "S2_internal_setter": "'Status' of 'Order' is not public and belongs to a state protocol", + "S3_object_initializer": "'Status' of 'Order' is not public and belongs to a state protocol", + "S4_read_only_use": null, + "S5_mentions_are_not_operations": null +} diff --git a/scripts/protocol_gate.py b/scripts/protocol_gate.py new file mode 100644 index 00000000..213c8f87 --- /dev/null +++ b/scripts/protocol_gate.py @@ -0,0 +1,519 @@ +#!/usr/bin/env python3 +"""State protocols (P-010, pillar 9): real C# -> OwnIR v1 -> core, tied back to source. + +The Layer 2/3 ledgers freeze the facts the Roslyn lowering emits for the state-protocol +surface, and both engines replay them with zero `dotnet`. This script is the other half: it +runs the REAL extractor over the C# those facts came from, so a frozen fact can never drift +away from the program it claims to describe. Five questions (needs `dotnet` on PATH; zero +Python dependencies). Every path below is under `frontend/roslyn/protocol-samples/`: + +1. **cases** — every `cases/.cs` is run through the extractor (with the sample API + beside it). The facts must equal the committed fixture + `tests/fixtures/lowered/typestate_cs_.facts.json` (JSON-equal: the extractor writes + platform newlines), and the reference engine's verdict on them must equal + `cases/expected.json`. + +2. **refused** — every `refused/.cs.txt` compiles, and the extractor must REFUSE it + (exit 2) with the text in `expected.json`: a shape the core cannot check, code with no + contract inside a region, a protocol that is not admitted. + +3. **does-not-compile** — every `does-not-compile/.cs.txt` must be rejected by the C# + compiler itself, built against the API as a SEPARATE assembly (so `internal` means what + it means to a real consumer). + +4. **same-assembly** — every `same-assembly/.cs.txt` compiles when it shares an + assembly with the API (where `internal` protects nothing), and the extractor must refuse + it all the same: a token made by hand, the protocol's state written past every token. A + control with a null expectation must be ACCEPTED: a mention is not an operation. + +5. **efcore** — `efcore/` is an ordinary ASP.NET Core + EF Core backend. It must run (its + acceptance runner drives real HTTP against real SQLite); the extractor must lower its + handlers to the committed `tests/fixtures/lowered/typestate_ef_orderbackend.facts.json` + with a clean verdict, from the project file alone; a copy whose handlers rely on IMPLICIT + usings must be refused rather than silently lose its regions; and the handlers under + `efcore/refused/`, `efcore/accepted/` and `efcore/known-gaps/` — all of which the C# + compiler accepts inside that same project — must be refused, accepted, and (still) + accepted. + +The refused / does-not-compile sources carry a `.cs.txt` suffix on purpose: a directory +scan must not pick up programs whose whole point is to be refused. The samples live under +`frontend/roslyn/` rather than `examples/` for a related reason: `examples/` is swept as one +document by the #260 shadow sweep, and a backend whose references that walk does not have +is, correctly, a refusal of the whole scan. + +Run: python scripts/protocol_gate.py (verify) + python scripts/protocol_gate.py --write (regenerate the fixtures from C#) + python scripts/protocol_gate.py --rust (also compare the two public CLIs) +""" + +from __future__ import annotations + +import json +import os +import re +import shutil +import subprocess +import sys +import tempfile + +ROOT = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) +sys.path.insert(0, ROOT) + +from ownlang.ownir import OwnIRError, check_facts # noqa: E402 + +PROTO = os.path.join(ROOT, "frontend", "roslyn", "protocol-samples") +EXTRACTOR = os.path.join(ROOT, "frontend", "roslyn", "OwnSharp.Extractor") +FIXDIR = os.path.join(ROOT, "tests", "fixtures", "lowered") +SAMPLES_REL = "frontend/roslyn/protocol-samples" +API_REL = f"{SAMPLES_REL}/Api" + + +def _run(cmd: list[str], cwd: str = ROOT) -> subprocess.CompletedProcess[str]: + return subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, encoding="utf-8", + errors="replace", check=False) + + +def _extractor_dll() -> str: + built = _run(["dotnet", "build", EXTRACTOR, "-c", "Release", "-nologo", "-v", "q"]) + if built.returncode != 0: + print(built.stdout[-2000:]) + raise SystemExit("FAIL: the extractor does not build") + return os.path.join(EXTRACTOR, "bin", "Release", "net8.0", "ownsharp-extract.dll") + + +def _fixture(case: str) -> str: + return os.path.join(FIXDIR, f"typestate_cs_{case.lower()}.facts.json") + + +def _verdict(facts: dict[str, object]) -> list[str] | str: + try: + return sorted({f.code for f in check_facts(facts)}) + except OwnIRError as e: + return f"refused: {e}" + + +def cases(dll: str, write: bool, fails: list[str]) -> int: + with open(os.path.join(PROTO, "cases", "expected.json"), encoding="utf-8") as f: + expected = json.load(f) + on_disk = sorted(n[:-3] for n in os.listdir(os.path.join(PROTO, "cases")) + if n.endswith(".cs")) + if on_disk != sorted(expected): + fails.append(f"cases: expected.json {sorted(expected)} != sources {on_disk}") + return 0 + with tempfile.TemporaryDirectory() as tmp: + for case in on_disk: + out = os.path.join(tmp, f"{case}.json") + # relative, forward-slash inputs: the `file` fields are part of the fixture + done = _run(["dotnet", dll, API_REL, f"{SAMPLES_REL}/cases/{case}.cs", + "--flow-locals", "-o", out]) + if done.returncode != 0: + fails.append(f"cases/{case}: the extractor exited {done.returncode}: " + f"{done.stderr.strip()[-300:]}") + continue + with open(out, encoding="utf-8") as f: + facts = json.load(f) + fixture = _fixture(case) + if write: + with open(fixture, "w", encoding="utf-8", newline="\n") as f: + json.dump(facts, f, indent=2, ensure_ascii=False) + f.write("\n") + elif not os.path.exists(fixture): + fails.append(f"cases/{case}: fixture missing ({os.path.basename(fixture)}); " + f"run with --write") + else: + with open(fixture, encoding="utf-8") as f: + if json.load(f) != facts: + fails.append(f"cases/{case}: the extractor no longer emits the " + f"committed facts ({os.path.basename(fixture)})") + got = _verdict(facts) + if got != expected[case]: + fails.append(f"cases/{case}: verdict {got!r}, expected {expected[case]!r}") + return len(on_disk) + + +def _staged(sub: str, case: str, tmp: str) -> str: + """Copy `.cs.txt` to `/.cs` and return it relative to ROOT-less tmp.""" + dst = os.path.join(tmp, f"{case}.cs") + shutil.copyfile(os.path.join(PROTO, sub, f"{case}.cs.txt"), dst) + return dst + + +def refused(dll: str, fails: list[str]) -> int: + with open(os.path.join(PROTO, "refused", "expected.json"), encoding="utf-8") as f: + expected = json.load(f) + on_disk = sorted(n[:-7] for n in os.listdir(os.path.join(PROTO, "refused")) + if n.endswith(".cs.txt")) + if on_disk != sorted(expected): + fails.append(f"refused: expected.json {sorted(expected)} != sources {on_disk}") + return 0 + with tempfile.TemporaryDirectory() as tmp: + for case in on_disk: + src = _staged("refused", case, tmp) + out = os.path.join(tmp, f"{case}.json") + done = _run(["dotnet", dll, API_REL, src, "--flow-locals", "-o", out]) + if done.returncode != 2: + fails.append(f"refused/{case}: the extractor exited {done.returncode}, " + f"expected a refusal (2)") + elif os.path.exists(out): + fails.append(f"refused/{case}: a facts file was written despite the refusal") + elif expected[case] not in done.stderr: + fails.append(f"refused/{case}: refusal text lacks {expected[case]!r}: " + f"{done.stderr.strip()[-300:]}") + return len(on_disk) + + +_PROJECT = """ + + net8.0 + 12 + enable + false + CS1998;CS0169;CS0649;CS0168;CS0219;CS0414 + + {items} + +""" + + +def does_not_compile(fails: list[str]) -> int: + with open(os.path.join(PROTO, "does-not-compile", "expected.json"), encoding="utf-8") as f: + expected = json.load(f) + on_disk = sorted(n[:-7] for n in os.listdir(os.path.join(PROTO, "does-not-compile")) + if n.endswith(".cs.txt")) + if on_disk != sorted(expected): + fails.append(f"does-not-compile: expected.json {sorted(expected)} != sources {on_disk}") + return 0 + with tempfile.TemporaryDirectory() as tmp: + api = os.path.join(tmp, "Api") + os.makedirs(api) + shutil.copyfile(os.path.join(PROTO, "Api", "ProtocolApi.cs"), + os.path.join(api, "ProtocolApi.cs")) + with open(os.path.join(api, "Api.csproj"), "w", encoding="utf-8") as f: + f.write(_PROJECT.format(items='')) + # the positive control: the compilable cases DO build against the same API + ok = os.path.join(tmp, "Ok") + os.makedirs(ok) + for n in os.listdir(os.path.join(PROTO, "cases")): + if n.endswith(".cs"): + shutil.copyfile(os.path.join(PROTO, "cases", n), os.path.join(ok, n)) + for n in os.listdir(os.path.join(PROTO, "refused")): + if n.endswith(".cs.txt"): + shutil.copyfile(os.path.join(PROTO, "refused", n), os.path.join(ok, n[:-4])) + with open(os.path.join(ok, "Ok.csproj"), "w", encoding="utf-8") as f: + f.write(_PROJECT.format(items='' + '')) + built = _run(["dotnet", "build", ok, "-nologo", "-v", "q"], cwd=tmp) + if built.returncode != 0: + codes = sorted(set(re.findall(r"error (CS\d+)", built.stdout))) + fails.append(f"does-not-compile: the positive control (cases + refused) does not " + f"build: {codes}") + for case in on_disk: + proj = os.path.join(tmp, case) + os.makedirs(proj) + shutil.copyfile(os.path.join(PROTO, "does-not-compile", f"{case}.cs.txt"), + os.path.join(proj, f"{case}.cs")) + with open(os.path.join(proj, f"{case}.csproj"), "w", encoding="utf-8") as f: + f.write(_PROJECT.format(items='' + '')) + built = _run(["dotnet", "build", proj, "-nologo", "-v", "q"], cwd=tmp) + codes = sorted(set(re.findall(r"error (CS\d+)", built.stdout))) + if built.returncode == 0: + fails.append(f"does-not-compile/{case}: it COMPILES") + elif not set(codes) & set(expected[case]): + fails.append(f"does-not-compile/{case}: errors {codes}, expected one of " + f"{expected[case]}") + return len(on_disk) + + +def same_assembly(dll: str, fails: list[str]) -> int: + """The protocol and its users in ONE assembly. There `internal` holds nothing, so every + program under `same-assembly/` COMPILES; the scan has to hold the boundary instead + (exit 2, the text in `expected.json`), and must still accept the control whose expected + text is null — the same assembly, the entity's public surface only.""" + with open(os.path.join(PROTO, "same-assembly", "expected.json"), encoding="utf-8") as f: + expected = json.load(f) + on_disk = sorted(n[:-7] for n in os.listdir(os.path.join(PROTO, "same-assembly")) + if n.endswith(".cs.txt")) + if on_disk != sorted(expected): + fails.append(f"same-assembly: expected.json {sorted(expected)} != sources {on_disk}") + return 0 + with tempfile.TemporaryDirectory() as tmp: + one = os.path.join(tmp, "One") + os.makedirs(one) + shutil.copyfile(os.path.join(PROTO, "Api", "ProtocolApi.cs"), + os.path.join(one, "ProtocolApi.cs")) + for case in on_disk: + shutil.copyfile(os.path.join(PROTO, "same-assembly", f"{case}.cs.txt"), + os.path.join(one, f"{case}.cs")) + with open(os.path.join(one, "One.csproj"), "w", encoding="utf-8") as f: + f.write(_PROJECT.format(items='')) + built = _run(["dotnet", "build", one, "-nologo", "-v", "q"], cwd=tmp) + if built.returncode != 0: + codes = sorted(set(re.findall(r"error (CS\d+)", built.stdout))) + fails.append(f"same-assembly: the programs do not build in one assembly: {codes}") + src = os.path.join(tmp, "src") + os.makedirs(src) + for case in on_disk: + staged = _staged("same-assembly", case, src) + out = os.path.join(tmp, f"{case}.json") + done = _run(["dotnet", dll, API_REL, staged, "--flow-locals", "-o", out]) + if expected[case] is None: + if done.returncode != 0: + fails.append(f"same-assembly/{case}: the control was not accepted (exit " + f"{done.returncode}): {done.stderr.strip()[-300:]}") + elif done.returncode != 2: + fails.append(f"same-assembly/{case}: the extractor exited {done.returncode}, " + f"expected a refusal (2)") + elif os.path.exists(out): + fails.append(f"same-assembly/{case}: a facts file was written despite the " + f"refusal") + elif expected[case] not in done.stderr: + fails.append(f"same-assembly/{case}: refusal text lacks {expected[case]!r}: " + f"{done.stderr.strip()[-300:]}") + return len(on_disk) + + +EF = os.path.join(PROTO, "efcore") +EF_REL = f"{SAMPLES_REL}/efcore/OrderBackend" +EF_PROJECT = f"{EF_REL}/OrderBackend.csproj" +EF_FIXTURE = os.path.join(FIXDIR, "typestate_ef_orderbackend.facts.json") +EF_REGIONS = ("OrderBackend.OrderHandlers.Ship$protocol", + "OrderBackend.OrderHandlers.SubmitAndApprove$protocol") +EF_CHECKS = 13 +# Textual, and labelled as such: the handler file carries no attribute, and the backend's own +# sources reach for neither reflection nor a custom query provider. +_EF_FORBIDDEN = ("System.Reflection", ".GetType()", "Activator.", "dynamic ", "IQueryProvider", + "ExpressionVisitor", "Expression<") +# The usings a Web SDK project gets implicitly. The scan does not read `obj/`, so a handler +# that leans on them does not bind; the staged copy below removes exactly these lines. +_EF_IMPLICIT = re.compile(r"^using (System|System\.Threading|System\.Threading\.Tasks|" + r"System\.Linq|Microsoft\.AspNetCore\.Http|" + r"Microsoft\.Extensions\.Logging);\n", flags=re.M) +# What a staged handler's directory promises: the extractor's answer to it. +_EF_STAGED = {"refused": "refuse", "accepted": "accept", "known-gaps": "accept"} + + +def _ef_sources() -> list[str]: + out = [] + for base, dirs, names in os.walk(os.path.join(EF, "OrderBackend")): + dirs[:] = [d for d in dirs if d not in ("bin", "obj")] + out += [os.path.join(base, n) for n in names if n.endswith(".cs")] + return sorted(out) + + +def _has_region(flow: list[dict[str, object]]) -> bool: + return any(op.get("op") == "borrow_mut" for op in flow) + + +def _stage_backend(tmp: str, name: str) -> str: + """A buildable copy of the backend project (sources only) under `//`.""" + stage = os.path.join(tmp, name) + shutil.copytree(os.path.join(EF, "OrderBackend"), os.path.join(stage, "OrderBackend"), + ignore=shutil.ignore_patterns("bin", "obj")) + shutil.copyfile(os.path.join(EF, "nuget.config"), os.path.join(stage, "nuget.config")) + return stage + + +def efcore(dll: str, write: bool, fails: list[str]) -> int: + """The same lowering over a REAL ASP.NET Core + EF Core backend. + + * the backend runs: a real HTTP listener, a real SQLite file, the transitions are seen by + the ChangeTracker on the instance EF tracks and are saved (`Acceptance`, its own exit code); + * the handler file is plain: no attribute; the sources use no reflection, no custom LINQ; + * the scan of the project file alone emits the committed facts, both regions are present + in them, and the verdict is clean — with the opaque calls (logger, EF, `Results.*`, + `await using`, `try`/`catch`, `return`) sitting AROUND the regions; + * the same handlers leaning on implicit usings still compile, and are REFUSED (exit 2) + instead of losing their regions; + * staged handlers the compiler accepts are refused / accepted as their directory says.""" + n = 0 + built = _run(["dotnet", "build", os.path.join(EF, "Acceptance"), "-nologo", "-v", "q"]) + if built.returncode != 0: + fails.append(f"efcore: the backend does not build: {built.stdout.strip()[-400:]}") + return n + ran = _run(["dotnet", os.path.join(EF, "Acceptance", "bin", "Debug", "net8.0", + "Acceptance.dll")]) + oks = len(re.findall(r"^ok\[", ran.stdout, flags=re.M)) + if (ran.returncode != 0 or "all checks hold" not in ran.stdout or oks != EF_CHECKS + or "FAIL[" in ran.stdout): + fails.append(f"efcore/acceptance: exit {ran.returncode}, {oks}/{EF_CHECKS} checks: " + f"{ran.stdout.strip()[-400:]}") + n += oks + + with open(os.path.join(EF, "OrderBackend", "OrderHandlers.cs"), encoding="utf-8") as f: + handlers = f.read() + if re.search(r"^\s*\[", handlers, flags=re.M): + fails.append("efcore/plain: OrderHandlers.cs carries an attribute") + for path in _ef_sources(): + with open(path, encoding="utf-8") as f: + text = f.read() + for word in _EF_FORBIDDEN: + if word in text: + fails.append(f"efcore/plain: {os.path.basename(path)} uses {word!r}") + n += 1 + + with tempfile.TemporaryDirectory() as tmp: + # (a) the scan of the project file alone: the committed facts, both regions, clean + out = os.path.join(tmp, "bound.json") + done = _run(["dotnet", dll, EF_PROJECT, "--flow-locals", "-o", out]) + if done.returncode != 0: + fails.append(f"efcore/bound: the extractor exited {done.returncode}: " + f"{done.stderr.strip()[-300:]}") + else: + with open(out, encoding="utf-8") as f: + facts = json.load(f) + by_name = {fn["name"]: fn for fn in facts.get("functions", [])} + for region in EF_REGIONS: + if region not in by_name or not _has_region(by_name[region]["body"]): + fails.append(f"efcore/bound: no protocol region lowered for {region}") + if write: + with open(EF_FIXTURE, "w", encoding="utf-8", newline="\n") as f: + json.dump(facts, f, indent=2, ensure_ascii=False) + f.write("\n") + elif not os.path.exists(EF_FIXTURE): + fails.append("efcore/bound: fixture missing; run with --write") + else: + with open(EF_FIXTURE, encoding="utf-8") as f: + if json.load(f) != facts: + fails.append("efcore/bound: the extractor no longer emits the committed " + f"facts ({os.path.basename(EF_FIXTURE)})") + got = _verdict(facts) + if got != []: + fails.append(f"efcore/bound: verdict {got!r}, expected clean") + n += 1 + + # (b) the same project with its handlers leaning on IMPLICIT usings. It compiles — + # the SDK generates them into obj/ — and the scan, which does not read obj/, must + # say that it cannot bind the region rather than drop it. + stage = _stage_backend(tmp, "implicit") + handler = os.path.join(stage, "OrderBackend", "OrderHandlers.cs") + with open(handler, encoding="utf-8") as f: + stripped, removed = _EF_IMPLICIT.subn("", f.read()) + with open(handler, "w", encoding="utf-8", newline="\n") as f: + f.write(stripped) + project = os.path.join(stage, "OrderBackend", "OrderBackend.csproj") + built = _run(["dotnet", "build", project, "-nologo", "-v", "q"], cwd=stage) + out = os.path.join(tmp, "implicit.json") + done = _run(["dotnet", dll, project, "--flow-locals", "-o", out]) + if removed == 0: + fails.append("efcore/implicit: the handler file has no explicit usings to remove") + elif built.returncode != 0: + codes = sorted(set(re.findall(r"error (CS\d+)", built.stdout))) + fails.append(f"efcore/implicit: the staged backend does not build: {codes}") + elif done.returncode != 2 or os.path.exists(out) or "does not resolve" not in done.stderr: + fails.append(f"efcore/implicit: exit {done.returncode}, expected a refusal naming " + f"the unresolved type: {done.stderr.strip()[-300:]}") + n += 1 + + # (c) handlers staged INTO a copy of the project, which must still compile: + # `refused/` - the scan must refuse each (exit 2, the expected text); + # `accepted/` - the scan must accept each: a mention is not an operation; + # `known-gaps/` - the scan ACCEPTS each although the row changes state past every + # token: outside what the profile claims, pinned so that it is a + # stated limit and never a forgotten one. + staged: list[tuple[str, str, str]] = [] + for kind in _EF_STAGED: + with open(os.path.join(EF, kind, "expected.json"), encoding="utf-8") as f: + expected = json.load(f) + on_disk = sorted(x[:-7] for x in os.listdir(os.path.join(EF, kind)) + if x.endswith(".cs.txt")) + if on_disk != sorted(expected): + fails.append(f"efcore/{kind}: expected.json {sorted(expected)} != sources " + f"{on_disk}") + return n + staged += [(kind, case, expected[case]) for case in on_disk] + stage = _stage_backend(tmp, "staged") + for kind, case, _ in staged: + shutil.copyfile(os.path.join(EF, kind, f"{case}.cs.txt"), + os.path.join(stage, "OrderBackend", f"{case}.cs")) + project = os.path.join(stage, "OrderBackend", "OrderBackend.csproj") + built = _run(["dotnet", "build", project, "-nologo", "-v", "q"], cwd=stage) + if built.returncode != 0: + codes = sorted(set(re.findall(r"error (CS\d+)", built.stdout))) + fails.append(f"efcore/staged: the staged backend does not build: {codes}") + return n + for kind, case, want in staged: + # one staged handler per scan: the project's own sources plus this file + keep = os.path.join(stage, "OrderBackend", f"{case}.cs") + hidden = [os.path.join(stage, "OrderBackend", f"{other}.cs") + for _, other, _ in staged if other != case] + for path in hidden: + os.replace(path, path + ".off") + out = os.path.join(tmp, f"{case}.json") + done = _run(["dotnet", dll, project, "--flow-locals", "-o", out]) + for path in hidden: + os.replace(path + ".off", path) + where = f"efcore/{kind}/{case}" + if _EF_STAGED[kind] == "accept": + if done.returncode != 0: + fails.append(f"{where}: the scan does not accept it (exit " + f"{done.returncode}): {done.stderr.strip()[-300:]}") + else: + with open(out, encoding="utf-8") as f: + got = _verdict(json.load(f)) + if got != []: + fails.append(f"{where}: verdict {got!r}, expected clean") + elif done.returncode != 2: + fails.append(f"{where}: the extractor exited {done.returncode}, expected a " + f"refusal (2)") + elif os.path.exists(out): + fails.append(f"{where}: a facts file was written despite the refusal") + elif want not in done.stderr or os.path.basename(keep) not in done.stderr: + fails.append(f"{where}: refusal text lacks {want!r}: " + f"{done.stderr.strip()[-300:]}") + n += 1 + return n + + +def rust_cli(binary: str, fails: list[str]) -> int: + """The two PUBLIC engines over the frozen C#-derived facts: exit code, stdout and + stderr must be byte-identical.""" + n = 0 + for name in sorted(os.listdir(FIXDIR)): + if not (name.startswith(("typestate_cs_", "typestate_ef_")) + and name.endswith(".facts.json")): + continue + rel = f"tests/fixtures/lowered/{name}" + args = ["ownir", rel, "--format", "human", "--severity", "error"] + py = subprocess.run([sys.executable, "-m", "ownlang", *args], cwd=ROOT, + capture_output=True, check=False) + rs = subprocess.run([binary, *args], cwd=ROOT, capture_output=True, check=False) + if (py.returncode, py.stdout, py.stderr) != (rs.returncode, rs.stdout, rs.stderr): + fails.append(f"rust/{name}: the two engines differ (python rc={py.returncode}, " + f"rust rc={rs.returncode})") + n += 1 + return n + + +def main() -> int: + write = "--write" in sys.argv[1:] + rust = None + if "--rust" in sys.argv[1:]: + rust = sys.argv[sys.argv.index("--rust") + 1] + fails: list[str] = [] + if rust is not None and "--rust-only" in sys.argv[1:]: + n = rust_cli(rust, fails) + for f in fails: + print(f"FAIL: {f}") + print(f"protocol gate (rust CLI parity): {n} C#-derived documents, {len(fails)} failure(s)") + return 1 if fails else 0 + dll = _extractor_dll() + n_cases = cases(dll, write, fails) + n_refused = refused(dll, fails) + n_nocompile = does_not_compile(fails) + n_same = same_assembly(dll, fails) + n_ef = efcore(dll, write, fails) + n_rust = rust_cli(rust, fails) if rust is not None else 0 + for f in fails: + print(f"FAIL: {f}") + print(f"protocol gate: {n_cases} cases lowered from C# and judged, {n_refused} refused by " + f"the lowering, {n_nocompile} rejected by the C# compiler, {n_same} same-assembly " + f"programs held to the boundary, {n_ef} checks on the real " + f"ASP.NET Core + EF Core backend" + + (f", {n_rust} documents byte-identical on both CLIs" if rust else "") + + f"; {len(fails)} failure(s)" + (" [fixtures written]" if write else "")) + return 1 if fails else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 537b8722c00c0019836547e5b82caba709554155 Mon Sep 17 00:00:00 2001 From: PhysShell <45852143+PhysShell@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:35:39 +0500 Subject: [PATCH 3/7] fix(perf): the instrument's generated facts follow OwnIR v1 The `facts` calibration generator stamps `ownir_version` on every document it writes. Left on 0 against a v1 core it hands both engines a document they refuse at the door, so every `facts` rung would be a cell that times the version gate. One literal changes: "ownir_version": 0 -> 1. Nothing else in the harness source set moves. This is a change to an INSTRUMENT SOURCE, so it moves the harness identity on purpose: 104c384d01bf6060bdec1e7c916053ddb04b97fcbd0b39f8a4fc57b8f139672f before 1a26aa63fd5fbbe06e7ae72dd5e1c8f2d611bff8856af4a5b93ae36d2d23a9b1 after Steps 4, 5 and 6 are re-bound in the next commit, which is separate so that the re-acceptance can be reviewed as one. Between the two, four controls say so by name: freeze-harness-untouched, constants-bound-to-freeze, training-prereg-bindings, envcapture-frozen-untouched. No existing control could see the defect: with the core on v1 and the generator on v0, the whole instrument suite was green. `perf-calibration-facts-current` is new: it runs the reference on what the generator writes and requires the `facts` workloads to be analysed and the `refused` workload to be refused. It fails on the old literal (three workloads, exit 2, "facts are schema v0") and holds on the new one. It lives outside the harness source set and moves no identity. Co-Authored-By: Claude Fable 5.1 --- scripts/perf_baseline.py | 2 +- tests/test_perf_instrument.py | 51 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/scripts/perf_baseline.py b/scripts/perf_baseline.py index 0d112d81..944c6bc1 100644 --- a/scripts/perf_baseline.py +++ b/scripts/perf_baseline.py @@ -1272,7 +1272,7 @@ def materialize_calibration(w: Workload, tmp: Path) -> Path: if gen == "facts": scale = _as_int(w.spec.get("scale", 1), 1) doc = { - "ownir_version": 0, + "ownir_version": 1, "module": w.id, "components": [{"name": f"C{i}", "kind": "class", "file": f"gen/{i}.cs", "line": i + 1} for i in range(scale)], diff --git a/tests/test_perf_instrument.py b/tests/test_perf_instrument.py index 7679fd54..bb4d15f2 100644 --- a/tests/test_perf_instrument.py +++ b/tests/test_perf_instrument.py @@ -7,6 +7,7 @@ perf-firewall-decisive a decisive workload cannot reach a clock pre-D7 perf-firewall-calibration a calibration workload can (or the instrument is inert) + perf-calibration-facts-current the reference analyses what the generator writes perf-reference-boundary the Python reference reads nothing its identity misses perf-d7-phase-universe D7's metrics are not the attribution taxonomy perf-gate-dormant the C1/C2 gate exists, is unarmed, and fails closed @@ -1498,8 +1499,58 @@ def control_digest_platform_stable() -> None: "binary is still hashed byte for byte") +def control_calibration_facts_current() -> None: + """A generated facts workload is a document the reference ANALYSES. + + The generator stamps `ownir_version`, and the reference refuses any other + version at its door (spec/OwnIR.md §2). A stamp left behind by a vocabulary + bump turns every `facts` rung into the refusal path: the cells would still + be produced, on both engines, and would time the version gate. The + instrument has a workload for that path on purpose (`refused`), so the two + generators must land on OPPOSITE sides of the door. + + Observed by running the reference on what the generator wrote, not by + comparing two integers: the first OwnIR vocabulary bump (v0 -> v1) left the + generator on v0 with every other control in this suite green. + """ + import subprocess + problems = [] + seen = {"facts": 0, "refused": 0} + with tempfile.TemporaryDirectory(prefix="perf-facts-") as td: + tmp = Path(td) + workloads, _ = pb.load_manifest() + env = dict(os.environ) + env["PYTHONPATH"] = str(ROOT) + for w in workloads: + gen = str(w.spec.get("generator")) + if w.decisive or gen not in seen: + continue + seen[gen] += 1 + target = pb.materialize_calibration(w, tmp) + r = subprocess.run([sys.executable, "-m", "ownlang", "ownir", str(target)], + capture_output=True, env=env, cwd=str(ROOT), check=False) + said = r.stderr.decode("utf-8", "replace").strip()[:160] + if gen == "facts" and r.returncode != 0: + problems.append(f"{w.id}: the reference does not analyse the generated facts " + f"(exit {r.returncode}: {said}) — the rung would time a refusal") + if gen == "refused" and r.returncode != 2: + problems.append(f"{w.id}: the refusal workload is not refused (exit " + f"{r.returncode}), so nothing measures the refusal path") + missing = [g for g, n in seen.items() if n == 0] + if missing: + problems.append(f"the manifest has no {missing} calibration workload; this control " + "exercised nothing for it") + if problems: + fail("perf-calibration-facts-current", "; ".join(problems)) + else: + ok("perf-calibration-facts-current", + f"the reference analyses all {seen['facts']} generated facts workload(s) and " + f"refuses the {seen['refused']} built to be refused") + + def run() -> int: control_firewall() + control_calibration_facts_current() control_reference_boundary() control_d7_phase_universe() control_gate_dormant() From ab678eb52ffd5c7b51931f381258fe82447945f9 Mon Sep 17 00:00:00 2001 From: PhysShell <45852143+PhysShell@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:35:40 +0500 Subject: [PATCH 4/7] docs(calibration): re-bind steps 4/5/6 and record T0 Amendment 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The instrument's generated facts moved to OwnIR v1 in the previous commit, and with them `measurement_harness_digest` 104c384d01bf6060bdec1e7c916053ddb04b97fcbd0b39f8a4fc57b8f139672f before 1a26aa63fd5fbbe06e7ae72dd5e1c8f2d611bff8856af4a5b93ae36d2d23a9b1 after Re-bound, in the order the chain runs: the digest in the policy freeze, in the ratified design constants, and in the training preregistration's bindings — and with it `design_constants_blob_sha1` (fa02f43bdf79 -> 0ff316d5aea6), which moved because the design-constants artifact itself was re-bound. The same chain in the same order as the S8 re-binding (5e7215a6). The frozen T0 records this as Amendment 1, a new state of the contract: T0-1 names the new identity (the merge gate reads it there and recomputes it from the tree being merged), the status block points at the amendment, and the amendment says what moved and what did not. FROZEN and collection_authorized are unchanged. No rule, budget, population, statistic, roll-up or host predicate changes. The freeze commit is untouched and stays an ancestor. Not touched: the committed evidence of runs that actually happened. No T0-governed clock has run under any identity, so there is no training or decisive observation to re-evaluate. The gate's own files do not change in this merge (the co-change rule). Controls after re-binding: perf instrument 17/17, round 7 apparatus 10/10, calibration policy 10/10, calibration freeze 7/7, calibration constants 4/4, training preregistration 9/9, step 7 environment capture 12/12, host qualification 26/26, merge gate 15/15, merge gate wiring 12/12. Co-Authored-By: Claude Fable 5.1 --- .../p022-263a-design-constants.json | 2 +- .../calibration/p022-263a-policy-freeze.json | 2 +- .../p022-263a-training-preregistration.json | 4 +- docs/notes/p022-263-t0-protocol-freeze.md | 90 ++++++++++++++++++- docs/notes/p022-263a-instrument.md | 10 +++ pyproject.toml | 2 +- 6 files changed, 103 insertions(+), 7 deletions(-) diff --git a/docs/evidence/calibration/p022-263a-design-constants.json b/docs/evidence/calibration/p022-263a-design-constants.json index fa02f43b..0ff316d5 100644 --- a/docs/evidence/calibration/p022-263a-design-constants.json +++ b/docs/evidence/calibration/p022-263a-design-constants.json @@ -1,6 +1,6 @@ { "artifact": "p022-263a-calibration-design-constants", - "bound_measurement_harness_digest": "104c384d01bf6060bdec1e7c916053ddb04b97fcbd0b39f8a4fc57b8f139672f", + "bound_measurement_harness_digest": "1a26aa63fd5fbbe06e7ae72dd5e1c8f2d611bff8856af4a5b93ae36d2d23a9b1", "bound_policy_implementation_digest": "c3068ed7fa880a7083866ead25fe8bf65c87889d242d8af1f7eee01582cd5cbf", "constants": { "G": [ diff --git a/docs/evidence/calibration/p022-263a-policy-freeze.json b/docs/evidence/calibration/p022-263a-policy-freeze.json index e206cdf8..f3445962 100644 --- a/docs/evidence/calibration/p022-263a-policy-freeze.json +++ b/docs/evidence/calibration/p022-263a-policy-freeze.json @@ -1,6 +1,6 @@ { "artifact": "p022-263a-calibration-policy-freeze", - "measurement_harness_digest": "104c384d01bf6060bdec1e7c916053ddb04b97fcbd0b39f8a4fc57b8f139672f", + "measurement_harness_digest": "1a26aa63fd5fbbe06e7ae72dd5e1c8f2d611bff8856af4a5b93ae36d2d23a9b1", "policy_implementation_digest": "c3068ed7fa880a7083866ead25fe8bf65c87889d242d8af1f7eee01582cd5cbf", "policy_implementation_digest_framing": "sha256 over the source set ordered by the UTF-8 bytes of each repo-relative POSIX path. Each file contributes, with no header and no separator: its path byte length as an 8-byte big-endian unsigned integer, its path's exact UTF-8 bytes, its blob byte length as an 8-byte big-endian unsigned integer, and its exact git blob bytes.", "policy_source_commit": "b4f657a0abdfdfaae199cbc7eee0c47acd8b0057", diff --git a/docs/evidence/calibration/p022-263a-training-preregistration.json b/docs/evidence/calibration/p022-263a-training-preregistration.json index a03e85a5..c2b23f61 100644 --- a/docs/evidence/calibration/p022-263a-training-preregistration.json +++ b/docs/evidence/calibration/p022-263a-training-preregistration.json @@ -30,8 +30,8 @@ "anchor_commit": "eedf6d3ed44ecf7bda69fa509dcd23b45960cf76", "artifact": "p022-263a-calibration-training-preregistration", "bindings": { - "design_constants_blob_sha1": "fa02f43bdf795e2f47d8ce781ec3a84f1dee64b5", - "measurement_harness_digest": "104c384d01bf6060bdec1e7c916053ddb04b97fcbd0b39f8a4fc57b8f139672f", + "design_constants_blob_sha1": "0ff316d5aea6af92c01679babaf0f0251191dcee", + "measurement_harness_digest": "1a26aa63fd5fbbe06e7ae72dd5e1c8f2d611bff8856af4a5b93ae36d2d23a9b1", "policy_implementation_digest": "c3068ed7fa880a7083866ead25fe8bf65c87889d242d8af1f7eee01582cd5cbf", "training_scope_implementation_digest": "614bf9efe6ba2bb10e26a251c10d6c4a8fdaa99985d43ea03d76c6774447d25b", "training_scope_root": "scripts/training/" diff --git a/docs/notes/p022-263-t0-protocol-freeze.md b/docs/notes/p022-263-t0-protocol-freeze.md index 174c1ac6..9b79fbe8 100644 --- a/docs/notes/p022-263-t0-protocol-freeze.md +++ b/docs/notes/p022-263-t0-protocol-freeze.md @@ -28,6 +28,12 @@ Status: Amendments after this line are a new state of the contract, never an edit of this one. This commit is not to be amended, rebased or force-pushed. + + AMENDMENT 1 (2026-10-01) is such a state — see "Amendments" at the end: + the accepted harness identity moved 104c384d01bf -> 1a26aa63fd5f when the + instrument's generated facts moved to OwnIR v1, and steps 4, 5 and 6 were + re-bound to it. The freeze and the authorisation above are unchanged, and so + is every rule, budget, population and predicate below. ``` **What this is.** The contract that fixes *how numbers will be judged*, before any @@ -164,9 +170,13 @@ Doing so edits `scripts/perf_baseline.py`, which is one of the two files in the harness source set, so it moves the harness identity measurement_harness_digest - 104c384d01bf6060bdec1e7c916053ddb04b97fcbd0b39f8a4fc57b8f139672f + 1a26aa63fd5fbbe06e7ae72dd5e1c8f2d611bff8856af4a5b93ae36d2d23a9b1 -to which steps 4, 5 and 6 were **re-bound** after the S8 memory-semantics repair. +to which steps 4, 5 and 6 are bound. The identity has moved twice, each time by +a recorded act and never as a side effect: steps 4, 5 and 6 were **re-bound** +after the S8 memory-semantics repair (`562a7f7232da` → `104c384d01bf`), and +re-bound again by Amendment 1, when the instrument's generated facts moved to +OwnIR v1 (`104c384d01bf` → `1a26aa63fd5f`). Moving it again is a new instrument and a re-evaluation of accepted instrumentation evidence, not a T0 detail. @@ -1240,3 +1250,79 @@ outcome. T0_CONTENT_COMPLETE_READY_FOR_FREEZE_REVIEW status: NOT_FROZEN — collection_authorized: false + +--- + +## Amendments + +Each entry is a **new state of the contract**. The state it replaces is not +edited: the freeze commit stays an ancestor of every later state, and what it +said is what it said. An amendment names exactly what moved and what did not. + +### Amendment 1 — the harness identity follows OwnIR v1 (2026-10-01) + +```text +Status after this amendment: + FROZEN, collection_authorized: true unchanged + accepted harness identity 104c384d01bf -> 1a26aa63fd5f + rules, budgets, populations, statistic, + roll-ups, host predicate, retry budget unchanged, every one of them +``` + +**The owner's ruling.** OwnIR's flow vocabulary gained `move` and the compound +`borrow_mut`, which is an incompatible vocabulary change, so `OWNIR_VERSION` +moved 0 → 1 ([OwnIR.md §2, §5.3](../../spec/OwnIR.md)). The instrument generates +its `facts` calibration workloads itself and stamps the version on each, so an +instrument left on v0 writes documents both engines refuse at the door: every +`facts` rung would be a cell that times the version gate. The owner ruled that +the instrument is repaired and officially re-bound on the S8 precedent, and +ruled out a compatibility shim on either side — a v1 core that also accepts v0, +or an instrument arranged so that the re-binding could be skipped. + +**What changed in the instrument.** One literal in the `facts` generator of +`scripts/perf_baseline.py`: `"ownir_version": 0` became `"ownir_version": 1`. +Nothing else in the harness source set moved: the workload manifest, the rung +ladder, the clock, the memory accounting, the identity gate and +`HARNESS_VERSION` are byte-for-byte what they were. + + harness identity before 104c384d01bf6060bdec1e7c916053ddb04b97fcbd0b39f8a4fc57b8f139672f + harness identity after 1a26aa63fd5fbbe06e7ae72dd5e1c8f2d611bff8856af4a5b93ae36d2d23a9b1 + +**What was re-bound**, in the order the chain runs: the policy freeze (step 4), +the ratified design constants (step 5), and the training preregistration's +bindings (step 6) — and with the last, `design_constants_blob_sha1` +(`fa02f43bdf79` → `0ff316d5aea6`), which moved because the design-constants +artifact itself was re-bound. T0-1 above names the new identity; the merge gate +reads it there and recomputes it from the instrument sources of the tree being +merged. + +**What was not touched.** The committed evidence of runs that actually +happened. The sizing, calibration and Round-7 datasets were produced by earlier +instruments and record what those produced; rewriting them would be forging a +record, not re-accepting a binding. No T0-governed clock has run under any +identity, so there is no training or decisive observation for this amendment to +re-evaluate, keep or discard. + +**The re-evaluation T0-1 says a moved identity owes.** The instrumentation +controls were re-run on the amended tree, and all hold: perf instrument 17/17, +Round 7 apparatus 10/10, calibration policy 10/10, calibration freeze 7/7, +calibration constants 4/4, training preregistration 9/9, step 7 environment +capture 12/12, host qualification 26/26, merge gate 15/15, merge gate wiring +12/12. Four of them named the move before the re-binding, each for its own +reason — `freeze-harness-untouched`, `constants-bound-to-freeze`, +`training-prereg-bindings`, `envcapture-frozen-untouched` — which is the +provenance system delivering its inconvenience on purpose. + +One control is new, because none of the existing ones could see the defect that +made this amendment necessary: with the core on v1 and the generator still on +v0, every control was green. `perf-calibration-facts-current` runs the reference +on what the generator writes and requires the `facts` workloads to be analysed +and the `refused` workload to be refused — the two generators on opposite sides +of the door. It lives in the control suite, outside the harness source set, so +adding it moves no identity. + +**What this amendment does not do.** It authorises nothing new: host +qualification, an exact execution binding, the campaign link and the session +preflight are still required, and each may still refuse. It changes no number a +verdict is computed from. And it is not a precedent for moving the identity as a +side effect: the next move is a new amendment or it is a defect. diff --git a/docs/notes/p022-263a-instrument.md b/docs/notes/p022-263a-instrument.md index bb066805..627dd2fd 100644 --- a/docs/notes/p022-263a-instrument.md +++ b/docs/notes/p022-263a-instrument.md @@ -266,6 +266,16 @@ commit; the committed evidence of runs that already happened was **not** rewritt and still carries the old field name, because it records what the old instrument produced. +The identity moved a second time, from `104c384d01bf` to `1a26aa63fd5f`, when +OwnIR's vocabulary moved to v1: the `facts` generator stamps `ownir_version` on +every document it writes, and a generator left on v0 would have handed both +engines a document they refuse at the door. One literal changed, steps 4, 5 and 6 +were re-bound again, and the frozen T0 records it as its Amendment 1 +([the T0 contract](p022-263-t0-protocol-freeze.md)). The control that would have +caught it is `perf-calibration-facts-current`: it runs the reference on the +generator's output and requires the `facts` workloads to be analysed and the +`refused` one to be refused. + On POSIX the same `wait4` call also carries the child's CPU split, fault counts and context-switch counts. Those are now kept rather than discarded — see *The interval kept its meaning and gave up its secrets* — under the same rule: off diff --git a/pyproject.toml b/pyproject.toml index d3e84c89..ae06d25c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ select = ["E", "W", "F", "I", "B", "UP", "C4", "RUF"] [tool.ruff.lint.per-file-ignores] # scripts/perf_baseline.py is an INSTRUMENT SOURCE: its bytes are hashed into # the measurement harness digest that the frozen #263 preregistration names -# (104c384d01bf), and steps 4, 5 and 6 are bound to that same value. Rewrapping +# (1a26aa63fd5f), and steps 4, 5 and 6 are bound to that same value. Rewrapping # a line there is not a cosmetic edit — it moves the digest and makes the frozen # contract point at an identity the tree no longer contains, which is exactly # what scripts/step7/mergegate.py exists to refuse. So the one over-long line From 78c041a1fc8f64d9e8651e68d172047cf811d640 Mon Sep 17 00:00:00 2001 From: PhysShell <45852143+PhysShell@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:55:08 +0500 Subject: [PATCH 5/7] fix(roslyn): a region opened inside the protocol's own types is refused, not skipped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The types that declare a protocol are trusted: their bodies implement it and are not lowered. The lowering skipped every method of such a type — including one that USES the protocol. A handler written in the same class as the region entry it calls was therefore never analysed, and read as clean: public static class ParcelHandlers { [ProtocolRegion] public static void WithPacked(...) => ...; public static void Dispatch(Parcel parcel) { WithPacked(parcel, packed => { packed.Send(); packed.Send(); }); } } Before: exit 0, no function emitted, verdict clean — a token spent twice. After: exit 2. Trusted is not the same as checked, and an unanalysed region must not read as a clean one. The remedy is in the message: use the protocol from outside the types that declare it. Found while probing what an unrelated attribute that merely shares the name [ProtocolRegion] does to a scan. refused/R15 pins it; no committed fact moves (18 cases, 15 refused, 0 failures), and the extractor's output on every C# sample CI scans is still equal to main's, 122 of 122. Co-Authored-By: Claude Fable 5.1 --- .../OwnSharp.Extractor/ProtocolLowering.cs | 12 +++++- frontend/roslyn/README.md | 4 ++ ..._region_opened_inside_protocol_type.cs.txt | 41 +++++++++++++++++++ .../protocol-samples/refused/expected.json | 1 + 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 frontend/roslyn/protocol-samples/refused/R15_region_opened_inside_protocol_type.cs.txt diff --git a/frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs b/frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs index 4a5fa1d3..7f922296 100644 --- a/frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs +++ b/frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs @@ -117,7 +117,17 @@ public static Result Lower(Compilation compilation, if (model.GetDeclaredSymbol(method) is not IMethodSymbol msym) continue; if (IsApiType(msym.ContainingType)) - continue; // the protocol API itself: its tokens' own bodies are not user code + { + // The protocol's own types are TRUSTED: their bodies implement the + // protocol and are not lowered. So a region OPENED in one of them — a + // handler written next to the region entry it calls — would never be + // analysed and would read as clean. Trusted is not the same as checked. + foreach (var opened in method.DescendantNodes().OfType() + .Where(i => IsRegionEntry(MethodOf(model, i)))) + refusals.Add(At(file, opened, + $"a protocol region is opened inside '{msym.ContainingType.Name}', one of the protocol's own types: code there implements the protocol and is not analysed, so this region would read as clean. Use the protocol from outside the types that declare it")); + continue; + } try { foreach (var p in msym.Parameters) diff --git a/frontend/roslyn/README.md b/frontend/roslyn/README.md index 1c56acb0..db44054f 100644 --- a/frontend/roslyn/README.md +++ b/frontend/roslyn/README.md @@ -135,6 +135,10 @@ not skipped: the extractor exits `2` and writes no facts, for the whole scan. `nameof`, `typeof` and reads are mentions, not operations. - *Source.* The protocol and its entity must be in the scan as source; one that arrives only as a compiled reference cannot be admitted. +- *Trusted types.* The types that declare a protocol (the tokens, the class + holding a region entry) implement it and are not analysed as user code. A + region opened inside one of them is refused rather than skipped: write the + code that uses a protocol outside the types that declare it. - *Binding.* The scan does not read `obj/`, so usings a project only gets **implicitly** are not there. A region whose entity does not bind is refused; write the usings out in the files that open regions (or qualify the names). diff --git a/frontend/roslyn/protocol-samples/refused/R15_region_opened_inside_protocol_type.cs.txt b/frontend/roslyn/protocol-samples/refused/R15_region_opened_inside_protocol_type.cs.txt new file mode 100644 index 00000000..1fd6885c --- /dev/null +++ b/frontend/roslyn/protocol-samples/refused/R15_region_opened_inside_protocol_type.cs.txt @@ -0,0 +1,41 @@ +using System; +using Own.Protocols.Sample; + +namespace Own.Protocols.Cases; + +// The types that DECLARE a protocol are trusted: their bodies are the protocol, and the +// lowering does not read them as user code. A handler written in the same class as the +// region entry it calls would therefore never be analysed — and this one spends a token +// twice. It is refused, not skipped: an unanalysed region must not read as a clean one. +public sealed class Parcel +{ + public bool Sent { get; private set; } + + internal void MarkSent() => Sent = true; +} + +[ProtocolToken] +public readonly ref struct PackedParcel +{ + private readonly Parcel _parcel; + internal PackedParcel(Parcel parcel) => _parcel = parcel; + + public void Send() => _parcel.MarkSent(); +} + +public delegate void PackedParcelRegion(PackedParcel packed); + +public static class ParcelHandlers +{ + [ProtocolRegion] + public static void WithPacked(Parcel parcel, PackedParcelRegion body) => body(new PackedParcel(parcel)); + + public static void Dispatch(Parcel parcel) + { + WithPacked(parcel, packed => + { + packed.Send(); + packed.Send(); // a stale token: OWN002 anywhere else + }); + } +} diff --git a/frontend/roslyn/protocol-samples/refused/expected.json b/frontend/roslyn/protocol-samples/refused/expected.json index 87ce6617..06d8a698 100644 --- a/frontend/roslyn/protocol-samples/refused/expected.json +++ b/frontend/roslyn/protocol-samples/refused/expected.json @@ -4,6 +4,7 @@ "R12_default_token_temporary": "a protocol token 'ApprovedOrder' is created outside the protocol's own types", "R13_public_mutator_reaches_protocol_state": "'Doc.Publish' is public and writes 'Published', state the protocol's transitions own", "R14_public_setter_of_protocol_state": "'Ticket.State' is public and writes 'State', state the protocol's transitions own", + "R15_region_opened_inside_protocol_type": "a protocol region is opened inside 'ParcelHandlers', one of the protocol's own types", "R1_default_token": "has no lowerable origin", "R2_token_outside_region": "is declared outside a protocol region", "R3_return_inside_region": "`return` inside a protocol region", From 98c062e56e818b6f48031a1e085dc4e3fc5745ce Mon Sep 17 00:00:00 2001 From: PhysShell <45852143+PhysShell@users.noreply.github.com> Date: Thu, 1 Oct 2026 22:18:58 +0500 Subject: [PATCH 6/7] docs(roslyn): the trust boundary is part of the profile's soundness model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit refused a region opened inside a type that declares the protocol, and documented it as one more thing the extractor refuses. That undersells what it is. The declaring types are the profile's trusted definition surface — token construction, region entry, transition plumbing — and everything else is the analysed surface. A type is one or the other, never both, because consumer code inside a declaring type is not a violation the core missed: it is a program the core never saw. The README states that as the model, next to what the profile claims, and the refusal list points at it. The lowering's header says the same. No behaviour changes: comments and prose only. Co-Authored-By: Claude Fable 5.1 --- .../OwnSharp.Extractor/ProtocolLowering.cs | 16 ++++++++++++---- frontend/roslyn/README.md | 17 +++++++++++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs b/frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs index 7f922296..35cedcc9 100644 --- a/frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs +++ b/frontend/roslyn/OwnSharp.Extractor/ProtocolLowering.cs @@ -30,6 +30,13 @@ // update, a change-tracker metadata write, raw SQL, another process. Those belong to // concurrency tokens, constraints and transactions. // +// THE TRUST BOUNDARY. The types that DECLARE a protocol (the tokens, the type holding a +// region entry) are the trusted definition surface: token construction, region entry and +// transition plumbing live there, and their bodies are not lowered. Everything else is +// consumer code — the analysed surface. A type is one or the other, never both: consumer code +// inside a declaring type would simply be absent from the analysed program, so a region +// opened there is refused rather than guessed about. +// // OUTSIDE a region nothing protocol-relevant is live, and a region lowers to a self-contained // unit (the entity is acquired and released around it). So whatever surrounds a region — // `try`/`catch`, `using`, a loop, a branch, `await` — is walked through and ignored. @@ -118,10 +125,11 @@ public static Result Lower(Compilation compilation, continue; if (IsApiType(msym.ContainingType)) { - // The protocol's own types are TRUSTED: their bodies implement the - // protocol and are not lowered. So a region OPENED in one of them — a - // handler written next to the region entry it calls — would never be - // analysed and would read as clean. Trusted is not the same as checked. + // The trust boundary (see the header): a declaring type's bodies + // implement the protocol and are not lowered. So a region OPENED in one + // of them — a handler written next to the region entry it calls — would + // not be a violation the core missed but a program it never saw, and + // would read as clean. Trusted is not the same as checked. foreach (var opened in method.DescendantNodes().OfType() .Where(i => IsRegionEntry(MethodOf(model, i)))) refusals.Add(At(file, opened, diff --git a/frontend/roslyn/README.md b/frontend/roslyn/README.md index db44054f..cd7f340f 100644 --- a/frontend/roslyn/README.md +++ b/frontend/roslyn/README.md @@ -118,6 +118,16 @@ SQL, another process. Those belong to concurrency tokens, constraints and transactions (two of them are pinned as stated limits under `protocol-samples/efcore/known-gaps`). +**The trust boundary.** The types that DECLARE a protocol — the tokens, and the +type holding a region entry — are its trusted definition surface: they construct +tokens, enter regions and implement transitions, and their bodies are not +lowered. Everything else is consumer code, and consumer code is what the profile +analyses. A type is one or the other, never both. A handler written inside a +declaring type would not be a violation the core missed; it would be a program +the core never saw, and it would read as clean. So a region opened inside a +declaring type is refused, and the profile does not try to guess which method of +such a type is plumbing and which is a business handler. + **Refusals.** A recognised protocol construct that cannot be lowered safely is not skipped: the extractor exits `2` and writes no facts, for the whole scan. @@ -135,10 +145,9 @@ not skipped: the extractor exits `2` and writes no facts, for the whole scan. `nameof`, `typeof` and reads are mentions, not operations. - *Source.* The protocol and its entity must be in the scan as source; one that arrives only as a compiled reference cannot be admitted. -- *Trusted types.* The types that declare a protocol (the tokens, the class - holding a region entry) implement it and are not analysed as user code. A - region opened inside one of them is refused rather than skipped: write the - code that uses a protocol outside the types that declare it. +- *Trust boundary.* A region opened inside a type that declares the protocol + (see above): write the code that uses a protocol outside the types that + declare it. - *Binding.* The scan does not read `obj/`, so usings a project only gets **implicitly** are not there. A region whose entity does not bind is refused; write the usings out in the files that open regions (or qualify the names). From f5052530e71db508271946e98501c5f85fbb5030 Mon Sep 17 00:00:00 2001 From: PhysShell <45852143+PhysShell@users.noreply.github.com> Date: Thu, 1 Oct 2026 23:36:50 +0500 Subject: [PATCH 7/7] fix(tests): the S0 control checks that the flag does not move the version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `tests/check_fix_candidates_facts.py` asserted `ownir_version == 0` under the message "ownir_version must stay 0". What it meant is that `--fix-candidates` is additive metadata and does not move the vocabulary version; what it checked was a literal, which held only until the vocabulary first moved. OwnIR v1 moved it, and the `C# leak extractor (Roslyn) -> OwnIR -> core` job went red on that line — with the extractor doing exactly the right thing. It now checks the claim: the flag-on facts are stamped with the core's current OWNIR_VERSION, and with the same value as the flag-off facts. This is the third control of this shape the bump has found (the validation ledger in #383, the instrument's generated facts in this branch), and the only one that needed the server to find it: the script takes the extractor's output as its argument, so it is not part of `tests/run_tests.py` and no local run of that suite could reach it. The job's steps were then run locally as written, from this one to the end of the job, on a Linux clone of the committed tree. tests/goldens/README.md records the golden's second amendment: one line, `"ownir_version": 0` -> `1`, made in the vocabulary commit. Co-Authored-By: Claude Fable 5.1 --- tests/check_fix_candidates_facts.py | 18 ++++++++++++++++-- tests/goldens/README.md | 13 +++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/tests/check_fix_candidates_facts.py b/tests/check_fix_candidates_facts.py index 82a81d58..9e5fac72 100644 --- a/tests/check_fix_candidates_facts.py +++ b/tests/check_fix_candidates_facts.py @@ -15,8 +15,13 @@ import copy import json +import os import sys +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) + +from ownlang.ownir import OWNIR_VERSION + _ADDITIVE_COMPONENT_KEYS = ( "qualified_name", "is_partial", @@ -65,9 +70,15 @@ def check(cond: bool, msg: str) -> None: if not cond: fails.append(msg) - # Top-level: additive version present, ownir_version untouched. + # Top-level: additive version present, ownir_version untouched. "Untouched" is a + # claim about the FLAG, not about a number: `--fix-candidates` is additive metadata, + # so it must stamp the vocabulary version the core currently understands — the same + # one a flag-off run stamps (compared below, where the flag-off facts are in hand). + # This used to read `== 0`, which held only until the vocabulary first moved. check(on.get("fix_candidates_version") == 1, "top-level fix_candidates_version must be 1") - check(on.get("ownir_version") == 0, "ownir_version must stay 0") + check(on.get("ownir_version") == OWNIR_VERSION, + f"--fix-candidates must not move ownir_version: the facts are stamped " + f"{on.get('ownir_version')!r}, the core understands {OWNIR_VERSION}") def only_fix(name: str) -> dict | None: fx = _fixes(on, name) @@ -226,6 +237,9 @@ def only_fix(name: str) -> dict | None: if off_path is not None: off = _load(off_path) check("fix_candidates_version" not in off, "flag-off: no fix_candidates_version") + check(on.get("ownir_version") == off.get("ownir_version"), + f"the flag moved ownir_version: {off.get('ownir_version')!r} without it, " + f"{on.get('ownir_version')!r} with it") for c in off.get("components", []): # type: ignore[union-attr] check("qualified_name" not in c, f"flag-off: {c.get('name')} has qualified_name") for s in c.get("subscriptions") or []: diff --git a/tests/goldens/README.md b/tests/goldens/README.md index 43595b1f..e68c4045 100644 --- a/tests/goldens/README.md +++ b/tests/goldens/README.md @@ -31,6 +31,19 @@ The remaining 11 followed the same derivation, and run 30443181114 (job values are what the extractor actually emits, not just the dozen that had been read off a log. The file is a measured artifact again. +**Amended a second time, by OwnIR v1.** The flow vocabulary gained `move` and +`borrow_mut`, `OWNIR_VERSION` moved 0 → 1, and every producer stamps it. One line +of this file changed — `"ownir_version": 0` became `"ownir_version": 1` — and +nothing else: no record, no key, no order. That is again the "intentional +alteration by an unrelated extractor change" case, and again the file is the +`ff21d4a` output plus documented deltas rather than a re-recording. + +The same bump found a control that had confused the two things this gate keeps +apart. `tests/check_fix_candidates_facts.py` asserted `ownir_version == 0` under +the message "must stay 0", meaning "the flag does not move the version" but +checking a literal. It now checks what it meant: the flag-on facts carry the +core's current `OWNIR_VERSION`, and the same one as the flag-off facts. + ### Regenerating (only when an unrelated extractor change intentionally alters this sample's facts) ```bash