1#!/usr/bin/env python3 2# coding: utf-8 3# 4# © 2023 Qualcomm Innovation Center, Inc. All rights reserved. 5# 6# SPDX-License-Identifier: BSD-3-Clause 7 8""" 9Run as a part of gitlab CI, after Parasoft reports have been generated. 10 11This script converts the Parasoft XML-format report to a Code Climate 12compatible json file, that gitlab code quality can interpret. 13""" 14 15import xml.etree.ElementTree as ET 16import json 17import argparse 18import sys 19import os 20import re 21 22argparser = argparse.ArgumentParser( 23 description="Convert Parasoft XML to Code Climate JSON") 24argparser.add_argument('input', type=argparse.FileType('r'), nargs='?', 25 default=sys.stdin, help="the Parasoft XML input") 26argparser.add_argument('--output', '-o', type=argparse.FileType('w'), 27 default=sys.stdout, help="the Code Climate JSON output") 28args = argparser.parse_args() 29 30tree = ET.parse(args.input) 31 32parasoft_viols = tree.findall(".//StdViol") + tree.findall(".//FlowViol") 33 34cc_viols = [] 35 36severity_map = { 37 1: "blocker", 38 2: "critical", 39 3: "major", 40 4: "minor", 41 5: "info", 42} 43 44deviation_map = { 45 # Deviation because the behaviour proscribed by the rule is exactly the 46 # intended behaviour of assert(): it prints the unexpanded expression. 47 'MISRAC2012-RULE_20_12-a': [ 48 (None, re.compile(r"parameter of potential macro 'assert'")), 49 ], 50 # False positives due to __c11 builtins taking int memory order arguments 51 # instead of enum in the Clang implementation. 52 'MISRAC2012-RULE_10_3-b': [ 53 (None, re.compile(r"number '2'.*'essentially Enum'.*" 54 r"'__c11_atomic_load'.*'essentially signed'")), 55 (None, re.compile(r"number '3'.*'essentially Enum'.*" 56 r"'__c11_atomic_(store'|exchange'|fetch_).*" 57 r"'essentially signed'")), 58 (None, re.compile(r"number '[45]'.*'essentially Enum'.*" 59 r"'__c11_atomic_compare_exchange_(strong|weak)'.*" 60 r"'essentially signed'")), 61 ], 62 # False positives with unknown cause: the return value of assert_if_const() 63 # is always used, to determine whether to call assert_failed() 64 'MISRAC2012-RULE_17_7-b': [ 65 (None, re.compile(r'"assert_if_const"')), 66 ], 67 'MISRAC2012-RULE_8_7-a': [ 68 # The could-be-static advisory rule is impractical to enforce for 69 # generated accessors, since the type system has no information about 70 # which accessors are used. 71 (re.compile(r'^build/.*/accessors\.c$'), None), 72 # The smccc module specifically has events that are only triggered by 73 # handlers for other events. 74 (re.compile(r'^build/.*/events/src/smccc\.c$'), None), 75 # The object module has type-specific APIs that are only used directly 76 # for some specific object types, and otherwise are called only by the 77 # type-generic APIs defined in the same file. 78 (re.compile(r'^build/.*/objects/.*\.c$'), None), 79 ], 80 # Invariant expressions are expected and unavoidable in generated event 81 # triggers because it is not possible to remove error result types from 82 # handlers that never return errors. 83 'MISRAC2012-RULE_14_3-ac': [ 84 (re.compile(r'^build/.*/events/src/.*\.c$'), None), 85 ], 86 # Could-be-const pointers are expected and unavoidable in generated event 87 # triggers because the object may or may not be modified depending on the 88 # handlers and the module configuration. The const qualifier is used to 89 # specify whether the handlers are allowed to modify the objects, rather 90 # than whether they actually do. 91 'MISRAC2012-RULE_8_13-a': [ 92 (re.compile(r'^build/.*/events/src/.*\.c$'), None), 93 ], 94 # The generated type-generic object functions terminate non-empty default 95 # clauses with a _Noreturn function, panic(), to indicate that the object 96 # type is invalid. There is an approved deviation for this, and in any 97 # case these rules are downgraded to advisory in generated code. 98 'MISRAC2012-RULE_16_1-d': [ 99 (re.compile(r'^build/.*/objects/.*\.c$'), None), 100 ], 101 'MISRAC2012-RULE_16_3-b': [ 102 (re.compile(r'^build/.*/objects/.*\.c$'), None), 103 ], 104 # False positive due to a builtin sizeof variant that does not evaluate its 105 # argument, so there is no uninitialised use. 106 'MISRAC2012-RULE_9_1-a': [ 107 (None, re.compile(r'passed to "__builtin_object_size"')), 108 ], 109 'MISRAC2012-RULE_1_3-b': [ 110 (None, re.compile(r'passed to "__builtin_object_size"')), 111 ], 112 # Deviation because casting a pointer to _Atomic to a pointer that can't be 113 # dereferenced at all (const void *) is reasonably safe, and is needed for 114 # certain builtin functions where the compiler knows the real underlying 115 # object type anyway (e.g. __builtin_object_size) or where the object type 116 # does not matter (e.g. __builtin_prefetch). 117 'MISRAC2012-RULE_11_8-a': [ 118 (None, re.compile(r"to the 'const void \*' type which removes the " 119 r"'_Atomic' qualifiers")), 120 ], 121 # Compliance with rule 21.25 would have a significant performance impact. 122 # All existing uses have been thoroughly analysed and tested, so we will 123 # seek a project-wide deviation for this rule. 124 'MISRAC2012-RULE_21_25-a': [ 125 (None, None), 126 ], 127} 128 129 130def matches_deviation(v): 131 rule = v.attrib['rule'] 132 if rule not in deviation_map: 133 return False 134 135 msg = v.attrib['msg'] 136 path = v.attrib['locFile'].split(os.sep, 2)[2] 137 138 def check_constraint(constraint, value): 139 if constraint is None: 140 return True 141 try: 142 return constraint.search(value) 143 except AttributeError: 144 return constraint == value 145 146 for d_path, d_msg in deviation_map[rule]: 147 if check_constraint(d_path, path) and check_constraint(d_msg, msg): 148 return True 149 150 return False 151 152 153cc_viols = [ 154 ({ 155 "type": "issue", 156 "categories": ["Bug Risk"], 157 "severity": ('info' if matches_deviation(v) 158 else severity_map[int(v.attrib['sev'])]), 159 "check_name": v.attrib['rule'], 160 "description": (v.attrib['msg'] + '. ' + 161 v.attrib['rule.header'] + '. (' + 162 v.attrib['rule'] + ')'), 163 "fingerprint": v.attrib['unbViolId'], 164 "location": { 165 "path": v.attrib['locFile'].split(os.sep, 2)[2], 166 "lines": { 167 "begin": int(v.attrib['locStartln']), 168 "end": int(v.attrib['locEndLn']) 169 } 170 } 171 }) 172 for v in parasoft_viols] 173 174args.output.write(json.dumps(cc_viols)) 175args.output.close() 176