Skip to content

Commit 98d0df3

Browse files
authored
Merge branch 'master' into performance
2 parents 53331ef + 202ac80 commit 98d0df3

10 files changed

Lines changed: 210 additions & 16 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Interaction with the analyzer is primarily through the command-line interpreter
5555
```
5656
> chkc c-file parse <filename>
5757
> chkc c-file analyze <filename>
58-
> chkc c-file report-file <filename>
58+
> chkc c-file report <filename>
5959
```
6060
The first command preprocesses the file with `cc`; the preprocessed file (.i file)
6161
is then parsed with **parseFile** (a wrapper for goblint-cil,

chc/app/CContext.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
3232
"""
3333

34-
from typing import List, TYPE_CHECKING
34+
from typing import Any, cast, List, TYPE_CHECKING
3535

3636
import chc.util.fileutil as UF
3737
from chc.util.IndexedTable import IndexedTableValue
@@ -163,5 +163,13 @@ def cfg_context(self) -> CfgContext:
163163
def exp_context(self) -> ExpContext:
164164
return self.cxd.get_exp_context(self.args[1])
165165

166+
def __eq__(self, other: Any) -> bool:
167+
if not isinstance(other, ProgramContext):
168+
return NotImplemented
169+
return self.index == cast("ProgramContext", other).index
170+
171+
def __hash__(self) -> int:
172+
return self.index
173+
166174
def __str__(self) -> str:
167175
return "(" + str(self.cfg_context) + "," + str(self.exp_context) + ")"

chc/app/CFile.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -306,17 +306,20 @@ def functions(self) -> Dict[int, CFunction]:
306306
if self._functions is None:
307307
self._functions = {}
308308
for (vid, gf) in self.gfunctions.items():
309-
fnname = gf.vname
310-
xnode = UF.get_cfun_xnode(
311-
self.targetpath,
312-
self.projectname,
313-
self.cfilepath,
314-
self.cfilename,
315-
fnname)
316-
if xnode is not None:
317-
cfunction = CFunction(self, xnode, fnname)
318-
self._functions[vid] = cfunction
319-
else:
309+
try:
310+
fnname = gf.vname
311+
xnode = UF.get_cfun_xnode(
312+
self.targetpath,
313+
self.projectname,
314+
self.cfilepath,
315+
self.cfilename,
316+
fnname)
317+
if xnode is not None:
318+
cfunction = CFunction(self, xnode, fnname)
319+
self._functions[vid] = cfunction
320+
else:
321+
chklogger.logger.warning("Function {fnname} not found")
322+
except Exception as e:
320323
chklogger.logger.warning("Function {fnname} not found")
321324
return self._functions
322325

chc/cmdline/c_file/cfileutil.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import subprocess
5757
import sys
5858

59+
5960
from typing import (
6061
Any, Callable, cast, Dict, List, NoReturn, Optional, TYPE_CHECKING)
6162

@@ -73,6 +74,7 @@
7374
from chc.util.loggingutil import chklogger, LogLevel
7475

7576
if TYPE_CHECKING:
77+
from chc.invariants.CInvariantFact import CInvariantNRVFact
7678
from chc.app.CAttributes import CAttributes
7779
from chc.app.CInstr import CCallInstr
7880
from chc.app.CTyp import (
@@ -942,6 +944,62 @@ def header(s: str) -> str:
942944
exit(0)
943945

944946

947+
def cfile_query_invariants(args: argparse.Namespace) -> NoReturn:
948+
949+
# arguments
950+
xcfilename: str = args.filename
951+
opttgtpath: Optional[str] = args.tgtpath
952+
xfunction: str = args.function
953+
xline: int = args.line
954+
955+
projectpath = os.path.dirname(os.path.abspath(xcfilename))
956+
targetpath = projectpath if opttgtpath is None else opttgtpath
957+
targetpath = os.path.abspath(targetpath)
958+
cfilename_c = os.path.basename(xcfilename)
959+
cfilename = cfilename_c[:-2]
960+
projectname = cfilename
961+
962+
cchpath = UF.get_cchpath(targetpath, projectname)
963+
contractpath = os.path.join(targetpath, "chc_contracts")
964+
965+
if not UF.check_analysis_results_files(targetpath, projectname, None, cfilename):
966+
print_error("No analysis results found for " + cfilename
967+
+ ". Please run analyzer first.")
968+
exit(1)
969+
970+
capp = CApplication(
971+
projectpath, projectname, targetpath, contractpath, singlefile=True)
972+
capp.initialize_single_file(cfilename)
973+
cfile = capp.get_cfile()
974+
975+
if not cfile.has_function_by_name(xfunction):
976+
print_error("No function found with name " + xfunction)
977+
exit(1)
978+
979+
cfun = cfile.get_function_by_name(xfunction)
980+
ppos = cfun.get_ppos()
981+
contexts = {ppo.context for ppo in ppos if ppo.line == xline}
982+
983+
print("Invariants for function " + xfunction + ", line " + str(xline))
984+
if len(contexts) == 0:
985+
print("\nNo ast positions found with invariants on line " + str(xline) + ".")
986+
exit(0)
987+
988+
for ctxt in contexts:
989+
print("\nAST position: " + str(ctxt))
990+
print("-" * (len(str(ctxt)) + 14))
991+
invs = cfun.invarianttable.get_sorted_invariants(ctxt)
992+
nrvfacts: List[str] = []
993+
for inv in invs:
994+
if inv.is_nrv_fact:
995+
inv = cast("CInvariantNRVFact", inv)
996+
if not inv.variable.is_check_variable:
997+
nrvfacts.append(str(inv))
998+
print("\n".join(nrvfacts))
999+
1000+
exit(0)
1001+
1002+
9451003
def cfile_testlibc_summary(args: argparse.Namespace) -> NoReturn:
9461004
"""Runs one of the programs in tests/libcsummaries
9471005

chc/cmdline/c_project/cprojectutil.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
from chc.app.CFunction import CFunction
6262
from chc.app.CInstr import CInstr, CCallInstr
6363
from chc.app.CStmt import CInstrsStmt, CStmt
64+
from chc.invariants.CInvariantFact import CInvariantNRVFact
6465
from chc.app.CTyp import (
6566
CTypComp, CTypFloat, CTypFun, CTypInt, CTypPtr)
6667
from chc.proof.CFunctionPO import CFunctionPO
@@ -819,6 +820,64 @@ def header(s: str) -> str:
819820
exit(0)
820821

821822

823+
def cproject_query_invariants(args: argparse.Namespace) -> NoReturn:
824+
825+
# arguments
826+
tgtpath: str = args.tgtpath
827+
projectname: str = args.projectname
828+
filename: str = args.filename
829+
xfunction: str = args.function
830+
xline: int = args.line
831+
832+
targetpath = os.path.abspath(tgtpath)
833+
projectpath = targetpath
834+
cfilename_c = os.path.basename(filename)
835+
cfilename = cfilename_c[:-2]
836+
cfilepath = os.path.dirname(filename)
837+
838+
if not UF.has_analysisresults_path(targetpath, projectname):
839+
print_error(
840+
f"No analysis results found for {projectname} in {targetpath}")
841+
exit(1)
842+
843+
contractpath = os.path.join(targetpath, "chc_contracts")
844+
capp = CApplication(
845+
projectpath, projectname, targetpath, contractpath)
846+
847+
if capp.has_file(filename[:-2]):
848+
cfile = capp.get_file(filename[:-2])
849+
else:
850+
print_error(f"File {filename} not found")
851+
exit(1)
852+
853+
if not cfile.has_function_by_name(xfunction):
854+
print_error("No function found with name " + xfunction)
855+
exit(1)
856+
857+
cfun = cfile.get_function_by_name(xfunction)
858+
ppos = cfun.get_ppos()
859+
contexts = {ppo.context for ppo in ppos if ppo.line == xline}
860+
861+
print("Invariants for function " + xfunction + ", line " + str(xline))
862+
if len(contexts) == 0:
863+
print("\nNo ast positions found with invariants on line " + str(xline) + ".")
864+
exit(0)
865+
866+
for ctxt in contexts:
867+
print("\nAST position: " + str(ctxt))
868+
print("-" * (len(str(ctxt)) + 14))
869+
invs = cfun.invarianttable.get_sorted_invariants(ctxt)
870+
nrvfacts: List[str] = []
871+
for inv in invs:
872+
if inv.is_nrv_fact:
873+
inv = cast("CInvariantNRVFact", inv)
874+
if not inv.variable.is_check_variable:
875+
nrvfacts.append(str(inv))
876+
print("\n".join(nrvfacts))
877+
878+
exit(0)
879+
880+
822881
def cproject_count_stmts(args: argparse.Namespace) -> NoReturn:
823882
"""CLI command to output size statistics for a c project."""
824883

chc/cmdline/chkc

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,27 @@ def parse() -> argparse.Namespace:
937937
help="file mode for log file: append (a, default), or write (w)")
938938
cfileinvestigate.set_defaults(func=C.cfile_investigate_file)
939939

940+
# --- query
941+
cfilequery = cfileparsers.add_parser("query")
942+
cfilequeryparsers = cfilequery.add_subparsers(title="show options")
943+
944+
# -- query invariants --
945+
cfilequeryinvs = cfilequeryparsers.add_parser("invariants")
946+
cfilequeryinvs.add_argument(
947+
"filename",
948+
help="name of file analyzed ((<cpath/>)<cfilename>)")
949+
cfilequeryinvs.add_argument(
950+
"function",
951+
help="name of function")
952+
cfilequeryinvs.add_argument(
953+
"line",
954+
type=int,
955+
help="line number in the code for which to show invariants")
956+
cfilequeryinvs.add_argument(
957+
"--tgtpath",
958+
help="directory that holds the analysis results")
959+
cfilequeryinvs.set_defaults(func=C.cfile_query_invariants)
960+
940961
# --- test libc summary
941962
cfiletestlibc = cfileparsers.add_parser("test-libc-summary")
942963
cfiletestlibc.add_argument(
@@ -1673,6 +1694,25 @@ def parse() -> argparse.Namespace:
16731694
nargs="*",
16741695
help="names of predicates of interest, e.g., not-null (default: all")
16751696
cprojectinvestigate.set_defaults(func=P.cproject_investigate)
1697+
# --- query
1698+
cprojectquery = cprojectparsers.add_parser("query")
1699+
cprojectqueryparsers = cprojectquery.add_subparsers(title="show options")
1700+
1701+
# --- query invariants
1702+
cprojectqueryinvs = cprojectqueryparsers.add_parser("invariants")
1703+
cprojectqueryinvs.add_argument(
1704+
"tgtpath", help="directory that contains the analysis results")
1705+
cprojectqueryinvs.add_argument(
1706+
"projectname", help="name of the project")
1707+
cprojectqueryinvs.add_argument(
1708+
"filename", help="filename with relative path wrt the project")
1709+
cprojectqueryinvs.add_argument(
1710+
"function", help="name of function")
1711+
cprojectqueryinvs.add_argument(
1712+
"line",
1713+
type=int,
1714+
help="line number in the source code to show invariants")
1715+
cprojectqueryinvs.set_defaults(func=P.cproject_query_invariants)
16761716

16771717
# --- count-statements
16781718
cprojectcountstmts = cprojectparsers.add_parser("count-statements")

chc/invariants/CInvariantFact.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ def is_nrv_fact(self) -> bool:
5858
def is_unreachable_fact(self) -> bool:
5959
return False
6060

61+
@property
62+
def is_parameter_constraint(self) -> bool:
63+
return False
64+
6165
def __str__(self) -> str:
6266
return "invariant-fact:" + self.tags[0]
6367

@@ -100,6 +104,10 @@ def __init__(
100104
def xpr(self) -> "CXXpr":
101105
return self.xd.get_xpr(self.args[0])
102106

107+
@property
108+
def is_parameter_constraint(self) -> bool:
109+
return True
110+
103111
def __str__(self) -> str:
104112
return str(self.xpr)
105113

chc/invariants/CXVariable.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ def denotation(self) -> "CVariableDenotation":
7575
else:
7676
raise UF.CHCError("Variable does not have a denotation")
7777

78+
@property
79+
def is_check_variable(self) -> bool:
80+
if self.has_denotation():
81+
return self.vd.get_c_variable_denotation(self.seqnr).is_check_variable
82+
else:
83+
return False
84+
7885
def has_denotation(self) -> bool:
7986
return self.seqnr > 0
8087

chc/util/ConfigLocal.template

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,13 @@ The config object passed to getLocals is the universal Config, you can update
3434
any of that config's variables here if you want to use a value other than the default.
3535
"""
3636

37-
import os
37+
from typing import TYPE_CHECKING
3838

39-
import chc.util.Config
39+
if TYPE_CHECKING:
40+
from chc.util.Config import Config
4041

4142

42-
def getLocals(config: chc.util.Config.Config) -> None:
43+
def getLocals(config: Config) -> None:
4344
'''Set local configuration variables here if they differ from the defaults in Config.py
4445

4546
Example :

chc/util/fileutil.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,16 @@ def get_cfile_predicate_dictionaryname(
939939
return os.path.join(filepath, cfilename + "_prd.xml")
940940

941941

942+
def check_analysis_results_files(
943+
targetpath: str,
944+
projectname: str,
945+
cfilepath: Optional[str],
946+
cfilename: str) -> bool:
947+
filename = get_cfile_predicate_dictionaryname(
948+
targetpath, projectname, cfilepath, cfilename)
949+
return os.path.isfile(filename)
950+
951+
942952
def get_cfile_predicate_dictionary_xnode(
943953
targetpath: str,
944954
projectname: str,

0 commit comments

Comments
 (0)