From 9c64caa4f32b3e2314bae794d4679e7bb06482fc Mon Sep 17 00:00:00 2001 From: Henny Sipma Date: Thu, 22 Oct 2026 11:48:37 -0700 Subject: [PATCH 1/3] Fix function signature printing in the presence of argument/returntype function pointers --- chc/app/CPrettyPrinter.py | 262 ++++++++++++++++++++++---------------- 1 file changed, 154 insertions(+), 108 deletions(-) diff --git a/chc/app/CPrettyPrinter.py b/chc/app/CPrettyPrinter.py index 54ce2af..b2996a8 100644 --- a/chc/app/CPrettyPrinter.py +++ b/chc/app/CPrettyPrinter.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ # The MIT License (MIT) # -# Copyright (c) 2025 Aarno Labs LLC +# Copyright (c) 2025-2026 Aarno Labs LLC # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -39,6 +39,9 @@ from chc.app.CVisitor import CVisitor +import chc.util.fileutil as UF +from chc.util.loggingutil import chklogger + if TYPE_CHECKING: from chc.app.CFileGlobals import CGCompTag, CGType, CGVarDef from chc.app.CCompInfo import CCompInfo @@ -195,15 +198,22 @@ def visit_cgcomptag(self, cgcomptag: "CGCompTag") -> None: self.ccode.newline() self.ccode.write("}") - def visit_cgvardef(self, cgvardef: "CGVarDef") -> None: - cgvardef.location.accept(self) - self.ccode.newline(indent=0) - cgvardef.varinfo.vtype.accept(self) - self.ccode.write(" ") - self.ccode.write(cgvardef.vname) - if cgvardef.initializer is not None: - self.ccode.write(" = ") - cgvardef.initializer.accept(self) + def visit_cgvardef(self, gvardef: "CGVarDef") -> None: + gvardef.location.accept(self) + + # 1. Capture storage specifiers safely (e.g., static, extern) + storage = "" + if gvardef.varinfo.vstorage == "static": + storage = "static " + elif gvardef.varinfo.vstorage == "extern": + storage = "extern " + + # 2. Convert the entire type and variable name atomically + # This prevents sequential chunk printing like "char [8] weak_keys[16]" + full_declaration = self.type_to_string(gvardef.varinfo.vtype, gvardef.varinfo.vname) + + # 3. Write it out cleanly as a single unit + self.ccode.write(f"{storage}{full_declaration};") def visit_typeinfo(self, typeinfo: "CTypeInfo") -> None: self.ccode.newline(indent=0) @@ -287,12 +297,7 @@ def visit_ptrtyp(self, t: CT.CTypPtr) -> None: self.ccode.write(" *") def visit_arraytyp(self, t: CT.CTypArray) -> None: - t.array_basetype.accept(self) - self.ccode.write("[") - if t.array_size_expr is not None: - # self.ccode.write("?") - t.array_size_expr.accept(self) - self.ccode.write("]") + self.ccode.write(self.type_to_string(t)) def visit_funtyp(self, t: CT.CTypFun) -> None: """Emits a function type without name.""" @@ -329,34 +334,15 @@ def gvar_definition_str( if vinfo.vtype.is_function: return "error" - if vinfo.vtype.is_array: - sizexps: List[Optional[CE.CExp]] = [] - self.ccode.newline() - atype = cast(CT.CTypArray, vinfo.vtype) - sizexps.append(atype.array_size_expr) - basetype = atype.array_basetype - while basetype.is_array: - atype = cast(CT.CTypArray, basetype) - sizexps.append(atype.array_size_expr) - basetype = atype.array_basetype - basetype.accept(self) - self.ccode.write(" ") - self.ccode.write(vinfo.vname) - for s in sizexps: - self.ccode.write("[") - if s: - s.accept(self) - self.ccode.write("]") - self.ccode.write(";") - self.ccode.newline() - return str(self.ccode) + storage = "" + if vinfo.vstorage == "static": + storage = "static " + elif vinfo.vstorage == "extern": + storage = "extern " self.ccode.newline() - vinfo.vtype.accept(self) - self.ccode.write(" ") - self.ccode.write(vinfo.vname) - if srcloc is not None: - self.write_chkx_srcloc_attribute(srcloc, vinfo.line, binloc) + full_declaration = self.type_to_string(vinfo.vtype, vinfo.vname) + self.ccode.write(f"{storage}{full_declaration}") self.ccode.write(";") self.ccode.newline() return str(self.ccode) @@ -369,6 +355,7 @@ def function_declaration_str( if not vinfo.vtype.is_function: return "error" + self.ccode.newline() self.write_function_signature(vinfo, srcloc=srcloc, binloc=binloc) self.ccode.write(";") return str(self.ccode) @@ -379,32 +366,11 @@ def write_function_signature( srcloc: Optional[str] = None, binloc: Optional[str] = None) -> None: self.ccode.newline() - ftype = cast(CT.CTypFun, vinfo.vtype) - if ftype.return_type.is_function_pointer: - returntyp = cast(CT.CTypPtr, ftype.return_type) - retfntyp = cast(CT.CTypFun, returntyp.pointedto_type) - retfntyp.return_type.accept(self) - self.ccode.write(" (*") - self.ccode.write(vinfo.vname) - self.ccode.write("(") - if ftype.funargs is not None: - ftype.funargs.accept(self) - self.ccode.write("))(") - if retfntyp.funargs is not None: - retfntyp.funargs.accept(self) - self.ccode.write(")") - if srcloc is not None: - self.write_chkx_srcloc_attribute(srcloc, vinfo.line, binloc) - else: - ftype.return_type.accept(self) - self.ccode.write(" ") - self.ccode.write(vinfo.vname) - self.ccode.write("(") - if ftype.funargs is not None: - ftype.funargs.accept(self) - if ftype.is_vararg: - self.ccode.write(", ...") - self.ccode.write(")") + + # Build the entire type signature starting at the core function name identifier + signature_str = self.type_to_string(vinfo.vtype, vinfo.vname) + self.ccode.write(signature_str) + if srcloc is not None: self.write_chkx_srcloc_attribute(srcloc, vinfo.line, binloc) @@ -427,7 +393,6 @@ def funtypptr_with_name(self, t: CT.CTypFun, name: str) -> None: self.ccode.write(")") else: t.return_type.accept(self) - self.ccode.write(" (*") self.ccode.write(name) self.ccode.write(")(") if t.funargs is not None: @@ -444,33 +409,125 @@ def visit_funargs(self, funargs: CT.CFunArgs) -> None: self.ccode.write(", ") args[-1].accept(self) + def type_to_string(self, t: CT.CTyp, inner_str: str = "") -> str: + """ + Helper method to generate explicit, C types recursively from the inside + out, expanding all typedef aliases and maintaining const correctness. + """ + is_const = False + + # --- Pre-processing Phase: Force Structural Unrolling --- + while t.is_named_type: + if hasattr(t, 'attributes') and t.attributes is not None: + attr_names = [attr.name for attr in t.attributes.attributes] + if any(name in attr_names for name in ["const", "pconst"]): + is_const = True + t = cast(CT.CTypNamed, t).expand() + + # Capture const markers at this recursive layer before doing anything else + if hasattr(t, 'attributes') and t.attributes is not None: + attr_names = [attr.name for attr in t.attributes.attributes] + if any(name in attr_names for name in ["const", "pconst"]): + is_const = True + + # Forced parenthetical grouping for function pointers + if t.is_pointer: + ptr_t = cast(CT.CTypPtr, t) + if ptr_t.pointedto_type.expand().is_function: + inner_str = f"(*{inner_str})" + if is_const: + inner_str = f"const {inner_str}" + return self.type_to_string(ptr_t.pointedto_type, inner_str) + + # --- Standard Structural Match Rules --- + if t.is_void: + base = "void" + return (f"const {base} {inner_str}".strip() + if is_const else f"{base} {inner_str}".strip()) + + elif t.is_int: + base = integernames[cast(CT.CTypInt, t).ikind] + return (f"const {base} {inner_str}".strip() + if is_const else f"{base} {inner_str}".strip()) + + elif t.is_float: + base = str(t) + return (f"const {base} {inner_str}".strip() + if is_const else f"{base} {inner_str}".strip()) + elif t.is_comp: + comp_t = cast(CT.CTypComp, t) + prefix = "struct " if comp_t.is_comp else "union " + base = f"{prefix}{self.de_anon_name(comp_t.name)}" + return (f"const {base} {inner_str}".strip() + if is_const else f"{base} {inner_str}".strip()) + + elif t.is_enum: + base = f"enum {cast(CT.CTypEnum, t).name}" + return (f"const {base} {inner_str}".strip() + if is_const else f"{base} {inner_str}".strip()) + + elif t.is_pointer: + ptr_t = cast(CT.CTypPtr, t) + basetype = (ptr_t.pointedto_type.expand() + if ptr_t.pointedto_type.is_named_type + else ptr_t.pointedto_type) + + # Note: In C, "const Type *var" attaches the const attribute to the + # 'pointedto_type' target layer, while "Type *const var" attaches + # it to the pointer shell layer itself. + # Here, 'is_const' is treated at the pointer shell layer as a constant + # pointer wrapper descriptor. + ptr_modifier = " *const " if is_const else " *" + + if basetype.is_function or basetype.is_array: + return self.type_to_string( + basetype, f"({ptr_modifier.strip()}{inner_str})") + else: + # Recurse straight down into the pointed-to type. + # When the recursion checks the attributes of 'ptr_t.pointedto_type', + # it will automatically find the 'const' marker if the target + # data structure is constant. + return self.type_to_string( + ptr_t.pointedto_type, + f"{ptr_modifier}{inner_str}".replace(" ", " ")) + + elif t.is_array: + arr_t = cast(CT.CTypArray, t) + size_str = "" + if arr_t.array_size_expr is not None: + temp_pp = CPrettyPrinter() + temp_pp.ccode.newline(indent=0) + arr_t.array_size_expr.accept(temp_pp) + size_str = str(temp_pp.ccode).strip() + + new_inner = f"{inner_str}[{size_str}]" if inner_str else f"[{size_str}]" + return self.type_to_string(arr_t.array_basetype, new_inner) + + elif t.is_function: + fun_t = cast(CT.CTypFun, t) + + if fun_t.funargs is None or len(fun_t.funargs.arguments) == 0: + args_str = "void" + else: + arg_list = [] + for arg in fun_t.funargs.arguments: + arg_list.append(self.type_to_string(arg.typ, arg.name)) + args_str = ", ".join(arg_list) + + if fun_t.is_vararg: + args_str += ", ..." + + return self.type_to_string(fun_t.return_type, f"{inner_str}({args_str})") + + elif t.is_builtin_vaargs: + return f"va_list {inner_str}".strip() + + return f"[[unknown type]] {inner_str}".strip() + def visit_funarg(self, funarg: CT.CFunArg) -> None: - if funarg.typ.is_function_pointer: - argtyp = cast(CT.CTypPtr, funarg.typ) - fnptr = cast(CT.CTypFun, argtyp.pointedto_type) - self.funtypptr_with_name(fnptr, funarg.name) - return - - if funarg.typ.is_pointer: - argtyp = cast(CT.CTypPtr, funarg.typ) - ptargtyp = argtyp.pointedto_type - if ptargtyp.is_named_type: - ptx = cast(CT.CTypNamed, ptargtyp).expand() - if ptx.is_function: - ptx = cast(CT.CTypFun, ptx) - self.funtypptr_with_name(ptx, funarg.name) - return - - # if ( - # argtyp.attributes.length > 0 - # and argtyp.attributes.attributes[0].name == "arraylen"): - # self.ptrarg_with_attribute_length( - # ptargtyp, argtyp.attributes.attributes[0], funarg.name) - # return - - funarg.typ.accept(self) - self.ccode.write(" ") - self.ccode.write(funarg.name) + + arg_str = self.type_to_string(funarg.typ, funarg.name) + self.ccode.write(arg_str) def ptrarg_with_attribute_length( self, t: CT.CTyp, a: CA.CAttribute, name: str) -> None: @@ -504,18 +561,6 @@ def ptrarg_with_attribute_length( self.ccode.write(str(aparam.intvalue)) self.ccode.write("]") - ''' - def visit_attributes(self, a: CA.CAttributes) -> None: - if a.length == 0: - return - if a.attributes[0].name in ["const", "pconst"]: - self.ccode.write("const ") - elif a.attributes[0].name == "volatile": - self.ccode.write("volatile ") - else: - self.ccode.write("[[" + a.attributes[0].name + " not yet supported]]") - ''' - def visit_constexp(self, e: CE.CExpConst) -> None: self.ccode.write(str(e)) @@ -699,7 +744,8 @@ def visit_attribute(self, a: CA.CAttribute) -> None: self.ccode.write(a.name) self.ccode.write(" ") else: - self.ccode.write("attribute not yet supported " + str(a)) + chklogger.logger.warning( + "attribute not yet supported %s", str(a)) def visit_attr_int(self, a: CA.CAttrInt) -> None: pass From e55b767601bc0fd572772ec3dc52fcaa7cefc783 Mon Sep 17 00:00:00 2001 From: Henny Sipma Date: Thu, 22 Oct 2026 12:53:32 -0700 Subject: [PATCH 2/3] CMD: add command to save local buffers --- chc/app/CTyp.py | 5 +- chc/app/CVarInfo.py | 4 + chc/cmdline/c_project/cprojectutil.py | 43 +++ chc/cmdline/chkc | 24 ++ chc/reporting/BinCheckData.py | 395 ++++++++++++++++++++++++++ 5 files changed, 470 insertions(+), 1 deletion(-) create mode 100644 chc/reporting/BinCheckData.py diff --git a/chc/app/CTyp.py b/chc/app/CTyp.py index 1a8e87b..0447bcd 100644 --- a/chc/app/CTyp.py +++ b/chc/app/CTyp.py @@ -6,7 +6,7 @@ # # Copyright (c) 2017-2020 Kestrel Technology LLC # Copyright (c) 2020-2022 Henny B. Sipma -# Copyright (c) 2023-2025 Aarno Labs LLC +# Copyright (c) 2023-2026 Aarno Labs LLC # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -562,6 +562,9 @@ def array_size_expr(self) -> "CExp": def has_array_size_expr(self) -> bool: return self.args[1] >= 0 + def has_array_constant_size(self) -> bool: + return not (self.size < 0) + @property def size(self) -> int: try: diff --git a/chc/app/CVarInfo.py b/chc/app/CVarInfo.py index 33c112e..53668a0 100644 --- a/chc/app/CVarInfo.py +++ b/chc/app/CVarInfo.py @@ -79,6 +79,10 @@ def vtype(self) -> "CTyp": def is_global(self) -> bool: return self.vglob + @property + def is_static(self) -> bool: + return self.is_global and self.vstorage == "s" + @property def vglob(self) -> bool: return self.args[3] == 1 diff --git a/chc/cmdline/c_project/cprojectutil.py b/chc/cmdline/c_project/cprojectutil.py index 96cf9eb..04243c6 100644 --- a/chc/cmdline/c_project/cprojectutil.py +++ b/chc/cmdline/c_project/cprojectutil.py @@ -49,6 +49,7 @@ from chc.linker.CLinker import CLinker +from chc.reporting.BinCheckData import BinCheckData import chc.reporting.ProofObligations as RP from chc.util.Config import Config @@ -57,6 +58,7 @@ if TYPE_CHECKING: from chc.app.CAttributes import CAttributes + from chc.app.CCompInfo import CCompInfo from chc.app.CFile import CFile from chc.app.CFunction import CFunction from chc.app.CInstr import CInstr, CCallInstr @@ -350,6 +352,47 @@ def has_const_attribute(attrs: "CAttributes") -> bool: exit(0) +def cproject_save_local_buffers(args: argparse.Namespace) -> NoReturn: + + # arguments + tgtpath: str = args.tgtpath + projectname: str = args.projectname + outputfilename: str = args.outputfilename + loglevel: str = args.loglevel + logfilename: Optional[str] = args.logfilename + logfilemode: str = args.logfilemode + + if not os.path.isdir(tgtpath): + print_error(f"Target directory {tgtpath} not found") + exit(1) + + targetpath = os.path.abspath(tgtpath) + projectpath = targetpath + contractpath = os.path.join(projectpath, "cch_contracts") + + if not UF.has_analysisresults_path(targetpath, projectname): + print_error( + f"No analysis results found for {projectname} in {targetpath}") + exit(1) + + set_logging( + loglevel, + targetpath, + logfilename=logfilename, + mode=logfilemode, + msg="c-project save-local-buffers invoked") + + capp = CApplication( + projectpath, projectname, targetpath, contractpath) + + jsonresult = BinCheckData(capp).to_json_result() + jsonok = JU.jsonok("save_bincheck_data", jsonresult.content) + with open(outputfilename, "w") as fp: + json.dump(jsonok, fp, indent=2) + + exit(0) + + def cproject_mk_headerfile(args: argparse.Namespace) -> NoReturn: # arguments diff --git a/chc/cmdline/chkc b/chc/cmdline/chkc index 35b3f10..4534bf0 100755 --- a/chc/cmdline/chkc +++ b/chc/cmdline/chkc @@ -1573,6 +1573,30 @@ def parse() -> argparse.Namespace: help="file mode for log file: append (a, default), or write (w)") cprojectmkheader.set_defaults(func=P.cproject_mk_headerfile) + # --- save-local-buffers + cprojectlocalbuffers = cprojectparsers.add_parser("save-local-buffers") + cprojectlocalbuffers.add_argument( + "tgtpath", help="directory that contains the parse/analysis results") + cprojectlocalbuffers.add_argument( + "projectname", help="name of the project") + cprojectlocalbuffers.add_argument( + "--outputfilename", "-o", required=True, + help="name of json output file") + cprojectlocalbuffers.add_argument( + "--loglevel", "-log", + choices=UL.LogLevel.options(), + default="NONE", + help="activate logging with given level (default to stderr)") + cprojectlocalbuffers.add_argument( + "--logfilename", + help="name of file to write log messages") + cprojectlocalbuffers.add_argument( + "--logfilemode", + choices=["a", "w"], + default="a", + help="file mode for log file: append (a, default), or write (w)") + cprojectlocalbuffers.set_defaults(func=P.cproject_save_local_buffers) + # --- cil-source cprojectcilsource = cprojectparsers.add_parser("cil-source") cprojectcilsource.add_argument( diff --git a/chc/reporting/BinCheckData.py b/chc/reporting/BinCheckData.py new file mode 100644 index 0000000..1ccb4e9 --- /dev/null +++ b/chc/reporting/BinCheckData.py @@ -0,0 +1,395 @@ +# ------------------------------------------------------------------------------ +# CodeHawk C Analyzer +# Author: Henny Sipma +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2026 Aarno Labs LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# ------------------------------------------------------------------------------ + +from abc import ABC, abstractmethod +from typing import Any, cast, ClassVar, Dict, List, TYPE_CHECKING + +from chc.jsoninterface.JSONResult import JSONResult + +if TYPE_CHECKING: + from chc.app.CApplication import CApplication + from chc.app.CCompInfo import CCompInfo + from chc.app.CFile import CFile + from chc.app.CFunction import CFunction + from chc.app.CTyp import ( + CTyp, CTypInt, CTypFloat, CTypPtr, CTypComp, CTypArray, CTypFun) + from chc.app.CVarInfo import CVarInfo + + +class BinCheckTypeClassification(ABC): + + def __init__(self, ctype: "CTyp") -> None: + self._ctype = ctype + + @property + def ctype(self) -> "CTyp": + return self._ctype + + @abstractmethod + def to_json_result(self) -> JSONResult: + ... + + +class BinCheckTypeInteger(BinCheckTypeClassification): + + def __init__(self, ctype: "CTypInt") -> None: + BinCheckTypeClassification.__init__(self, ctype) + + @property + def ctype(self) -> "CTypInt": + return cast("CTypInt", super().ctype) + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["typeclass"] = "int" + content["kind"] = self.ctype.ikind + return JSONResult("inttypeclass", content, "ok") + + +class BinCheckTypeFloat(BinCheckTypeClassification): + + def __init__(self, ctype: "CTypFloat") -> None: + BinCheckTypeClassification.__init__(self, ctype) + + @property + def ctype(self) -> "CTypFloat": + return cast("CTypFloat", super().ctype) + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["typeclass"] = "float" + content["kind"] = self.ctype.fkind + return JSONResult("floattypeclass", content, "ok") + + +class BinCheckTypePointer(BinCheckTypeClassification): + + def __init__(self, ctype: "CTyp") -> None: + BinCheckTypeClassification.__init__(self, ctype) + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["typeclass"] = "pointer" + content["is-function-pointer"] = self.ctype.is_function + return JSONResult("pointertypeclass", content, "ok") + + +class BinCheckTypeCompound(BinCheckTypeClassification): + + compinfokeys: ClassVar[List[int]] = [] + compinfos: ClassVar[Dict[int, "CCompInfo"]] = {} + + def __init__(self, ctype: "CTypComp") -> None: + BinCheckTypeClassification.__init__(self, ctype) + if not ctype.ckey in self.compinfokeys: + self.compinfos.setdefault(ctype.ckey, ctype.compinfo) + + @property + def ctype(self) -> "CTypComp": + return cast("CTypComp", super().ctype) + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["typeclass"] = "compound" + content["kind"] = "struct" if self.ctype.is_struct else "union" + content["ckey"] = self.ctype.ckey + content["name"] = self.ctype.name + return JSONResult("compoundtypeclass", content, "ok") + + +class BinCheckTypeArray(BinCheckTypeClassification): + + def __init__(self, ctype: "CTypArray") -> None: + BinCheckTypeClassification.__init__(self, ctype) + + @property + def ctype(self) -> "CTypArray": + return cast("CTypArray", super().ctype) + + @property + def elementtype(self) -> BinCheckTypeClassification: + return get_type_classification(self.ctype.array_basetype) + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["typeclass"] = "array" + content["elem-type"] = self.elementtype.to_json_result().content + content["elem-count"] = ( + self.ctype.size if self.ctype.has_array_constant_size() else 0) + return JSONResult("arraytypeclass", content, "ok") + + +class BinCheckTypeOther(BinCheckTypeClassification): + + def __init__(self, ctype: "CTyp") -> None: + BinCheckTypeClassification.__init__(self, ctype) + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["typeclass"] = "other" + content["name"] = str(self.ctype) + return JSONResult("othertypeclass", content, "ok") + + +class BinCheckTypeClassificationFactory: + + @staticmethod + def get_bincheck_type_classification( + ctype: "CTyp") -> BinCheckTypeClassification: + ctype = ctype.expand() + if ctype.is_int: + ictype = cast("CTypInt", ctype) + return BinCheckTypeInteger(ictype) + elif ctype.is_float: + fctype = cast("CTypFloat", ctype) + return BinCheckTypeFloat(fctype) + elif ctype.is_pointer: + return BinCheckTypePointer(ctype) + elif ctype.is_array: + actype = cast ("CTypArray", ctype) + return BinCheckTypeArray(actype) + elif ctype.is_comp: + cctype = cast ("CTypComp", ctype) + return BinCheckTypeCompound(cctype) + else: + return BinCheckTypeOther(ctype) + + +def get_type_classification(ctype: "CTyp") -> BinCheckTypeClassification: + return BinCheckTypeClassificationFactory.get_bincheck_type_classification( + ctype.expand()) + + +class BinCheckSignature: + + def __init__(self, ftype: "CTypFun") -> None: + self._ftype = ftype + + @property + def ftype(self) -> "CTypFun": + return self._ftype + + @property + def returntype(self) -> BinCheckTypeClassification: + return get_type_classification(self.ftype.return_type) + + @property + def argument_types(self) -> List[BinCheckTypeClassification]: + if self.ftype.funargs is None: + return [] + result: List[BinCheckTypeClassification] = [] + for arg in self.ftype.funargs.arguments: + bctype = get_type_classification(arg.typ) + result.append(bctype) + return result + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["return-type"] = self.returntype.to_json_result().content + content["is-vararg"] = self.ftype.is_vararg + content["argument-types"] = argtypes = [] + for argtype in self.argument_types: + argtypes.append(argtype.to_json_result().content) + return JSONResult("binchecksignature", content, "ok") + + +class BinCheckStructDef: + + def __init__(self, compinfo: "CCompInfo") -> None: + self._compinfo = compinfo + + @property + def compinfo(self) -> "CCompInfo": + return self._compinfo + + @property + def field_types(self) -> List[BinCheckTypeClassification]: + result: List[BinCheckTypeClassification] = [] + for fieldinfo in self.compinfo.fields: + bctype = get_type_classification(fieldinfo.ftype) + result.append(bctype) + return result + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["ckey"] = self.compinfo.ckey + content["name"] = self.compinfo.name + content["field-types"] = fields = [] + for f in self.field_types: + fields.append(f.to_json_result().content) + return JSONResult("bincheckstructdef", content, "ok") + + +class BinCheckLocalVar: + + def __init__(self, cvar: "CVarInfo") -> None: + self._cvar = cvar + + @property + def cvar(self) -> "CVarInfo": + return self._cvar + + @property + def ctype(self) -> BinCheckTypeClassification: + return get_type_classification(self.cvar.vtype) + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["vname"] = self.cvar.vname + content["vtype"] = self.ctype.to_json_result().content + return JSONResult("binchecklocalvar", content, "ok") + + +class BinCheckGlobalVar: + + def __init__(self, cvar: "CVarInfo") -> None: + self._cvar = cvar + + @property + def cvar(self) -> "CVarInfo": + return self._cvar + + @property + def ctype(self) -> BinCheckTypeClassification: + return get_type_classification(self.cvar.vtype) + + @property + def is_static(self) -> bool: + return self.cvar.is_static + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["vname"] = self.cvar.vname + content["vtype"] = self.ctype.to_json_result().content + content["is-static"] = self.is_static + return JSONResult("bincheckglobalvar", content, "ok") + + +class BinCheckFunctionData: + + def __init__(self, cfun: "CFunction") -> None: + self._cfun = cfun + + @property + def cfun(self) -> "CFunction": + return self._cfun + + @property + def localvars(self) -> List[BinCheckLocalVar]: + return [BinCheckLocalVar(cvar) for cvar in self.cfun.locals.values()] + + @property + def signature(self) -> BinCheckSignature: + ftype = cast("CTypFun", self.cfun.ftype) + return BinCheckSignature(ftype) + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["name"] = self.cfun.name + content["signature"] = self.signature.to_json_result().content + content["local-vars"] = locals = [] + for cvar in self.localvars: + locals.append(cvar.to_json_result().content) + return JSONResult("bincheckfunctiondata", content, "ok") + + +class BinCheckFileData: + + def __init__(self, cfile: "CFile") -> None: + self._cfile = cfile + self._functiondata: Dict[str, BinCheckFunctionData] = {} + self._staticvars: Dict[str, BinCheckGlobalVar] = {} + + @property + def cfile(self) -> "CFile": + return self._cfile + + @property + def functiondata(self) -> List[BinCheckFunctionData]: + return [BinCheckFunctionData(cfun) for cfun in self.cfile.functions.values()] + + @property + def staticvars(self) -> List[BinCheckGlobalVar]: + result: List[BinCheckGlobalVar] = [] + for cvar in self.cfile.cfileglobals.global_varinfo_names.values(): + if cvar.is_static: + result.append(BinCheckGlobalVar(cvar)) + return result + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["filename"] = self.cfile.name + content["static-vars"] = statics = [] + for cvar in self.staticvars: + statics.append(cvar.to_json_result().content) + content["functions"] = cfuns = [] + for cfun in self.functiondata: + cfuns.append(cfun.to_json_result().content) + content["structdefs"] = jstructdefs = [] + structdefs = list(BinCheckTypeCompound.compinfos.values()) + structdefkeys = list(BinCheckTypeCompound.compinfos.keys()) + while len(structdefs) > 0: + BinCheckTypeCompound.compinfokeys.extend(structdefkeys) + BinCheckTypeCompound.compinfos.clear() + bcstructdefs = [BinCheckStructDef(s) for s in sorted(structdefs, key=lambda s:s.ckey)] + jstructdefs.extend([s.to_json_result().content for s in bcstructdefs]) + structdefs = list(BinCheckTypeCompound.compinfos.values()) + structdefkeys = list(BinCheckTypeCompound.compinfos.keys()) + return JSONResult("bincheckfiledata", content, "ok") + + +class BinCheckData: + + def __init__(self, capp: "CApplication") -> None: + self._capp = capp + self._filedata: Dict[str, BinCheckFileData] = {} + self._globalvars: Dict[str, BinCheckGlobalVar] = {} + self._structdefs: Dict[int, BinCheckStructDef] = {} + + @property + def capp(self) -> "CApplication": + return self._capp + + @property + def filedata(self) -> List[BinCheckFileData]: + return [BinCheckFileData(cfile) for cfile in self.capp.files.values()] + + @property + def globalvars(self) -> List[BinCheckGlobalVar]: + result: List[BinCheckGlobalVar] = [] + for cvar in self.capp.declarations.varinfo_values(): + if not cvar.is_static and not cvar.vtype.is_function: + result.append(BinCheckGlobalVar(cvar)) + return result + + def to_json_result(self) -> JSONResult: + content: Dict[str, Any] = {} + content["projectname"] = self.capp.projectname + content["global-vars"] = [ + g.to_json_result().content for g in self.globalvars] + content["files"] = [f.to_json_result().content for f in self.filedata] + return JSONResult("bincheckdata", content, "ok") From 4ca6e41df48004050fab6ae07ebdc821c894bbc2 Mon Sep 17 00:00:00 2001 From: Henny Sipma Date: Thu, 22 Oct 2026 12:58:26 -0700 Subject: [PATCH 3/3] fix pycodestyle errors --- chc/cmdline/c_project/cprojectutil.py | 4 ++-- chc/cmdline/chkc | 2 +- chc/reporting/BinCheckData.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/chc/cmdline/c_project/cprojectutil.py b/chc/cmdline/c_project/cprojectutil.py index 04243c6..b268c25 100644 --- a/chc/cmdline/c_project/cprojectutil.py +++ b/chc/cmdline/c_project/cprojectutil.py @@ -360,7 +360,7 @@ def cproject_save_local_buffers(args: argparse.Namespace) -> NoReturn: outputfilename: str = args.outputfilename loglevel: str = args.loglevel logfilename: Optional[str] = args.logfilename - logfilemode: str = args.logfilemode + logfilemode: str = args.logfilemode if not os.path.isdir(tgtpath): print_error(f"Target directory {tgtpath} not found") @@ -380,7 +380,7 @@ def cproject_save_local_buffers(args: argparse.Namespace) -> NoReturn: targetpath, logfilename=logfilename, mode=logfilemode, - msg="c-project save-local-buffers invoked") + msg="c-project save-local-buffers invoked") capp = CApplication( projectpath, projectname, targetpath, contractpath) diff --git a/chc/cmdline/chkc b/chc/cmdline/chkc index 4534bf0..5848e0c 100755 --- a/chc/cmdline/chkc +++ b/chc/cmdline/chkc @@ -1594,7 +1594,7 @@ def parse() -> argparse.Namespace: "--logfilemode", choices=["a", "w"], default="a", - help="file mode for log file: append (a, default), or write (w)") + help="file mode for log file: append (a, default), or write (w)") cprojectlocalbuffers.set_defaults(func=P.cproject_save_local_buffers) # --- cil-source diff --git a/chc/reporting/BinCheckData.py b/chc/reporting/BinCheckData.py index 1ccb4e9..2ac5e5e 100644 --- a/chc/reporting/BinCheckData.py +++ b/chc/reporting/BinCheckData.py @@ -105,7 +105,7 @@ class BinCheckTypeCompound(BinCheckTypeClassification): def __init__(self, ctype: "CTypComp") -> None: BinCheckTypeClassification.__init__(self, ctype) - if not ctype.ckey in self.compinfokeys: + if ctype.ckey not in self.compinfokeys: self.compinfos.setdefault(ctype.ckey, ctype.compinfo) @property @@ -170,10 +170,10 @@ def get_bincheck_type_classification( elif ctype.is_pointer: return BinCheckTypePointer(ctype) elif ctype.is_array: - actype = cast ("CTypArray", ctype) + actype = cast("CTypArray", ctype) return BinCheckTypeArray(actype) elif ctype.is_comp: - cctype = cast ("CTypComp", ctype) + cctype = cast("CTypComp", ctype) return BinCheckTypeCompound(cctype) else: return BinCheckTypeOther(ctype)