Skip to content

Commit 5b4dd36

Browse files
mbaludaCopilot
andcommitted
Simplify MISRA C rule matching and reporting
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3a9b8cb commit 5b4dd36

6 files changed

Lines changed: 63 additions & 40 deletions

File tree

‎c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql‎

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,17 @@ import cpp
1818
import codingstandards.c.misra
1919
import semmle.code.cpp.commons.Assertions
2020

21-
/** A Standard Library termination function prohibited by this rule. */
22-
class BannedFunction extends Function {
23-
BannedFunction() {
24-
this.getName() = ["abort", "exit", "system"] and
25-
this.getFile().getBaseName() = "stdlib.h"
26-
}
27-
}
28-
29-
from FunctionCall fc, Function f, string message
21+
from FunctionCall fc, string message
3022
where
3123
not isExcluded(fc, BannedPackage::terminationFunctionsOfStdlibhUsedQuery()) and
32-
f = fc.getTarget() and
33-
(
34-
f instanceof BannedFunction
35-
or
36-
any(Options options).exits(f) and
37-
exists(LibcAssert assertion | assertion.getAnExpandedElement() = fc)
38-
) and
39-
(
40-
if f instanceof BannedFunction
24+
exists(Function f | f = fc.getTarget() |
25+
if f.hasGlobalOrStdName(["abort", "exit", "system"]) and f.getFile().getBaseName() = "stdlib.h"
4126
then message = "Call to banned function " + f.getName() + "."
42-
else message = "Enabled assertion calls non-returning function '" + f.getName() + "'."
27+
else
28+
exists(Options options, LibcAssert assertion |
29+
options.exits(f) and
30+
assertion.getAnExpandedElement() = fc and
31+
message = "Enabled assertion calls non-returning function '" + f.getName() + "'."
32+
)
4333
)
4434
select fc, message
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| test.cpp:12:3:12:7 | call to abort | Call to banned function abort. |
2+
| test.cpp:13:3:13:6 | call to exit | Call to banned function exit. |
3+
| test.cpp:14:3:14:8 | call to system | Call to banned function system. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace application {
2+
void abort();
3+
void exit(int);
4+
int system(const char *);
5+
} // namespace application
6+
7+
namespace std {
8+
void abort();
9+
void exit(int);
10+
int system(const char *);
11+
} // namespace std
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// semmle-extractor-options:--clang -std=c++14 -nostdinc
2+
#include "headers/stdlib.h"
3+
4+
using std::abort;
5+
using std::exit;
6+
using std::system;
7+
8+
void qualified_library_calls() {
9+
application::abort(); // COMPLIANT
10+
application::exit(0); // COMPLIANT
11+
application::system(""); // COMPLIANT
12+
abort(); // NON_COMPLIANT
13+
exit(0); // NON_COMPLIANT
14+
system(""); // NON_COMPLIANT
15+
}

‎scripts/release/create_supported_rules_list.py‎

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@
44
import json
55
from utils import *
66

7+
8+
def get_applicable_standards(standard_name, query):
9+
"""Yield applicable standards, including unchanged queries reused by MISRA C:2025."""
10+
if standard_name not in {"MISRA-C-2012", "MISRA-C-2025"}:
11+
yield standard_name
12+
return
13+
14+
tags = query["tags"]
15+
if any(tag.startswith("external/misra/c/2012/") for tag in tags):
16+
yield "MISRA-C-2012"
17+
if "external/misra/legacy" not in tags:
18+
yield "MISRA-C-2025"
19+
20+
721
help_statement = """
822
Usage: {script_name}
923
@@ -37,30 +51,19 @@
3751
rule_packages_file_path = repo_root.joinpath('rule_packages', language_name)
3852
for rule_package_file_name in os.listdir(rule_packages_file_path):
3953
try:
40-
rule_package_file = open(rule_packages_file_path.joinpath(rule_package_file_name), "r")
54+
package_definition = json.loads(
55+
rule_packages_file_path.joinpath(rule_package_file_name).read_text()
56+
)
4157
except PermissionError:
4258
print("Error: No permission to read the rule package file located at '" + str(rule_package_file_name) + "'")
4359
sys.exit(1)
44-
else:
45-
with rule_package_file:
46-
package_definition = json.load(rule_package_file)
47-
for standard_name, rules in package_definition.items():
48-
for rule_id, rule_details in rules.items():
49-
for query in rule_details["queries"]:
50-
if standard_name in {"MISRA-C-2012", "MISRA-C-2025"}:
51-
applicable_standards = []
52-
tags = query["tags"]
53-
if any(tag.startswith("external/misra/c/2012/") for tag in tags):
54-
applicable_standards.append("MISRA-C-2012")
55-
# Current suites reuse unchanged queries but exclude legacy variants.
56-
if "external/misra/legacy" not in tags:
57-
applicable_standards.append("MISRA-C-2025")
58-
else:
59-
applicable_standards = [standard_name]
60-
for applicable_standard in applicable_standards:
61-
queries = rules_covered[applicable_standard].setdefault(rule_id, [])
62-
if query["short_name"] not in queries:
63-
queries.append(query["short_name"])
60+
for standard_name, rules in package_definition.items():
61+
for rule_id, rule_details in rules.items():
62+
for query in rule_details["queries"]:
63+
for applicable_standard in get_applicable_standards(standard_name, query):
64+
queries = rules_covered[applicable_standard].setdefault(rule_id, [])
65+
if query["short_name"] not in queries:
66+
queries.append(query["short_name"])
6467

6568

6669
if is_csv:

0 commit comments

Comments
 (0)