forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathlint-cppcheck-dash.py
More file actions
executable file
·137 lines (118 loc) · 4.84 KB
/
lint-cppcheck-dash.py
File metadata and controls
executable file
·137 lines (118 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env python3
#
# Copyright (c) 2019 The Bitcoin Core developers
# Copyright (c) 2025 The Dash Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
# Run cppcheck for dash specific files
import multiprocessing
import os
import re
import subprocess
import sys
os.environ['LC_ALL'] = 'C'
ALWAYS_ENABLED_WARNINGS = (
"Class '.*' has a constructor with 1 argument that is not explicit.",
"Struct '.*' has a constructor with 1 argument that is not explicit.",
"Function parameter '.*' should be passed by const reference.",
"Comparison of modulo result is predetermined",
"Local variable '.*' shadows outer argument",
"Redundant initialization for '.*'. The initialized value is overwritten before it is read.",
"Dereferencing '.*' after it is deallocated / released",
"The scope of the variable '.*' can be reduced.",
"Parameter '.*' can be declared with const",
"Variable '.*' can be declared with const",
"Variable '.*' is assigned a value that is never used.",
"Unused variable",
"The function '.*' overrides a function in a base class but is not marked with a 'override' specifier.",
# Enable to catch all warnings
# ".*",
)
SUPPRESSED_WARNINGS = (
"src/stacktraces.cpp:.*: .*: Parameter 'info' can be declared as pointer to const",
"src/stacktraces.cpp:.*: note: You might need to cast the function pointer here",
# current version of cppcheck fails with this error if exhaustive level is used
# TODO: remove with a newer version
"warning: Internal error: Child process crashed with signal 6",
# The following can be useful to ignore when the catch all is used
# "Consider performing initialization in initialization list.",
"Consider using std::transform algorithm instead of a raw loop.",
"Consider using std::accumulate algorithm instead of a raw loop.",
"Consider using std::any_of algorithm instead of a raw loop.",
"Consider using std::copy_if algorithm instead of a raw loop.",
# "Consider using std::count_if algorithm instead of a raw loop.",
# "Consider using std::find_if algorithm instead of a raw loop.",
# "Member variable '.*' is not initialized in the constructor.",
"unusedFunction",
"unknownMacro",
"unusedStructMember",
)
def main():
warnings = []
exit_code = 0
try:
subprocess.check_output(['cppcheck', '--version'])
except FileNotFoundError:
print("Skipping cppcheck linting since cppcheck is not installed.")
sys.exit(0)
with open('test/util/data/non-backported.txt', 'r', encoding='utf-8') as f:
patterns = [line.strip() for line in f if line.strip()]
files_output = subprocess.check_output(['git', 'ls-files', '--'] + patterns, universal_newlines=True, encoding="utf8")
files = [f.strip() for f in files_output.splitlines() if f.strip()]
always_enabled_regexp = '|'.join(ALWAYS_ENABLED_WARNINGS)
suppressed_regexp = '|'.join(SUPPRESSED_WARNINGS)
files_regexp = '|'.join(re.escape(f) for f in files)
script_dir = os.path.dirname(os.path.abspath(__file__))
cache_dir = os.environ.get('CACHE_DIR')
if cache_dir:
cppcheck_dir = os.path.join(cache_dir, 'cppcheck')
else:
cppcheck_dir = os.path.join(script_dir, '.cppcheck')
os.makedirs(cppcheck_dir, exist_ok=True)
cppcheck_cmd = [
'cppcheck',
'--enable=all',
'--inline-suppr',
'--suppress=missingIncludeSystem',
f'--cppcheck-build-dir={cppcheck_dir}',
'-j', str(multiprocessing.cpu_count()),
'--language=c++',
'--std=c++20',
'--template=gcc',
'--check-level=exhaustive',
'-D__cplusplus',
'-DENABLE_WALLET',
'-DCLIENT_VERSION_BUILD',
'-DCLIENT_VERSION_IS_RELEASE',
'-DCLIENT_VERSION_MAJOR',
'-DCLIENT_VERSION_MINOR',
'-DCOPYRIGHT_YEAR',
'-DDEBUG',
'-DUSE_EPOLL',
'-DCHAR_BIT=8',
'-I', 'src/',
'-q',
] + files
dependencies_output = subprocess.run(
cppcheck_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
unique_sorted_lines = sorted(set(dependencies_output.stdout.splitlines()))
for line in unique_sorted_lines:
if not re.search(files_regexp, line):
continue
if re.search(always_enabled_regexp, line) or not re.search(suppressed_regexp, line):
warnings.append(line)
if warnings:
print('\n'.join(warnings))
print()
print("Advice not applicable in this specific case? Add an exception by updating")
print(f"SUPPRESSED_WARNINGS in {__file__}")
# Uncomment to enforce the linter / comment to run locally
exit_code = 1
sys.exit(exit_code)
if __name__ == "__main__":
main()