Skip to content

Commit 5ccf5a8

Browse files
committed
PROOF: add return values to output parameter results
1 parent 366a572 commit 5ccf5a8

File tree

8 files changed

+327
-38
lines changed

8 files changed

+327
-38
lines changed

chc/app/CContextDictionary.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ def get_program_context(self, ix: int) -> ProgramContext:
8888
def get_program_context_map(self) -> Dict[int, IndexedTableValue]:
8989
return self.context_table.objectmap(self.get_program_context)
9090

91+
# ----------------- read_xml service ---------------------------------------
92+
93+
def read_xml_program_context(
94+
self, node: ET.Element, tag: str = "ictxt") -> ProgramContext:
95+
xtag = node.get(tag)
96+
if xtag is not None:
97+
return self.get_program_context(int(xtag))
98+
else:
99+
raise UF.CHCError(f"Xml element {node.tag} is missing attribute {tag}")
100+
91101
# ----------------------- Printing -----------------------------------------
92102

93103
def objectmap_to_string(self, name: str) -> str:

chc/app/CDictionary.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,13 @@ def read_xml_exp(self, node: ET.Element, tag: str = "iexp") -> CExp:
268268
else:
269269
raise Exception('xml node was missing the tag "' + tag + '"')
270270

271+
def read_xml_exp_o(
272+
self, node: ET.Element, tag: str = "iexp") -> Optional[CExp]:
273+
xtag_o = node.get(tag)
274+
if xtag_o is not None:
275+
return self.get_exp(int(xtag_o))
276+
return None
277+
271278
def write_xml_exp_opt(
272279
self,
273280
node: ET.Element,

chc/app/CFileDeclarations.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
List,
3838
Mapping,
3939
NoReturn,
40+
Optional,
4041
Tuple,
4142
TYPE_CHECKING)
4243
import xml.etree.ElementTree as ET
@@ -92,6 +93,10 @@ def xget_int_attr(p: ET.Element, tag: str) -> int:
9293
return UF.xget_int_attr(p, tag, "CFileDeclarations")
9394

9495

96+
def xget_int_attr_o(node: ET.Element, tag: str) -> Optional[int]:
97+
return UF.xget_int_attr_o(node, tag)
98+
99+
95100
class CFilename(CDeclarationsRecord):
96101

97102
def __init__(self, decls: CDeclarations, ixval: IndexedTableValue):
@@ -266,6 +271,17 @@ def read_xml_location(
266271
return CLocation(self, itv)
267272
return self.get_location(index)
268273

274+
def read_xml_location_o(
275+
self, xnode: ET.Element, tag: str = "iloc") -> Optional[CLocation]:
276+
index = xget_int_attr_o(xnode, tag)
277+
if index is not None:
278+
if index == -1:
279+
args = [-1, -1, -1]
280+
itv = IndexedTableValue(-1, [], args)
281+
return CLocation(self, itv)
282+
return self.get_location(index)
283+
return None
284+
269285
# -------------------- Index items by category ---------------------------
270286

271287
def index_filename(self, name: str) -> int:

chc/app/CFunction.py

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@
3535

3636
from chc.api.CFunctionApi import CFunctionApi
3737

38-
from chc.app.CLocation import CLocation
3938
from chc.app.CFunDeclarations import CFunDeclarations
4039
from chc.app.CInstr import CCallInstr
40+
from chc.app.CFunctionReturnSite import CFunctionReturnSite
41+
from chc.app.CLocation import CLocation
4142
from chc.app.CStmt import CFunctionBody
4243
from chc.app.IndexManager import FileVarReference
4344

@@ -159,6 +160,7 @@ def __init__(self, cfile: "CFile", xnode: ET.Element, fname: str) -> None:
159160
self._formals: Dict[int, "CVarInfo"] = {} # vid -> CVarInfo
160161
self._locals: Dict[int, "CVarInfo"] = {} # vid -> CVarInfo
161162
self._sbody: Optional[CFunctionBody] = None
163+
self._returnsites: Optional[List[CFunctionReturnSite]] = None
162164
self._podictionary: Optional[CFunPODictionary] = None
163165
self._api: Optional[CFunctionApi] = None
164166
self._proofs: Optional[CFunctionProofs] = None
@@ -373,6 +375,36 @@ def podictionary(self) -> CFunPODictionary:
373375
self._podictionary = CFunPODictionary(self, pxnode)
374376
return self._podictionary
375377

378+
@property
379+
def returnsites(self) -> List[CFunctionReturnSite]:
380+
if self._returnsites is None:
381+
self._returnsites = []
382+
xsponode = UF.get_spo_xnode(
383+
self.targetpath,
384+
self.projectname,
385+
self.cfilepath,
386+
self.cfilename,
387+
self.name)
388+
if xsponode is None:
389+
raise UF.CHCError(self.xmsg("spo file is missing"))
390+
xxsponode = xsponode.find("spos")
391+
if xxsponode is None:
392+
raise UF.CHCError(self.xmsg("spo file has no spos element"))
393+
xreturnsites = xxsponode.find("returnsites")
394+
if xreturnsites is None:
395+
raise UF.CHCError(
396+
self.xmsg("spo file has no returnsites element"))
397+
for xreturnsite in xreturnsites.findall("rs"):
398+
self._returnsites.append(CFunctionReturnSite(self, xreturnsite))
399+
return self._returnsites
400+
401+
def get_returnsite(
402+
self, ctxtid: int) -> Optional[CFunctionReturnSite]:
403+
for rs in self.returnsites:
404+
if rs.context.index == ctxtid:
405+
return rs
406+
return None
407+
376408
@property
377409
def proofs(self) -> CFunctionProofs:
378410
if self._proofs is None:
@@ -397,22 +429,30 @@ def proofs(self) -> CFunctionProofs:
397429
raise UF.CHCError(self.xmsg("spo file is missing"))
398430
xxsponode = xsponode.find("spos")
399431
if xxsponode is None:
400-
raise UF.CHCError(self.xmsg("_spo file has no spos element"))
432+
raise UF.CHCError(self.xmsg("spo file has no spos element"))
401433
self._proofs = CFunctionProofs(self, xxpponode, xxsponode)
402434
return self._proofs
403435

404436
@property
405437
def analysis_info(self) -> CAnalysisInfo:
406438
if self._analysisinfo is None:
407-
xpponode = UF.get_ppo_xnode(
408-
self.targetpath,
409-
self.projectname,
410-
self.cfilepath,
411-
self.cfilename,
412-
self.name)
413-
if xpponode is not None:
414-
self._analysisinfo = (
415-
CAnalysisInfo(xpponode.find("analysis-info"), self))
439+
if UF.has_ppo_file(
440+
self.targetpath,
441+
self.projectname,
442+
self.cfilepath,
443+
self.cfilename,
444+
self.name):
445+
xpponode = UF.get_ppo_xnode(
446+
self.targetpath,
447+
self.projectname,
448+
self.cfilepath,
449+
self.cfilename,
450+
self.name)
451+
if xpponode is not None:
452+
self._analysisinfo = (
453+
CAnalysisInfo(xpponode.find("analysis-info"), self))
454+
else:
455+
self._analysisinfo = CAnalysisInfo(None, self)
416456
else:
417457
self._analysisinfo = CAnalysisInfo(None, self)
418458
return self._analysisinfo

chc/app/CFunctionReturnSite.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# ------------------------------------------------------------------------------
2+
# CodeHawk C Analyzer
3+
# Author: Henny Sipma
4+
# ------------------------------------------------------------------------------
5+
# The MIT License (MIT)
6+
#
7+
# Copyright (c) 2025 Aarno Labs LLC
8+
#
9+
# Permission is hereby granted, free of charge, to any person obtaining a copy
10+
# of this software and associated documentation files (the "Software"), to deal
11+
# in the Software without restriction, including without limitation the rights
12+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
# copies of the Software, and to permit persons to whom the Software is
14+
# furnished to do so, subject to the following conditions:
15+
#
16+
# The above copyright notice and this permission notice shall be included in all
17+
# copies or substantial portions of the Software.
18+
#
19+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
# SOFTWARE.
26+
# ------------------------------------------------------------------------------
27+
28+
import xml.etree.ElementTree as ET
29+
30+
from typing import Optional, TYPE_CHECKING
31+
32+
if TYPE_CHECKING:
33+
from chc.app.CContext import ProgramContext
34+
from chc.app.CContext import CContextDictionary
35+
from chc.app.CDictionary import CDictionary
36+
from chc.app.CExp import CExp
37+
from chc.app.CFile import CFile
38+
from chc.app.CFileDeclarations import CFileDeclarations
39+
from chc.app.CFunction import CFunction
40+
from chc.app.CLocation import CLocation
41+
42+
43+
class CFunctionReturnSite:
44+
45+
def __init__(self, cfun: "CFunction", xnode: ET.Element) -> None:
46+
self._cfun = cfun
47+
self._xnode = xnode
48+
49+
@property
50+
def cfun(self) -> "CFunction":
51+
return self._cfun
52+
53+
@property
54+
def cdictionary(self) -> "CDictionary":
55+
return self.cfun.cdictionary
56+
57+
@property
58+
def cfile(self) -> "CFile":
59+
return self.cfun.cfile
60+
61+
@property
62+
def cfiledeclarations(self) -> "CFileDeclarations":
63+
return self.cfile.declarations
64+
65+
@property
66+
def contextdictionary(self) -> "CContextDictionary":
67+
return self.cfile.contextdictionary
68+
69+
@property
70+
def xnode(self) -> ET.Element:
71+
return self._xnode
72+
73+
@property
74+
def location(self) -> "CLocation":
75+
return self.cfiledeclarations.read_xml_location(self.xnode)
76+
77+
@property
78+
def returnexp(self) -> Optional["CExp"]:
79+
return self.cdictionary.read_xml_exp_o(self.xnode)
80+
81+
@property
82+
def context(self) -> "ProgramContext":
83+
return self.contextdictionary.read_xml_program_context(self.xnode)
84+
85+
def __str__(self):
86+
rexp = str(self.returnexp) if self.returnexp else ""
87+
return (
88+
"return " + rexp + " ("
89+
+ str(self.location.line) + ", " + str(self.location.byte)
90+
+ ") ("
91+
+ str(self.context)
92+
)

chc/cmdline/c_file/cfileutil.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -653,14 +653,17 @@ def pofilter(po: "CFunctionPO") -> bool:
653653
print(RP.file_proofobligation_stats_tostring(cfile))
654654

655655
if canalysis == "outputparameters":
656+
print("\n\nOutput parameter analysis results:\n")
656657
for cfun in cfile.functions.values():
657-
try:
658-
op_checker = OutputParameterChecker(cfun)
659-
print(str(op_checker))
660-
except UF.CHCError as e:
661-
print("Skipping function " + cfun.name)
662-
continue
663-
658+
op_checker = OutputParameterChecker(cfun)
659+
results = op_checker.results()
660+
if len(results) > 0:
661+
print("\nFunction: " + cfun.name + ": ")
662+
for result in results:
663+
if result.is_success():
664+
print(result.success_str())
665+
else:
666+
print(result.failure_str())
664667
exit(0)
665668

666669
for fnname in cfunctions:

0 commit comments

Comments
 (0)