Skip to content
This repository was archived by the owner on Apr 25, 2024. It is now read-only.

Commit 14c565a

Browse files
ehildenbrv-auditor
andauthored
Fully qualify claim labels/dependencies, include dependencies in output (#842)
We were having trouble in runtimeverification/evm-semantics#2272 selecting the correct claims because they can be unqualified by the user. This PR: - Fully qualifies all claim labels and dependency labels returned by `KProve.get_claim_modules`. - Makes sure that we return not only the user-selected claims, but their dependencies (unless the user turns that off), from `KProve.get_claims`. - Adds a test-suite for the claim selection functionality. There is a bug in this code, and a test added (and skipped using `pytest.skip()`) which demonstrates a (probably minor) issue where we are not checking that claim dependencies are in a module imported by the current module. --------- Co-authored-by: devops <[email protected]>
1 parent 5fae467 commit 14c565a

File tree

6 files changed

+184
-11
lines changed

6 files changed

+184
-11
lines changed

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
project = 'pyk'
1010
author = 'Runtime Verification, Inc'
1111
copyright = '2024, Runtime Verification, Inc'
12-
version = '0.1.605'
13-
release = '0.1.605'
12+
version = '0.1.606'
13+
release = '0.1.606'
1414

1515
# -- General configuration ---------------------------------------------------
1616
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

package/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.605
1+
0.1.606

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
44

55
[tool.poetry]
66
name = "pyk"
7-
version = "0.1.605"
7+
version = "0.1.606"
88
description = ""
99
authors = [
1010
"Runtime Verification, Inc. <[email protected]>",

src/pyk/ktool/kprove.py

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ def get_claims(
347347
md_selector: str | None = None,
348348
claim_labels: Iterable[str] | None = None,
349349
exclude_claim_labels: Iterable[str] | None = None,
350+
include_dependencies: bool = True,
350351
) -> list[KClaim]:
351352
flat_module_list = self.get_claim_modules(
352353
spec_file=spec_file,
@@ -355,16 +356,51 @@ def get_claims(
355356
md_selector=md_selector,
356357
)
357358

358-
all_claims = {c.label: c for m in flat_module_list.modules for c in m.claims}
359-
unfound_labels = []
360-
claim_labels = list(all_claims.keys()) if claim_labels is None else claim_labels
361-
exclude_claim_labels = [] if exclude_claim_labels is None else exclude_claim_labels
362-
unfound_labels.extend([cl for cl in claim_labels if cl not in all_claims])
363-
unfound_labels.extend([cl for cl in exclude_claim_labels if cl not in all_claims])
359+
_module_names = [module.name for module in flat_module_list.modules]
360+
361+
def _get_claim_module(_label: str) -> str | None:
362+
if _label.find('.') > 0 and _label.split('.')[0] in _module_names:
363+
return _label.split('.')[0]
364+
return None
365+
366+
all_claims = {
367+
claim.label: (claim, _get_claim_module(claim.label))
368+
for module in flat_module_list.modules
369+
for claim in module.claims
370+
if _get_claim_module(claim.label) is not None
371+
}
372+
373+
claim_labels = list(all_claims.keys()) if claim_labels is None else list(claim_labels)
374+
exclude_claim_labels = [] if exclude_claim_labels is None else list(exclude_claim_labels)
375+
376+
final_claims: dict[str, KClaim] = {}
377+
unfound_labels: list[str] = []
378+
while len(claim_labels) > 0:
379+
claim_label = claim_labels.pop(0)
380+
if claim_label in final_claims or claim_label in exclude_claim_labels:
381+
continue
382+
if claim_label not in all_claims:
383+
claim_label = f'{flat_module_list.main_module}.{claim_label}'
384+
if claim_label not in all_claims:
385+
unfound_labels.append(claim_label)
386+
continue
387+
388+
_claim, _module_name = all_claims[claim_label]
389+
_updated_dependencies: list[str] = []
390+
for _dependency_label in _claim.dependencies:
391+
if _get_claim_module(_dependency_label) is None:
392+
_dependency_label = f'{_module_name}.{_dependency_label}'
393+
_updated_dependencies.append(_dependency_label)
394+
if len(_updated_dependencies) > 0:
395+
claim_labels.extend(_updated_dependencies)
396+
_claim = _claim.let(att=_claim.att.update({'depends': ','.join(_updated_dependencies)}))
397+
398+
final_claims[claim_label] = _claim
399+
364400
if len(unfound_labels) > 0:
365401
raise ValueError(f'Claim labels not found: {unfound_labels}')
366402

367-
return [all_claims[cl] for cl in all_claims if cl in claim_labels and cl not in exclude_claim_labels]
403+
return list(final_claims.values())
368404

369405
@contextmanager
370406
def _tmp_claim_definition(
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
module MULTI-CLAIM
2+
configuration <k> $PGM:Pgm </k>
3+
4+
syntax Pgm ::= "a" | "b" | "c"
5+
endmodule
6+
7+
module MULTI-CLAIM-SPEC-DEPENDENCY-1
8+
imports MULTI-CLAIM
9+
10+
claim [dep-1.1]: <k> a => b </k>
11+
endmodule
12+
13+
module MULTI-CLAIM-SPEC-DEPENDENCY-2
14+
imports MULTI-CLAIM
15+
16+
claim [dep-2.1]: <k> b => c </k>
17+
18+
claim [dep-2.2]: <k> b => c </k> [depends(MULTI-CLAIM-SPEC-DEPENDENCY-1.dep-1.1)]
19+
20+
claim [rep-dep]: <k> c => b </k>
21+
endmodule
22+
23+
module MULTI-CLAIM-SPEC
24+
imports MULTI-CLAIM-SPEC-DEPENDENCY-1
25+
imports MULTI-CLAIM-SPEC-DEPENDENCY-2
26+
27+
claim <k> a => b </k>
28+
29+
claim [dep]: <k> c => a </k>
30+
31+
claim [main.1]: <k> a => c </k> [depends(dep,MULTI-CLAIM-SPEC-DEPENDENCY-1.dep-1.1)]
32+
33+
claim [main.2]: <k> a => c </k> [depends(dep,MULTI-CLAIM-SPEC-DEPENDENCY-1.dep-1.2)]
34+
35+
claim [main.3]: <k> a => c </k> [depends(MULTI-CLAIM-SPEC.dep)]
36+
37+
claim [main.4]: <k> a => c </k> [depends(dep-1.2)]
38+
39+
claim [rep-dep]: <k> c => b </k>
40+
endmodule
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
import pytest
6+
7+
from pyk.testing import KProveTest
8+
9+
from ..utils import K_FILES
10+
11+
if TYPE_CHECKING:
12+
from collections.abc import Iterable
13+
14+
from pyk.ktool.kprove import KProve
15+
16+
17+
GET_CLAIMS_SPEC_DATA_SKIP: Iterable[str] = ('simple-dep-in-submodule-2',)
18+
19+
20+
GET_CLAIMS_SPEC_DATA: Iterable[tuple[str, list[str], dict[str, list[str]] | None]] = (
21+
(
22+
'simple-dep-in-submodule-fail',
23+
['dep-1.1'],
24+
None,
25+
),
26+
(
27+
'simple-dep-in-submodule-pass',
28+
['MULTI-CLAIM-SPEC-DEPENDENCY-1.dep-1.1'],
29+
{'MULTI-CLAIM-SPEC-DEPENDENCY-1.dep-1.1': []},
30+
),
31+
(
32+
'simple-dep-in-submodule-2',
33+
['MULTI-CLAIM-SPEC-DEPENDENCY-2.dep-2.2'],
34+
None,
35+
),
36+
(
37+
'no-dep-name-unqualified',
38+
['dep'],
39+
{'MULTI-CLAIM-SPEC.dep': []},
40+
),
41+
(
42+
'no-dep-name-qualified',
43+
['MULTI-CLAIM-SPEC.dep'],
44+
{'MULTI-CLAIM-SPEC.dep': []},
45+
),
46+
(
47+
'two-deps-one-nested',
48+
['MULTI-CLAIM-SPEC.main.1'],
49+
{
50+
'MULTI-CLAIM-SPEC.main.1': ['MULTI-CLAIM-SPEC.dep', 'MULTI-CLAIM-SPEC-DEPENDENCY-1.dep-1.1'],
51+
'MULTI-CLAIM-SPEC.dep': [],
52+
'MULTI-CLAIM-SPEC-DEPENDENCY-1.dep-1.1': [],
53+
},
54+
),
55+
(
56+
'bad-unqualified-main-dep',
57+
['MULTI-CLAIM-SPEC.main.4'],
58+
None,
59+
),
60+
(
61+
'rep-dep-in-submodule',
62+
['rep-dep'],
63+
{'MULTI-CLAIM-SPEC.rep-dep': []},
64+
),
65+
)
66+
67+
68+
class TestGetClaims(KProveTest):
69+
KOMPILE_MAIN_FILE = K_FILES / 'multi-claim-spec.k'
70+
KOMPILE_ARGS = {'main_module': 'MULTI-CLAIM'}
71+
SPEC_MODULE_NAME = 'MULTI-CLAIM-SPEC'
72+
73+
@pytest.mark.parametrize(
74+
'test_id,include_labels,expected_graph',
75+
GET_CLAIMS_SPEC_DATA,
76+
ids=[test_id for test_id, *_ in GET_CLAIMS_SPEC_DATA],
77+
)
78+
def test_get_claims(
79+
self, kprove: KProve, test_id: str, include_labels: list[str], expected_graph: dict[str, list[str]] | None
80+
) -> None:
81+
if test_id in GET_CLAIMS_SPEC_DATA_SKIP:
82+
pytest.skip()
83+
if expected_graph is None:
84+
with pytest.raises(ValueError):
85+
actual_claims = kprove.get_claims(
86+
self.KOMPILE_MAIN_FILE, self.SPEC_MODULE_NAME, claim_labels=include_labels
87+
)
88+
89+
else:
90+
actual_claims = kprove.get_claims(
91+
self.KOMPILE_MAIN_FILE, self.SPEC_MODULE_NAME, claim_labels=include_labels
92+
)
93+
actual_graph: dict[str, list[str]] = {claim.label: claim.dependencies for claim in actual_claims}
94+
95+
assert set(expected_graph.keys()) == set(actual_graph.keys())
96+
for claim_label, deps in expected_graph.items():
97+
assert set(deps) == set(actual_graph[claim_label])

0 commit comments

Comments
 (0)