cppcheck-py 2.9__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cppcheck/__init__.py +55 -0
- cppcheck/cppcheck-2.9.tar.gz +0 -0
- cppcheck/data/addons/__init__.py +0 -0
- cppcheck/data/addons/cppcheck.py +39 -0
- cppcheck/data/addons/cppcheckdata.py +1529 -0
- cppcheck/data/addons/findcasts.py +33 -0
- cppcheck/data/addons/misc.py +163 -0
- cppcheck/data/addons/misra.py +4728 -0
- cppcheck/data/addons/misra_9.py +513 -0
- cppcheck/data/addons/naming.py +91 -0
- cppcheck/data/addons/namingng.py +253 -0
- cppcheck/data/addons/runaddon.py +12 -0
- cppcheck/data/addons/threadsafety.py +39 -0
- cppcheck/data/addons/y2038.py +252 -0
- cppcheck/data/cfg/avr.cfg +431 -0
- cppcheck/data/cfg/bento4.cfg +142 -0
- cppcheck/data/cfg/boost.cfg +1268 -0
- cppcheck/data/cfg/bsd.cfg +546 -0
- cppcheck/data/cfg/cairo.cfg +88 -0
- cppcheck/data/cfg/cppcheck-lib.cfg +72 -0
- cppcheck/data/cfg/cppunit.cfg +48 -0
- cppcheck/data/cfg/daca.cfg +31 -0
- cppcheck/data/cfg/dpdk.cfg +11 -0
- cppcheck/data/cfg/embedded_sql.cfg +4 -0
- cppcheck/data/cfg/emscripten.cfg +7 -0
- cppcheck/data/cfg/ginac.cfg +12848 -0
- cppcheck/data/cfg/gnu.cfg +1626 -0
- cppcheck/data/cfg/googletest.cfg +60 -0
- cppcheck/data/cfg/gtk.cfg +20584 -0
- cppcheck/data/cfg/icu.cfg +6 -0
- cppcheck/data/cfg/kde.cfg +51 -0
- cppcheck/data/cfg/libcerror.cfg +128 -0
- cppcheck/data/cfg/libcurl.cfg +487 -0
- cppcheck/data/cfg/libsigc++.cfg +36 -0
- cppcheck/data/cfg/lua.cfg +316 -0
- cppcheck/data/cfg/mfc.cfg +256 -0
- cppcheck/data/cfg/microsoft_atl.cfg +40 -0
- cppcheck/data/cfg/microsoft_sal.cfg +491 -0
- cppcheck/data/cfg/microsoft_unittest.cfg +20 -0
- cppcheck/data/cfg/motif.cfg +318 -0
- cppcheck/data/cfg/nspr.cfg +38 -0
- cppcheck/data/cfg/ntl.cfg +6907 -0
- cppcheck/data/cfg/opencv2.cfg +132 -0
- cppcheck/data/cfg/opengl.cfg +580 -0
- cppcheck/data/cfg/openmp.cfg +157 -0
- cppcheck/data/cfg/openssl.cfg +174 -0
- cppcheck/data/cfg/pcre.cfg +165 -0
- cppcheck/data/cfg/posix.cfg +6252 -0
- cppcheck/data/cfg/python.cfg +674 -0
- cppcheck/data/cfg/qt.cfg +5255 -0
- cppcheck/data/cfg/ruby.cfg +142 -0
- cppcheck/data/cfg/sdl.cfg +354 -0
- cppcheck/data/cfg/sfml.cfg +301 -0
- cppcheck/data/cfg/sqlite3.cfg +1527 -0
- cppcheck/data/cfg/std.cfg +8716 -0
- cppcheck/data/cfg/tinyxml2.cfg +56 -0
- cppcheck/data/cfg/vcl.cfg +4 -0
- cppcheck/data/cfg/windows.cfg +17941 -0
- cppcheck/data/cfg/wxsqlite3.cfg +1955 -0
- cppcheck/data/cfg/wxsvg.cfg +16905 -0
- cppcheck/data/cfg/wxwidgets.cfg +16464 -0
- cppcheck/data/cfg/zlib.cfg +1450 -0
- cppcheck/data/cppcheck-htmlreport +986 -0
- cppcheck/data/cppcheck.exe +0 -0
- cppcheck/data/platforms/aix_ppc64.xml +17 -0
- cppcheck/data/platforms/arm32-wchar_t2.xml +17 -0
- cppcheck/data/platforms/arm32-wchar_t4.xml +17 -0
- cppcheck/data/platforms/arm64-wchar_t2.xml +17 -0
- cppcheck/data/platforms/arm64-wchar_t4.xml +17 -0
- cppcheck/data/platforms/avr8.xml +18 -0
- cppcheck/data/platforms/cray_sv1.xml +17 -0
- cppcheck/data/platforms/elbrus-e1cp.xml +18 -0
- cppcheck/data/platforms/mips32.xml +18 -0
- cppcheck/data/platforms/msp430_eabi_large_datamodel.xml +17 -0
- cppcheck/data/platforms/pic16.xml +18 -0
- cppcheck/data/platforms/pic8-enhanced.xml +18 -0
- cppcheck/data/platforms/pic8.xml +18 -0
- cppcheck/data/platforms/unix32-unsigned.xml +17 -0
- cppcheck/data/platforms/unix64-unsigned.xml +17 -0
- cppcheck_py-2.9.dist-info/METADATA +76 -0
- cppcheck_py-2.9.dist-info/RECORD +84 -0
- cppcheck_py-2.9.dist-info/WHEEL +5 -0
- cppcheck_py-2.9.dist-info/entry_points.txt +4 -0
- cppcheck_py-2.9.dist-info/licenses/LICENSE.md +191 -0
|
@@ -0,0 +1,4728 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
# MISRA C 2012 checkers
|
|
4
|
+
# Partially reused for "MISRA C++ 2008" checking
|
|
5
|
+
#
|
|
6
|
+
# Example usage of this addon (scan a sourcefile main.cpp)
|
|
7
|
+
# cppcheck --dump main.cpp
|
|
8
|
+
# python misra.py --rule-texts=<path-to-rule-texts> main.cpp.dump
|
|
9
|
+
#
|
|
10
|
+
# Limitations: This addon is released as open source. Rule texts can't be freely
|
|
11
|
+
# distributed. https://www.misra.org.uk/forum/viewtopic.php?f=56&t=1189
|
|
12
|
+
#
|
|
13
|
+
# The MISRA standard documents may be obtained from https://www.misra.org.uk
|
|
14
|
+
#
|
|
15
|
+
# Total number of rules: 143
|
|
16
|
+
|
|
17
|
+
from __future__ import print_function
|
|
18
|
+
|
|
19
|
+
import cppcheckdata
|
|
20
|
+
import itertools
|
|
21
|
+
import json
|
|
22
|
+
import sys
|
|
23
|
+
import re
|
|
24
|
+
import os
|
|
25
|
+
import argparse
|
|
26
|
+
import codecs
|
|
27
|
+
import string
|
|
28
|
+
import copy
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
from itertools import izip as zip
|
|
32
|
+
except ImportError:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
import misra_9
|
|
36
|
+
|
|
37
|
+
def grouped(iterable, n):
|
|
38
|
+
"""s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."""
|
|
39
|
+
return zip(*[iter(iterable)] * n)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
INT_TYPES = ['bool', 'char', 'short', 'int', 'long', 'long long']
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
STDINT_TYPES = ['%s%d_t' % (n, v) for n, v in itertools.product(
|
|
46
|
+
['int', 'uint', 'int_least', 'uint_least', 'int_fast', 'uint_fast'],
|
|
47
|
+
[8, 16, 32, 64])]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
typeBits = {
|
|
51
|
+
'CHAR': None,
|
|
52
|
+
'SHORT': None,
|
|
53
|
+
'INT': None,
|
|
54
|
+
'LONG': None,
|
|
55
|
+
'LONG_LONG': None,
|
|
56
|
+
'POINTER': None
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def isUnsignedType(ty):
|
|
61
|
+
return ty == 'unsigned' or ty.startswith('uint')
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def simpleMatch(token, pattern):
|
|
65
|
+
return cppcheckdata.simpleMatch(token, pattern)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def rawlink(rawtoken):
|
|
69
|
+
if rawtoken.str == '}':
|
|
70
|
+
indent = 0
|
|
71
|
+
while rawtoken:
|
|
72
|
+
if rawtoken.str == '}':
|
|
73
|
+
indent = indent + 1
|
|
74
|
+
elif rawtoken.str == '{':
|
|
75
|
+
indent = indent - 1
|
|
76
|
+
if indent == 0:
|
|
77
|
+
break
|
|
78
|
+
rawtoken = rawtoken.previous
|
|
79
|
+
else:
|
|
80
|
+
rawtoken = None
|
|
81
|
+
return rawtoken
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Identifiers described in Section 7 "Library" of C90 Standard
|
|
85
|
+
# Based on ISO/IEC9899:1990 Annex D -- Library summary and
|
|
86
|
+
# Annex E -- Implementation limits.
|
|
87
|
+
C90_STDLIB_IDENTIFIERS = {
|
|
88
|
+
# D.1 Errors
|
|
89
|
+
'errno.h': ['EDOM', 'ERANGE', 'errno'],
|
|
90
|
+
# D.2 Common definitions
|
|
91
|
+
'stddef.h': ['NULL', 'offsetof', 'ptrdiff_t', 'size_t', 'wchar_t'],
|
|
92
|
+
# D.3 Diagnostics
|
|
93
|
+
'assert.h': ['NDEBUG', 'assert'],
|
|
94
|
+
# D.4 Character handling
|
|
95
|
+
'ctype.h': [
|
|
96
|
+
'isalnum', 'isalpha', 'isblank', 'iscntrl', 'isdigit',
|
|
97
|
+
'isgraph', 'islower', 'isprint', 'ispunct', 'isspace',
|
|
98
|
+
'isupper', 'isxdigit', 'tolower', 'toupper',
|
|
99
|
+
],
|
|
100
|
+
# D.5 Localization
|
|
101
|
+
'locale.h': [
|
|
102
|
+
'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY',
|
|
103
|
+
'LC_NUMERIC', 'LC_TIME', 'NULL', 'lconv',
|
|
104
|
+
'setlocale', 'localeconv',
|
|
105
|
+
],
|
|
106
|
+
# D.6 Mathematics
|
|
107
|
+
'math.h': [
|
|
108
|
+
'HUGE_VAL', 'acos', 'asin' , 'atan2', 'cos', 'sin', 'tan', 'cosh',
|
|
109
|
+
'sinh', 'tanh', 'exp', 'frexp', 'ldexp', 'log', 'loglO', 'modf',
|
|
110
|
+
'pow', 'sqrt', 'ceil', 'fabs', 'floor', 'fmod',
|
|
111
|
+
],
|
|
112
|
+
# D.7 Nonlocal jumps
|
|
113
|
+
'setjmp.h': ['jmp_buf', 'setjmp', 'longjmp'],
|
|
114
|
+
# D.8 Signal handling
|
|
115
|
+
'signal.h': [
|
|
116
|
+
'sig_atomic_t', 'SIG_DFL', 'SIG_ERR', 'SIG_IGN', 'SIGABRT', 'SIGFPE',
|
|
117
|
+
'SIGILL', 'SIGINT', 'SIGSEGV', 'SIGTERM', 'signal', 'raise',
|
|
118
|
+
],
|
|
119
|
+
# D.9 Variable arguments
|
|
120
|
+
'stdarg.h': ['va_list', 'va_start', 'va_arg', 'va_end'],
|
|
121
|
+
# D.10 Input/output
|
|
122
|
+
'stdio.h': [
|
|
123
|
+
'_IOFBF', '_IOLBF', '_IONBF', 'BUFSIZ', 'EOF', 'FILE', 'FILENAME_MAX',
|
|
124
|
+
'FOPEN_MAX', 'fpos_t', 'L_tmpnam', 'NULL', 'SEEK_CUR', 'SEEK_END',
|
|
125
|
+
'SEEK_SET', 'size_t', 'stderr', 'stdin', 'stdout', 'TMP_MAX',
|
|
126
|
+
'remove', 'rename', 'tmpfile', 'tmpnam', 'fclose', 'fflush', 'fopen',
|
|
127
|
+
'freopen', 'setbuf', 'setvbuf', 'fprintf', 'fscanf', 'printf',
|
|
128
|
+
'scanf', 'sprintf', 'sscanf', 'vfprintf', 'vprintf', 'vsprintf',
|
|
129
|
+
'fgetc', 'fgets', 'fputc', 'fputs', 'getc', 'getchar', 'gets', 'putc',
|
|
130
|
+
'putchar', 'puts', 'ungetc', 'fread', 'fwrite', 'fgetpos', 'fseek',
|
|
131
|
+
'fsetpos', 'rewind', 'clearerr', 'feof', 'ferror', 'perror',
|
|
132
|
+
],
|
|
133
|
+
# D.11 General utilities
|
|
134
|
+
'stdlib.h': [
|
|
135
|
+
'EXIT_FAILURE', 'EXIT_SUCCESS', 'MB_CUR_MAX', 'NULL', 'RAND_MAX',
|
|
136
|
+
'div_t', 'ldiv_t', 'wchar_t', 'atof', 'atoi', 'strtod', 'rand',
|
|
137
|
+
'srand', 'calloc', 'free', 'malloc', 'realloc', 'abort', 'atexit',
|
|
138
|
+
'exit', 'getenv', 'system', 'bsearch', 'qsort', 'abs', 'div', 'ldiv',
|
|
139
|
+
'mblen', 'mbtowc', 'wctomb', 'mbstowcs', 'wcstombs',
|
|
140
|
+
],
|
|
141
|
+
# D.12 String handling
|
|
142
|
+
'string.h': [
|
|
143
|
+
'NULL', 'size_t', 'memcpy', 'memmove', 'strcpy', 'strncpy', 'strcat',
|
|
144
|
+
'strncat', 'memcmp', 'strcmp', 'strcoll', 'strncmp', 'strxfrm',
|
|
145
|
+
'memchr', 'strchr', 'strcspn', 'strpbrk', 'strrchr', 'strspn',
|
|
146
|
+
'strstr', 'strtok', 'memset', 'strerror', 'strlen',
|
|
147
|
+
],
|
|
148
|
+
# D.13 Date and time
|
|
149
|
+
'time.h': [
|
|
150
|
+
'CLK_TCK', 'NULL', 'clock_t', 'time_t', 'size_t', 'tm', 'clock',
|
|
151
|
+
'difftime', 'mktime', 'time', 'asctime', 'ctime', 'gmtime',
|
|
152
|
+
'localtime', 'strftime',
|
|
153
|
+
],
|
|
154
|
+
# Annex E: Implementation limits
|
|
155
|
+
'limits.h': [
|
|
156
|
+
'CHAR_BIT', 'SCHAR_MIN', 'SCHAR_MAX', 'UCHAR_MAX', 'CHAR_MIN',
|
|
157
|
+
'CHAR_MAX', 'MB_LEN_MAX', 'SHRT_MIN', 'SHRT_MAX', 'USHRT_MAX',
|
|
158
|
+
'INT_MIN', 'INT_MAX', 'UINT_MAX', 'LONG_MIN', 'LONG_MAX', 'ULONG_MAX',
|
|
159
|
+
],
|
|
160
|
+
'float.h': [
|
|
161
|
+
'FLT_ROUNDS', 'FLT_RADIX', 'FLT_MANT_DIG', 'DBL_MANT_DIG',
|
|
162
|
+
'LDBL_MANT_DIG', 'DECIMAL_DIG', 'FLT_DIG', 'DBL_DIG', 'LDBL_DIG',
|
|
163
|
+
'DBL_MIN_EXP', 'LDBL_MIN_EXP', 'FLT_MIN_10_EXP', 'DBL_MIN_10_EXP',
|
|
164
|
+
'LDBL_MIN_10_EXP', 'FLT_MAX_EXP', 'DBL_MAX_EXP', 'LDBL_MAX_EXP',
|
|
165
|
+
'FLT_MAX_10_EXP', 'DBL_MAX_10_EXP', 'LDBL_MAX_10_EXP', 'FLT_MAX',
|
|
166
|
+
'DBL_MAX', 'LDBL_MAX', 'FLT_MIN', 'DBL_MIN', 'LDBL_MIN',
|
|
167
|
+
'FLT_EPSILON', 'DBL_EPSILON', 'LDBL_EPSILON'
|
|
168
|
+
],
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# Identifiers described in Section 7 "Library" of C99 Standard
|
|
173
|
+
# Based on ISO/IEC 9899 WF14/N1256 Annex B -- Library summary
|
|
174
|
+
C99_STDLIB_IDENTIFIERS = {
|
|
175
|
+
# B.1 Diagnostics
|
|
176
|
+
'assert.h': C90_STDLIB_IDENTIFIERS['assert.h'],
|
|
177
|
+
# B.2 Complex
|
|
178
|
+
'complex.h': [
|
|
179
|
+
'complex', 'imaginary', 'I', '_Complex_I', '_Imaginary_I',
|
|
180
|
+
'CX_LIMITED_RANGE',
|
|
181
|
+
'cacos', 'cacosf', 'cacosl',
|
|
182
|
+
'casin', 'casinf', 'casinl',
|
|
183
|
+
'catan', 'catanf', 'catanl',
|
|
184
|
+
'ccos', 'ccosf', 'ccosl',
|
|
185
|
+
'csin', 'csinf', 'csinl',
|
|
186
|
+
'ctan', 'ctanf', 'ctanl',
|
|
187
|
+
'cacosh', 'cacoshf', 'cacoshl',
|
|
188
|
+
'casinh', 'casinhf', 'casinhl',
|
|
189
|
+
'catanh', 'catanhf', 'catanhl',
|
|
190
|
+
'ccosh', 'ccoshf', 'ccoshl',
|
|
191
|
+
'csinh', 'csinhf', 'csinhl',
|
|
192
|
+
'ctanh', 'ctanhf', 'ctanhl',
|
|
193
|
+
'cexp', 'cexpf', 'cexpl',
|
|
194
|
+
'clog', 'clogf', 'clogl',
|
|
195
|
+
'cabs', 'cabsf', 'cabsl',
|
|
196
|
+
'cpow', 'cpowf', 'cpowl',
|
|
197
|
+
'csqrt', 'csqrtf', 'csqrtl',
|
|
198
|
+
'carg', 'cargf', 'cargl',
|
|
199
|
+
'cimag', 'cimagf', 'cimagl',
|
|
200
|
+
'conj', 'conjf', 'conjl',
|
|
201
|
+
'cproj', 'cprojf', 'cprojl',
|
|
202
|
+
'creal', 'crealf', 'creall',
|
|
203
|
+
],
|
|
204
|
+
# B.3 Character handling
|
|
205
|
+
'ctype.h': C90_STDLIB_IDENTIFIERS['ctype.h'],
|
|
206
|
+
# B.4 Errors
|
|
207
|
+
'errno.h': C90_STDLIB_IDENTIFIERS['errno.h'] + ['EILSEQ'],
|
|
208
|
+
# B.5 Floating-point environment
|
|
209
|
+
'fenv.h': [
|
|
210
|
+
'fenv_t', 'FE_OVERFLOW', 'FE_TOWARDZERO',
|
|
211
|
+
'fexcept_t', 'FE_UNDERFLOW', 'FE_UPWARD',
|
|
212
|
+
'FE_DIVBYZERO', 'FE_ALL_EXCEPT', 'FE_DFL_ENV',
|
|
213
|
+
'FE_INEXACT', 'FE_DOWNWARD',
|
|
214
|
+
'FE_INVALID', 'FE_TONEAREST',
|
|
215
|
+
'FENV_ACCESS',
|
|
216
|
+
'feclearexcept', 'fegetexceptflag', 'fegetround',
|
|
217
|
+
'fesetround', 'fegetenv', 'feholdexcept',
|
|
218
|
+
'fesetenv', 'feupdateenv',
|
|
219
|
+
],
|
|
220
|
+
# B.6 Characteristics of floating types
|
|
221
|
+
'float.h': C90_STDLIB_IDENTIFIERS['float.h'] + ['FLT_EVAL_METHOD'],
|
|
222
|
+
# B.7 Format conversion of integer types
|
|
223
|
+
'inttypes.h': [
|
|
224
|
+
'imaxdiv_t', 'imaxabs', 'imaxdiv', 'strtoimax',
|
|
225
|
+
'strtoumax', 'wcstoimax', 'wcstoumax',
|
|
226
|
+
],
|
|
227
|
+
# B.8 Alternative spellings
|
|
228
|
+
'iso646.h': [
|
|
229
|
+
'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 'not_eq',
|
|
230
|
+
'or', 'or_eq', 'xor', 'xor_eq',
|
|
231
|
+
],
|
|
232
|
+
# B.9 Size of integer types
|
|
233
|
+
'limits.h': C90_STDLIB_IDENTIFIERS['limits.h'] +
|
|
234
|
+
['LLONG_MIN', 'LLONG_MAX', 'ULLONG_MAX'],
|
|
235
|
+
# B.10 Localization
|
|
236
|
+
'locale.h': C90_STDLIB_IDENTIFIERS['locale.h'],
|
|
237
|
+
# B.11 Mathematics
|
|
238
|
+
'math.h': C90_STDLIB_IDENTIFIERS['math.h'] + [
|
|
239
|
+
'float_t', 'double_t', 'HUGE_VAL', 'HUGE_VALF', 'HUGE_VALL',
|
|
240
|
+
'INFINITY', 'NAN', 'FP_INFINITE', 'FP_NAN', 'FP_NORMAL',
|
|
241
|
+
'FP_SUBNORMAL', 'FP_ZERO', 'FP_FAST_FMA', 'FP_FAST_FMAF',
|
|
242
|
+
'FP_FAST_FMAL', 'FP_ILOGB0', 'FP_ILOGBNAN', 'MATH_ERRNO',
|
|
243
|
+
'MATH_ERREXCEPT', 'math_errhandling', 'FP_CONTRACT', 'fpclassify',
|
|
244
|
+
'isfinite', 'isinf', 'isnan', 'isnormal', 'signbit', 'acosf', 'acosl',
|
|
245
|
+
'asinf', 'asinl', 'atanf', 'atanl', 'atan2', 'atan2f', 'atan2l',
|
|
246
|
+
'cosf', 'cosl', 'sinf', 'sinl', 'tanf', 'tanl', 'acosh', 'acoshf',
|
|
247
|
+
'acoshl', 'asinh', 'asinhf', 'asinhl', 'atanh', 'atanhf', 'atanhl',
|
|
248
|
+
'cosh', 'coshf', 'coshl', 'sinh', 'sinhf', 'sinhl', 'tanh', 'tanhf',
|
|
249
|
+
'tanhl', 'expf', 'expl', 'exp2', 'exp2f', 'exp2l', 'expm1', 'expm1f',
|
|
250
|
+
'expm1l', 'frexpf', 'frexpl', 'ilogb', 'ilogbf', 'ilogbl', 'float',
|
|
251
|
+
'ldexpl', 'logf', 'logl', 'log10f', 'log10l', 'log1p', 'log1pf',
|
|
252
|
+
'log1pl', 'log2', 'log2f', 'log2l', 'logb', 'logbf', 'logbl', 'modff',
|
|
253
|
+
'modfl', 'scalbn', 'scalbnf', 'scalbnl', 'scalbln', 'scalblnf',
|
|
254
|
+
'scalblnl', 'hypotl', 'powf', 'powl', 'sqrtf', 'sqrtl', 'erf', 'erff',
|
|
255
|
+
'erfl', 'erfc', 'erfcf', 'erfcl', 'lgamma', 'lgammaf', 'lgammal',
|
|
256
|
+
'tgamma', 'tgammaf', 'tgammal', 'ceilf', 'ceill', 'floorf', 'floorl',
|
|
257
|
+
'nearbyint', 'nearbyintf', 'nearbyintl', 'rint', 'rintf', 'rintl',
|
|
258
|
+
'lrint', 'lrintf', 'lrintl', 'llrint', 'llrintf', 'llrintl', 'round',
|
|
259
|
+
'roundf', 'roundl', 'lround', 'lroundf', 'lroundl', 'llround',
|
|
260
|
+
'llroundf', 'llroundl', 'trunc', 'truncf', 'truncl', 'fmodf', 'fmodl',
|
|
261
|
+
'remainder', 'remainderf', 'remainderl', 'remquo', 'remquof',
|
|
262
|
+
'remquol', 'copysign', 'copysignf', 'copysignl', 'nan', 'nanf',
|
|
263
|
+
'nanl', 'nextafter', 'nextafterf', 'nextafterl', 'nexttoward',
|
|
264
|
+
'nexttowardf', 'nexttowardl', 'fdim', 'fdimf', 'fdiml', 'fmax',
|
|
265
|
+
'fmaxf', 'fmaxl', 'fmin', 'fminf', 'fminl', 'fmal', 'isgreater',
|
|
266
|
+
'isgreaterequal', 'isless', 'islessequal', 'islessgreater',
|
|
267
|
+
'isunordered',
|
|
268
|
+
],
|
|
269
|
+
# B.12 Nonlocal jumps
|
|
270
|
+
'setjmp.h': C90_STDLIB_IDENTIFIERS['setjmp.h'],
|
|
271
|
+
# B.13 Signal handling
|
|
272
|
+
'signal.h': C90_STDLIB_IDENTIFIERS['signal.h'],
|
|
273
|
+
# B.14 Variable arguments
|
|
274
|
+
'stdarg.h': C90_STDLIB_IDENTIFIERS['stdarg.h'] + ['va_copy'],
|
|
275
|
+
# B.15 Boolean type and values
|
|
276
|
+
'stdbool.h': ['bool', 'true', 'false', '__bool_true_false_are_defined'],
|
|
277
|
+
# B.16 Common definitions
|
|
278
|
+
'stddef.h': C90_STDLIB_IDENTIFIERS['stddef.h'],
|
|
279
|
+
# B.17 Integer types
|
|
280
|
+
'stdint.h': [
|
|
281
|
+
'intptr_t', 'uintptr_t', 'intmax_t', 'uintmax_t', 'INTN_MIN',
|
|
282
|
+
'INTN_MAX', 'UINTN_MAX', 'INT_LEASTN_MIN', 'INT_LEASTN_MAX',
|
|
283
|
+
'UINT_LEASTN_MAX', 'INT_FASTN_MIN', 'INT_FASTN_MAX', 'UINT_FASTN_MAX',
|
|
284
|
+
'INTPTR_MIN', 'INTPTR_MAX', 'UINTPTR_MAX', 'INTMAX_MIN', 'INTMAX_MAX',
|
|
285
|
+
'UINTMAX_MAX', 'PTRDIFF_MIN', 'PTRDIFF_MAX', 'SIG_ATOMIC_MIN',
|
|
286
|
+
'SIG_ATOMIC_MAX', 'SIZE_MAX', 'WCHAR_MIN', 'WCHAR_MAX', 'WINT_MIN',
|
|
287
|
+
'WINT_MAX', 'INTN_C', 'UINTN_C', 'INTMAX_C', 'UINTMAX_C',
|
|
288
|
+
] + STDINT_TYPES,
|
|
289
|
+
# B.18 Input/output
|
|
290
|
+
'stdio.h': C90_STDLIB_IDENTIFIERS['stdio.h'] + [
|
|
291
|
+
'mode', 'restrict', 'snprintf', 'vfscanf', 'vscanf',
|
|
292
|
+
'vsnprintf', 'vsscanf',
|
|
293
|
+
],
|
|
294
|
+
# B.19 General utilities
|
|
295
|
+
'stdlib.h': C90_STDLIB_IDENTIFIERS['stdlib.h'] + [
|
|
296
|
+
'_Exit', 'labs', 'llabs', 'lldiv', 'lldiv_t', 'strtof', 'strtol',
|
|
297
|
+
'strtold', 'strtoll', 'strtoul', 'strtoull'
|
|
298
|
+
],
|
|
299
|
+
# B.20 String handling
|
|
300
|
+
'string.h': C90_STDLIB_IDENTIFIERS['string.h'],
|
|
301
|
+
# B.21 Type-generic math
|
|
302
|
+
'tgmath.h': [
|
|
303
|
+
'acos', 'asin', 'atan', 'acosh', 'asinh', 'atanh', 'cos', 'sin', 'tan',
|
|
304
|
+
'cosh', 'sinh', 'tanh', 'exp', 'log', 'pow', 'sqrt', 'fabs', 'atan2',
|
|
305
|
+
'cbrt', 'ceil', 'copysign', 'erf', 'erfc', 'exp2', 'expm1', 'fdim',
|
|
306
|
+
'floor', 'fma', 'fmax', 'fmin', 'fmod', 'frexp', 'hypot', 'ilogb',
|
|
307
|
+
'ldexp', 'lgamma', 'llrint', 'llround', 'log10', 'log1p', 'log2',
|
|
308
|
+
'logb', 'lrint', 'lround', 'nearbyint', 'nextafter', 'nexttoward',
|
|
309
|
+
'remainder', 'remquo', 'rint', 'round', 'scalbn', 'scalbln', 'tgamma',
|
|
310
|
+
'trunc', 'carg', 'cimag', 'conj', 'cproj', 'creal',
|
|
311
|
+
],
|
|
312
|
+
# B.22 Date and time
|
|
313
|
+
'time.h': C90_STDLIB_IDENTIFIERS['time.h'] + ['CLOCKS_PER_SEC'],
|
|
314
|
+
# B.23 Extended multibyte/wide character utilities
|
|
315
|
+
'wchar.h': [
|
|
316
|
+
'wchar_t', 'size_t', 'mbstate_t', 'wint_t', 'tm', 'NULL', 'WCHAR_MAX',
|
|
317
|
+
'WCHAR_MIN', 'WEOF', 'fwprintf', 'fwscanf', 'swprintf', 'swscanf',
|
|
318
|
+
'vfwprintf', 'vfwscanf', 'vswprintf', 'vswscanf', 'vwprintf',
|
|
319
|
+
'vwscanf', 'wprintf', 'wscanf', 'fgetwc', 'fgetws', 'fputwc', 'fputws',
|
|
320
|
+
'fwide', 'getwc', 'getwchar', 'putwc', 'putwchar', 'ungetwc', 'wcstod',
|
|
321
|
+
'wcstof', 'double', 'int', 'long', 'long', 'long', 'wcscpy', 'wcsncpy',
|
|
322
|
+
'wmemcpy', 'wmemmove', 'wcscat', 'wcsncat', 'wcscmp', 'wcscoll',
|
|
323
|
+
'wcsncmp', 'wcsxfrm', 'wmemcmp', 'wcschr', 'wcscspn', 'wcspbrk',
|
|
324
|
+
'wcsrchr', 'wcsspn', 'wcsstr', 'wcstok', 'wmemchr', 'wcslen',
|
|
325
|
+
'wmemset', 'wcsftime', 'btowc', 'wctob', 'mbsinit', 'mbrlen',
|
|
326
|
+
'mbrtowc', 'wcrtomb', 'mbsrtowcs', 'wcsrtombs',
|
|
327
|
+
],
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def isStdLibId(id_, standard='c99'):
|
|
332
|
+
id_lists = []
|
|
333
|
+
if standard == 'c89':
|
|
334
|
+
id_lists = C90_STDLIB_IDENTIFIERS.values()
|
|
335
|
+
elif standard in ('c99', 'c11'):
|
|
336
|
+
id_lists = C99_STDLIB_IDENTIFIERS.values()
|
|
337
|
+
for l in id_lists:
|
|
338
|
+
if id_ in l:
|
|
339
|
+
return True
|
|
340
|
+
return False
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
# Reserved keywords defined in ISO/IEC9899:1990 -- ch 6.1.1
|
|
344
|
+
C90_KEYWORDS = {
|
|
345
|
+
'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do',
|
|
346
|
+
'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if',
|
|
347
|
+
'int', 'long', 'register', 'return', 'short', 'signed',
|
|
348
|
+
'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', 'unsigned',
|
|
349
|
+
'void', 'volatile', 'while'
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
# Reserved keywords defined in ISO/IEC 9899 WF14/N1256 -- ch. 6.4.1
|
|
354
|
+
C99_ADDED_KEYWORDS = {
|
|
355
|
+
'inline', 'restrict', '_Bool', '_Complex', '_Imaginary',
|
|
356
|
+
'bool', 'complex', 'imaginary'
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
C11_ADDED_KEYWORDS = {
|
|
360
|
+
'_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn',
|
|
361
|
+
'_Statis_assert', '_Thread_local' ,
|
|
362
|
+
'alignas', 'alignof', 'noreturn', 'static_assert'
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
def isKeyword(keyword, standard='c99'):
|
|
366
|
+
kw_set = {}
|
|
367
|
+
if standard == 'c89':
|
|
368
|
+
kw_set = C90_KEYWORDS
|
|
369
|
+
elif standard == 'c99':
|
|
370
|
+
kw_set = copy.copy(C90_KEYWORDS)
|
|
371
|
+
kw_set.update(C99_ADDED_KEYWORDS)
|
|
372
|
+
else:
|
|
373
|
+
kw_set = copy.copy(C90_KEYWORDS)
|
|
374
|
+
kw_set.update(C99_ADDED_KEYWORDS)
|
|
375
|
+
kw_set.update(C11_ADDED_KEYWORDS)
|
|
376
|
+
return keyword in kw_set
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def is_source_file(file):
|
|
380
|
+
return file.endswith('.c')
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def is_header(file):
|
|
384
|
+
return file.endswith('.h')
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def is_errno_setting_function(function_name):
|
|
388
|
+
return function_name and \
|
|
389
|
+
function_name in ('ftell', 'fgetpos', 'fsetpos', 'fgetwc', 'fputwc'
|
|
390
|
+
'strtoimax', 'strtoumax', 'strtol', 'strtoul',
|
|
391
|
+
'strtoll', 'strtoull', 'strtof', 'strtod', 'strtold'
|
|
392
|
+
'wcstoimax', 'wcstoumax', 'wcstol', 'wcstoul',
|
|
393
|
+
'wcstoll', 'wcstoull', 'wcstof', 'wcstod', 'wcstold'
|
|
394
|
+
'wcrtomb', 'wcsrtombs', 'mbrtowc')
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def get_type_conversion_to_from(token):
|
|
398
|
+
def get_vartok(expr):
|
|
399
|
+
while expr:
|
|
400
|
+
if isCast(expr):
|
|
401
|
+
if expr.astOperand2 is None:
|
|
402
|
+
expr = expr.astOperand1
|
|
403
|
+
else:
|
|
404
|
+
expr = expr.astOperand2
|
|
405
|
+
elif expr.str in ('.', '::'):
|
|
406
|
+
expr = expr.astOperand2
|
|
407
|
+
elif expr.str == '[':
|
|
408
|
+
expr = expr.astOperand1
|
|
409
|
+
else:
|
|
410
|
+
break
|
|
411
|
+
return expr if (expr and expr.variable) else None
|
|
412
|
+
|
|
413
|
+
if isCast(token):
|
|
414
|
+
vartok = get_vartok(token)
|
|
415
|
+
if vartok:
|
|
416
|
+
return (token.next, vartok.variable.typeStartToken)
|
|
417
|
+
|
|
418
|
+
elif token.str == '=':
|
|
419
|
+
lhs = get_vartok(token.astOperand1)
|
|
420
|
+
rhs = get_vartok(token.astOperand2)
|
|
421
|
+
if lhs and rhs:
|
|
422
|
+
return (lhs.variable.typeStartToken, rhs.variable.typeStartToken)
|
|
423
|
+
|
|
424
|
+
return None
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def is_composite_expr(expr, composite_operator=False):
|
|
428
|
+
"""MISRA C 2012, section 8.10.3"""
|
|
429
|
+
if expr is None:
|
|
430
|
+
return False
|
|
431
|
+
|
|
432
|
+
if not composite_operator:
|
|
433
|
+
if (expr.str in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":", '~')):
|
|
434
|
+
return is_composite_expr(expr.astOperand1,True) or is_composite_expr(expr.astOperand2, True)
|
|
435
|
+
if expr.str == '?' and simpleMatch(expr.astOperand2, ':'):
|
|
436
|
+
colon = expr.astOperand2
|
|
437
|
+
return is_composite_expr(colon.astOperand1,True) or is_composite_expr(colon.astOperand2, True)
|
|
438
|
+
return False
|
|
439
|
+
|
|
440
|
+
# non constant expression?
|
|
441
|
+
if expr.isNumber:
|
|
442
|
+
return False
|
|
443
|
+
if expr.astOperand1 or expr.astOperand2:
|
|
444
|
+
return is_composite_expr(expr.astOperand1,True) or is_composite_expr(expr.astOperand2, True)
|
|
445
|
+
return True
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def getEssentialTypeCategory(expr):
|
|
449
|
+
if not expr:
|
|
450
|
+
return None
|
|
451
|
+
if expr.str == ',':
|
|
452
|
+
return getEssentialTypeCategory(expr.astOperand2)
|
|
453
|
+
if expr.str in ('<', '<=', '==', '!=', '>=', '>', '&&', '||', '!'):
|
|
454
|
+
return 'bool'
|
|
455
|
+
if expr.str in ('<<', '>>'):
|
|
456
|
+
# TODO this is incomplete
|
|
457
|
+
return getEssentialTypeCategory(expr.astOperand1)
|
|
458
|
+
if len(expr.str) == 1 and expr.str in '+-*/%&|^':
|
|
459
|
+
# TODO this is incomplete
|
|
460
|
+
e1 = getEssentialTypeCategory(expr.astOperand1)
|
|
461
|
+
e2 = getEssentialTypeCategory(expr.astOperand2)
|
|
462
|
+
# print('{0}: {1} {2}'.format(expr.str, e1, e2))
|
|
463
|
+
if e1 and e2 and e1 == e2:
|
|
464
|
+
return e1
|
|
465
|
+
if expr.valueType:
|
|
466
|
+
return expr.valueType.sign
|
|
467
|
+
if expr.valueType and expr.valueType.typeScope and expr.valueType.typeScope.className:
|
|
468
|
+
return "enum<" + expr.valueType.typeScope.className + ">"
|
|
469
|
+
vartok = expr
|
|
470
|
+
while simpleMatch(vartok, '[') or (vartok and vartok.str == '*' and vartok.astOperand2 is None):
|
|
471
|
+
vartok = vartok.astOperand1
|
|
472
|
+
if vartok and vartok.variable:
|
|
473
|
+
typeToken = vartok.variable.typeStartToken
|
|
474
|
+
while typeToken and typeToken.isName:
|
|
475
|
+
if typeToken.str == 'char' and not typeToken.isSigned and not typeToken.isUnsigned:
|
|
476
|
+
return 'char'
|
|
477
|
+
if typeToken.valueType:
|
|
478
|
+
if typeToken.valueType.type == 'bool':
|
|
479
|
+
return typeToken.valueType.type
|
|
480
|
+
if typeToken.valueType.type in ('float', 'double', 'long double'):
|
|
481
|
+
return "float"
|
|
482
|
+
if typeToken.valueType.sign:
|
|
483
|
+
return typeToken.valueType.sign
|
|
484
|
+
typeToken = typeToken.next
|
|
485
|
+
|
|
486
|
+
# See Appendix D, section D.6, Character constants
|
|
487
|
+
if expr.str[0] == "'" and expr.str[-1] == "'":
|
|
488
|
+
if len(expr.str) == 3 or (len(expr.str) == 4 and expr.str[1] == '\\'):
|
|
489
|
+
return 'char'
|
|
490
|
+
return expr.valueType.sign
|
|
491
|
+
|
|
492
|
+
if expr.valueType:
|
|
493
|
+
return expr.valueType.sign
|
|
494
|
+
return None
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def getEssentialCategorylist(operand1, operand2):
|
|
498
|
+
if not operand1 or not operand2:
|
|
499
|
+
return None, None
|
|
500
|
+
if (operand1.str in ('++', '--') or
|
|
501
|
+
operand2.str in ('++', '--')):
|
|
502
|
+
return None, None
|
|
503
|
+
if ((operand1.valueType and operand1.valueType.pointer) or
|
|
504
|
+
(operand2.valueType and operand2.valueType.pointer)):
|
|
505
|
+
return None, None
|
|
506
|
+
e1 = getEssentialTypeCategory(operand1)
|
|
507
|
+
e2 = getEssentialTypeCategory(operand2)
|
|
508
|
+
return e1, e2
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def get_essential_type_from_value(value, is_signed):
|
|
512
|
+
if value is None:
|
|
513
|
+
return None
|
|
514
|
+
for t in ('char', 'short', 'int', 'long', 'long long'):
|
|
515
|
+
bits = bitsOfEssentialType(t)
|
|
516
|
+
if bits >= 64:
|
|
517
|
+
continue
|
|
518
|
+
if is_signed:
|
|
519
|
+
range_min = -(1 << (bits - 1))
|
|
520
|
+
range_max = (1 << (bits - 1)) - 1
|
|
521
|
+
else:
|
|
522
|
+
range_min = 0
|
|
523
|
+
range_max = (1 << bits) - 1
|
|
524
|
+
sign = 'signed' if is_signed else 'unsigned'
|
|
525
|
+
if is_signed and value < 0 and value >= range_min:
|
|
526
|
+
return '%s %s' % (sign, t)
|
|
527
|
+
if value >= 0 and value <= range_max:
|
|
528
|
+
return '%s %s' % (sign, t)
|
|
529
|
+
return None
|
|
530
|
+
|
|
531
|
+
def getEssentialType(expr):
|
|
532
|
+
if not expr:
|
|
533
|
+
return None
|
|
534
|
+
|
|
535
|
+
# See Appendix D, section D.6, Character constants
|
|
536
|
+
if expr.str[0] == "'" and expr.str[-1] == "'":
|
|
537
|
+
if len(expr.str) == 3 or (len(expr.str) == 4 and expr.str[1] == '\\'):
|
|
538
|
+
return 'char'
|
|
539
|
+
return '%s %s' % (expr.valueType.sign, expr.valueType.type)
|
|
540
|
+
|
|
541
|
+
if expr.variable or isCast(expr):
|
|
542
|
+
typeToken = expr.variable.typeStartToken if expr.variable else expr.next
|
|
543
|
+
while typeToken and typeToken.isName:
|
|
544
|
+
if typeToken.str == 'char' and not typeToken.isSigned and not typeToken.isUnsigned:
|
|
545
|
+
return 'char'
|
|
546
|
+
typeToken = typeToken.next
|
|
547
|
+
if expr.valueType:
|
|
548
|
+
if expr.valueType.type == 'bool':
|
|
549
|
+
return 'bool'
|
|
550
|
+
if expr.valueType.isFloat():
|
|
551
|
+
return expr.valueType.type
|
|
552
|
+
if expr.valueType.isIntegral():
|
|
553
|
+
if (expr.valueType.sign is None) and expr.valueType.type == 'char':
|
|
554
|
+
return 'char'
|
|
555
|
+
return '%s %s' % (expr.valueType.sign, expr.valueType.type)
|
|
556
|
+
|
|
557
|
+
elif expr.isNumber:
|
|
558
|
+
# Appendix D, D.6 The essential type of literal constants
|
|
559
|
+
# Integer constants
|
|
560
|
+
if expr.valueType.type == 'bool':
|
|
561
|
+
return 'bool'
|
|
562
|
+
if expr.valueType.isFloat():
|
|
563
|
+
return expr.valueType.type
|
|
564
|
+
if expr.valueType.isIntegral():
|
|
565
|
+
if expr.valueType.type != 'int':
|
|
566
|
+
return '%s %s' % (expr.valueType.sign, expr.valueType.type)
|
|
567
|
+
return get_essential_type_from_value(expr.getKnownIntValue(), expr.valueType.sign == 'signed')
|
|
568
|
+
|
|
569
|
+
elif expr.str in ('<', '<=', '>=', '>', '==', '!=', '&&', '||', '!'):
|
|
570
|
+
return 'bool'
|
|
571
|
+
|
|
572
|
+
elif expr.astOperand1 and expr.astOperand2 and expr.str in (
|
|
573
|
+
'+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":"):
|
|
574
|
+
if expr.astOperand1.valueType and expr.astOperand1.valueType.pointer > 0:
|
|
575
|
+
return None
|
|
576
|
+
if expr.astOperand2.valueType and expr.astOperand2.valueType.pointer > 0:
|
|
577
|
+
return None
|
|
578
|
+
e1 = getEssentialType(expr.astOperand1)
|
|
579
|
+
e2 = getEssentialType(expr.astOperand2)
|
|
580
|
+
if e1 is None or e2 is None:
|
|
581
|
+
return None
|
|
582
|
+
if is_constant_integer_expression(expr):
|
|
583
|
+
sign1 = e1.split(' ')[0]
|
|
584
|
+
sign2 = e2.split(' ')[0]
|
|
585
|
+
if sign1 == sign2 and sign1 in ('signed', 'unsigned'):
|
|
586
|
+
e = get_essential_type_from_value(expr.getKnownIntValue(), sign1 == 'signed')
|
|
587
|
+
if e:
|
|
588
|
+
return e
|
|
589
|
+
if bitsOfEssentialType(e2) >= bitsOfEssentialType(e1):
|
|
590
|
+
return e2
|
|
591
|
+
else:
|
|
592
|
+
return e1
|
|
593
|
+
|
|
594
|
+
elif expr.str == "~":
|
|
595
|
+
e1 = getEssentialType(expr.astOperand1)
|
|
596
|
+
return e1
|
|
597
|
+
|
|
598
|
+
return None
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def bitsOfEssentialType(ty):
|
|
602
|
+
if ty is None:
|
|
603
|
+
return 0
|
|
604
|
+
last_type = ty.split(' ')[-1]
|
|
605
|
+
if last_type == 'Boolean':
|
|
606
|
+
return 1
|
|
607
|
+
if last_type == 'char':
|
|
608
|
+
return typeBits['CHAR']
|
|
609
|
+
if last_type == 'short':
|
|
610
|
+
return typeBits['SHORT']
|
|
611
|
+
if last_type == 'int':
|
|
612
|
+
return typeBits['INT']
|
|
613
|
+
if ty.endswith('long long'):
|
|
614
|
+
return typeBits['LONG_LONG']
|
|
615
|
+
if last_type == 'long':
|
|
616
|
+
return typeBits['LONG']
|
|
617
|
+
for sty in STDINT_TYPES:
|
|
618
|
+
if ty == sty:
|
|
619
|
+
return int(''.join(filter(str.isdigit, sty)))
|
|
620
|
+
return 0
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
def get_function_pointer_type(tok):
|
|
624
|
+
ret = ''
|
|
625
|
+
par = 0
|
|
626
|
+
while tok and (tok.isName or tok.str == '*'):
|
|
627
|
+
ret += ' ' + tok.str
|
|
628
|
+
tok = tok.next
|
|
629
|
+
if tok is None or tok.str != '(':
|
|
630
|
+
return None
|
|
631
|
+
tok = tok.link
|
|
632
|
+
if not simpleMatch(tok, ') ('):
|
|
633
|
+
return None
|
|
634
|
+
ret += '('
|
|
635
|
+
tok = tok.next.next
|
|
636
|
+
while tok and (tok.str not in '()'):
|
|
637
|
+
ret += ' ' + tok.str
|
|
638
|
+
tok = tok.next
|
|
639
|
+
if (tok is None) or tok.str != ')':
|
|
640
|
+
return None
|
|
641
|
+
return ret[1:] + ')'
|
|
642
|
+
|
|
643
|
+
def isCast(expr):
|
|
644
|
+
if not expr or expr.str != '(' or not expr.astOperand1 or expr.astOperand2:
|
|
645
|
+
return False
|
|
646
|
+
if simpleMatch(expr, '( )'):
|
|
647
|
+
return False
|
|
648
|
+
return True
|
|
649
|
+
|
|
650
|
+
def is_constant_integer_expression(expr):
|
|
651
|
+
if expr is None:
|
|
652
|
+
return False
|
|
653
|
+
if expr.isInt:
|
|
654
|
+
return True
|
|
655
|
+
if not expr.isArithmeticalOp:
|
|
656
|
+
return False
|
|
657
|
+
if expr.astOperand1 and not is_constant_integer_expression(expr.astOperand1):
|
|
658
|
+
return False
|
|
659
|
+
if expr.astOperand2 and not is_constant_integer_expression(expr.astOperand2):
|
|
660
|
+
return False
|
|
661
|
+
return True
|
|
662
|
+
|
|
663
|
+
def isFunctionCall(expr, std='c99'):
|
|
664
|
+
if not expr:
|
|
665
|
+
return False
|
|
666
|
+
if expr.str != '(' or not expr.astOperand1:
|
|
667
|
+
return False
|
|
668
|
+
if expr.astOperand1 != expr.previous:
|
|
669
|
+
return False
|
|
670
|
+
if isKeyword(expr.astOperand1.str, std):
|
|
671
|
+
return False
|
|
672
|
+
return True
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def hasExternalLinkage(var):
|
|
676
|
+
return var.isGlobal and not var.isStatic
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def countSideEffects(expr):
|
|
680
|
+
if not expr or expr.str in (',', ';'):
|
|
681
|
+
return 0
|
|
682
|
+
ret = 0
|
|
683
|
+
if expr.str in ('++', '--', '='):
|
|
684
|
+
ret = 1
|
|
685
|
+
return ret + countSideEffects(expr.astOperand1) + countSideEffects(expr.astOperand2)
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def getForLoopExpressions(forToken):
|
|
689
|
+
if not forToken or forToken.str != 'for':
|
|
690
|
+
return None
|
|
691
|
+
lpar = forToken.next
|
|
692
|
+
if not lpar or lpar.str != '(':
|
|
693
|
+
return None
|
|
694
|
+
if not lpar.astOperand2 or lpar.astOperand2.str != ';':
|
|
695
|
+
return None
|
|
696
|
+
if not lpar.astOperand2.astOperand2 or lpar.astOperand2.astOperand2.str != ';':
|
|
697
|
+
return None
|
|
698
|
+
return [lpar.astOperand2.astOperand1,
|
|
699
|
+
lpar.astOperand2.astOperand2.astOperand1,
|
|
700
|
+
lpar.astOperand2.astOperand2.astOperand2]
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
def getForLoopCounterVariables(forToken):
|
|
704
|
+
""" Return a set of Variable objects defined in ``for`` statement and
|
|
705
|
+
satisfy requirements to loop counter term from section 8.14 of MISRA
|
|
706
|
+
document.
|
|
707
|
+
"""
|
|
708
|
+
if not forToken or forToken.str != 'for':
|
|
709
|
+
return None
|
|
710
|
+
tn = forToken.next
|
|
711
|
+
if not tn or tn.str != '(':
|
|
712
|
+
return None
|
|
713
|
+
vars_defined = set()
|
|
714
|
+
vars_initialized = set()
|
|
715
|
+
vars_exit = set()
|
|
716
|
+
vars_modified = set()
|
|
717
|
+
cur_clause = 1
|
|
718
|
+
te = tn.link
|
|
719
|
+
while tn and tn != te:
|
|
720
|
+
if tn.variable:
|
|
721
|
+
if cur_clause == 1 and tn.variable.nameToken == tn:
|
|
722
|
+
vars_defined.add(tn.variable)
|
|
723
|
+
elif cur_clause == 2:
|
|
724
|
+
vars_exit.add(tn.variable)
|
|
725
|
+
elif cur_clause == 3:
|
|
726
|
+
if tn.next and countSideEffectsRecursive(tn.next) > 0:
|
|
727
|
+
vars_modified.add(tn.variable)
|
|
728
|
+
elif tn.previous and tn.previous.str in ('++', '--'):
|
|
729
|
+
vars_modified.add(tn.variable)
|
|
730
|
+
if cur_clause == 1 and tn.isAssignmentOp and tn.astOperand1.variable:
|
|
731
|
+
vars_initialized.add(tn.astOperand1.variable)
|
|
732
|
+
if tn.str == ';':
|
|
733
|
+
cur_clause += 1
|
|
734
|
+
tn = tn.next
|
|
735
|
+
return vars_defined | vars_initialized, vars_exit & vars_modified
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
def findCounterTokens(cond):
|
|
739
|
+
if not cond:
|
|
740
|
+
return []
|
|
741
|
+
if cond.str in ['&&', '||']:
|
|
742
|
+
c = findCounterTokens(cond.astOperand1)
|
|
743
|
+
c.extend(findCounterTokens(cond.astOperand2))
|
|
744
|
+
return c
|
|
745
|
+
ret = []
|
|
746
|
+
if ((cond.isArithmeticalOp and cond.astOperand1 and cond.astOperand2) or
|
|
747
|
+
(cond.isComparisonOp and cond.astOperand1 and cond.astOperand2)):
|
|
748
|
+
if cond.astOperand1.isName:
|
|
749
|
+
ret.append(cond.astOperand1)
|
|
750
|
+
if cond.astOperand2.isName:
|
|
751
|
+
ret.append(cond.astOperand2)
|
|
752
|
+
if cond.astOperand1.isOp:
|
|
753
|
+
ret.extend(findCounterTokens(cond.astOperand1))
|
|
754
|
+
if cond.astOperand2.isOp:
|
|
755
|
+
ret.extend(findCounterTokens(cond.astOperand2))
|
|
756
|
+
return ret
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
def isFloatCounterInWhileLoop(whileToken):
|
|
760
|
+
if not simpleMatch(whileToken, 'while ('):
|
|
761
|
+
return False
|
|
762
|
+
lpar = whileToken.next
|
|
763
|
+
rpar = lpar.link
|
|
764
|
+
counterTokens = findCounterTokens(lpar.astOperand2)
|
|
765
|
+
whileBodyStart = None
|
|
766
|
+
if simpleMatch(rpar, ') {'):
|
|
767
|
+
whileBodyStart = rpar.next
|
|
768
|
+
elif simpleMatch(whileToken.previous, '} while') and simpleMatch(whileToken.previous.link.previous, 'do {'):
|
|
769
|
+
whileBodyStart = whileToken.previous.link
|
|
770
|
+
else:
|
|
771
|
+
return False
|
|
772
|
+
token = whileBodyStart
|
|
773
|
+
while token != whileBodyStart.link:
|
|
774
|
+
token = token.next
|
|
775
|
+
for counterToken in counterTokens:
|
|
776
|
+
if not counterToken.valueType or not counterToken.valueType.isFloat():
|
|
777
|
+
continue
|
|
778
|
+
if token.isAssignmentOp and token.astOperand1.str == counterToken.str:
|
|
779
|
+
return True
|
|
780
|
+
if token.str == counterToken.str and token.astParent and token.astParent.str in ('++', '--'):
|
|
781
|
+
return True
|
|
782
|
+
return False
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
def countSideEffectsRecursive(expr):
|
|
786
|
+
if not expr or expr.str == ';':
|
|
787
|
+
return 0
|
|
788
|
+
if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '[':
|
|
789
|
+
prev = expr.astOperand1.previous
|
|
790
|
+
if prev and (prev.str == '{' or prev.str == '{'):
|
|
791
|
+
return countSideEffectsRecursive(expr.astOperand2)
|
|
792
|
+
if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '.':
|
|
793
|
+
e = expr.astOperand1
|
|
794
|
+
while e and e.str == '.' and e.astOperand2:
|
|
795
|
+
e = e.astOperand1
|
|
796
|
+
if e and e.str == '.':
|
|
797
|
+
return 0
|
|
798
|
+
if expr.isAssignmentOp or expr.str in {'++', '--'}:
|
|
799
|
+
return 1
|
|
800
|
+
# Todo: Check function calls
|
|
801
|
+
return countSideEffectsRecursive(expr.astOperand1) + countSideEffectsRecursive(expr.astOperand2)
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
def isBoolExpression(expr):
|
|
805
|
+
if not expr:
|
|
806
|
+
return False
|
|
807
|
+
if expr.valueType and (expr.valueType.type == 'bool' or expr.valueType.bits == 1):
|
|
808
|
+
return True
|
|
809
|
+
return expr.str in ['!', '==', '!=', '<', '<=', '>', '>=', '&&', '||', '0', '1', 'true', 'false']
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
def isEnumConstant(expr):
|
|
813
|
+
if not expr or not expr.values:
|
|
814
|
+
return False
|
|
815
|
+
values = expr.values
|
|
816
|
+
return len(values) == 1 and values[0].valueKind == 'known'
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
def isConstantExpression(expr):
|
|
820
|
+
if expr.isNumber:
|
|
821
|
+
return True
|
|
822
|
+
if expr.isName and not isEnumConstant(expr):
|
|
823
|
+
return False
|
|
824
|
+
if simpleMatch(expr.previous, 'sizeof ('):
|
|
825
|
+
return True
|
|
826
|
+
if expr.astOperand1 and not isConstantExpression(expr.astOperand1):
|
|
827
|
+
return False
|
|
828
|
+
if expr.astOperand2 and not isConstantExpression(expr.astOperand2):
|
|
829
|
+
return False
|
|
830
|
+
return True
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
def isUnsignedInt(expr):
|
|
834
|
+
return expr and expr.valueType and expr.valueType.type in ('short', 'int') and expr.valueType.sign == 'unsigned'
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def getPrecedence(expr):
|
|
838
|
+
if not expr:
|
|
839
|
+
return 16
|
|
840
|
+
if not expr.astOperand1 or not expr.astOperand2:
|
|
841
|
+
return 16
|
|
842
|
+
if expr.str in ('*', '/', '%'):
|
|
843
|
+
return 12
|
|
844
|
+
if expr.str in ('+', '-'):
|
|
845
|
+
return 11
|
|
846
|
+
if expr.str in ('<<', '>>'):
|
|
847
|
+
return 10
|
|
848
|
+
if expr.str in ('<', '>', '<=', '>='):
|
|
849
|
+
return 9
|
|
850
|
+
if expr.str in ('==', '!='):
|
|
851
|
+
return 8
|
|
852
|
+
if expr.str == '&':
|
|
853
|
+
return 7
|
|
854
|
+
if expr.str == '^':
|
|
855
|
+
return 6
|
|
856
|
+
if expr.str == '|':
|
|
857
|
+
return 5
|
|
858
|
+
if expr.str == '&&':
|
|
859
|
+
return 4
|
|
860
|
+
if expr.str == '||':
|
|
861
|
+
return 3
|
|
862
|
+
if expr.str in ('?', ':'):
|
|
863
|
+
return 2
|
|
864
|
+
if expr.isAssignmentOp:
|
|
865
|
+
return 1
|
|
866
|
+
if expr.str == ',':
|
|
867
|
+
return 0
|
|
868
|
+
return -1
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
def findRawLink(token):
|
|
872
|
+
tok1 = None
|
|
873
|
+
tok2 = None
|
|
874
|
+
forward = False
|
|
875
|
+
|
|
876
|
+
if token.str in '{([':
|
|
877
|
+
tok1 = token.str
|
|
878
|
+
tok2 = '})]'['{(['.find(token.str)]
|
|
879
|
+
forward = True
|
|
880
|
+
elif token.str in '})]':
|
|
881
|
+
tok1 = token.str
|
|
882
|
+
tok2 = '{(['['})]'.find(token.str)]
|
|
883
|
+
forward = False
|
|
884
|
+
else:
|
|
885
|
+
return None
|
|
886
|
+
|
|
887
|
+
# try to find link
|
|
888
|
+
indent = 0
|
|
889
|
+
while token:
|
|
890
|
+
if token.str == tok1:
|
|
891
|
+
indent = indent + 1
|
|
892
|
+
elif token.str == tok2:
|
|
893
|
+
if indent <= 1:
|
|
894
|
+
return token
|
|
895
|
+
indent = indent - 1
|
|
896
|
+
if forward is True:
|
|
897
|
+
token = token.next
|
|
898
|
+
else:
|
|
899
|
+
token = token.previous
|
|
900
|
+
|
|
901
|
+
# raw link not found
|
|
902
|
+
return None
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
def numberOfParentheses(tok1, tok2):
|
|
906
|
+
while tok1 and tok1 != tok2:
|
|
907
|
+
if tok1.str == '(' or tok1.str == ')':
|
|
908
|
+
return False
|
|
909
|
+
tok1 = tok1.next
|
|
910
|
+
return tok1 == tok2
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
def findGotoLabel(gotoToken):
|
|
914
|
+
label = gotoToken.next.str
|
|
915
|
+
tok = gotoToken.next.next
|
|
916
|
+
while tok:
|
|
917
|
+
if tok.str == '}' and tok.scope.type == 'Function':
|
|
918
|
+
break
|
|
919
|
+
if tok.str == label and tok.next.str == ':':
|
|
920
|
+
return tok
|
|
921
|
+
tok = tok.next
|
|
922
|
+
return None
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
def findInclude(directives, header):
|
|
926
|
+
for directive in directives:
|
|
927
|
+
if directive.str == '#include ' + header:
|
|
928
|
+
return directive
|
|
929
|
+
return None
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
# Get function arguments
|
|
933
|
+
def getArgumentsRecursive(tok, arguments):
|
|
934
|
+
if tok is None:
|
|
935
|
+
return
|
|
936
|
+
if tok.str == ',':
|
|
937
|
+
getArgumentsRecursive(tok.astOperand1, arguments)
|
|
938
|
+
getArgumentsRecursive(tok.astOperand2, arguments)
|
|
939
|
+
else:
|
|
940
|
+
arguments.append(tok)
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def getArguments(ftok):
|
|
944
|
+
arguments = []
|
|
945
|
+
getArgumentsRecursive(ftok.astOperand2, arguments)
|
|
946
|
+
return arguments
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
def isalnum(c):
|
|
950
|
+
return c in string.digits or c in string.ascii_letters
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
def isHexEscapeSequence(symbols):
|
|
954
|
+
"""Checks that given symbols are valid hex escape sequence.
|
|
955
|
+
|
|
956
|
+
hexadecimal-escape-sequence:
|
|
957
|
+
\\x hexadecimal-digit
|
|
958
|
+
hexadecimal-escape-sequence hexadecimal-digit
|
|
959
|
+
|
|
960
|
+
Reference: n1570 6.4.4.4"""
|
|
961
|
+
if len(symbols) < 3 or symbols[:2] != '\\x':
|
|
962
|
+
return False
|
|
963
|
+
return all([s in string.hexdigits for s in symbols[2:]])
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
def isOctalEscapeSequence(symbols):
|
|
967
|
+
r"""Checks that given symbols are valid octal escape sequence:
|
|
968
|
+
|
|
969
|
+
octal-escape-sequence:
|
|
970
|
+
\ octal-digit
|
|
971
|
+
\ octal-digit octal-digit
|
|
972
|
+
\ octal-digit octal-digit octal-digit
|
|
973
|
+
|
|
974
|
+
Reference: n1570 6.4.4.4"""
|
|
975
|
+
if len(symbols) not in range(2, 5) or symbols[0] != '\\':
|
|
976
|
+
return False
|
|
977
|
+
return all([s in string.octdigits for s in symbols[1:]])
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
def isSimpleEscapeSequence(symbols):
|
|
981
|
+
"""Checks that given symbols are simple escape sequence.
|
|
982
|
+
Reference: n1570 6.4.4.4"""
|
|
983
|
+
if len(symbols) != 2 or symbols[0] != '\\':
|
|
984
|
+
return False
|
|
985
|
+
return symbols[1] in ("'", '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v')
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
def isTernaryOperator(token):
|
|
989
|
+
if not token:
|
|
990
|
+
return False
|
|
991
|
+
if not token.astOperand2:
|
|
992
|
+
return False
|
|
993
|
+
return token.str == '?' and token.astOperand2.str == ':'
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
def getTernaryOperandsRecursive(token):
|
|
997
|
+
"""Returns list of ternary operands including nested ones."""
|
|
998
|
+
if not isTernaryOperator(token):
|
|
999
|
+
return []
|
|
1000
|
+
result = []
|
|
1001
|
+
result += getTernaryOperandsRecursive(token.astOperand2.astOperand1)
|
|
1002
|
+
if token.astOperand2.astOperand1 and not isTernaryOperator(token.astOperand2.astOperand1):
|
|
1003
|
+
result += [token.astOperand2.astOperand1]
|
|
1004
|
+
result += getTernaryOperandsRecursive(token.astOperand2.astOperand2)
|
|
1005
|
+
if token.astOperand2.astOperand2 and not isTernaryOperator(token.astOperand2.astOperand2):
|
|
1006
|
+
result += [token.astOperand2.astOperand2]
|
|
1007
|
+
return result
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
def hasNumericEscapeSequence(symbols):
|
|
1011
|
+
"""Check that given string contains octal or hexadecimal escape sequences."""
|
|
1012
|
+
if '\\' not in symbols:
|
|
1013
|
+
return False
|
|
1014
|
+
for c, cn in grouped(symbols, 2):
|
|
1015
|
+
if c == '\\' and cn in ('x' + string.octdigits):
|
|
1016
|
+
return True
|
|
1017
|
+
return False
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
def isNoReturnScope(tok):
|
|
1021
|
+
if tok is None or tok.str != '}':
|
|
1022
|
+
return False
|
|
1023
|
+
if tok.previous is None or tok.previous.str != ';':
|
|
1024
|
+
return False
|
|
1025
|
+
if simpleMatch(tok.previous.previous, 'break ;'):
|
|
1026
|
+
return True
|
|
1027
|
+
prev = tok.previous.previous
|
|
1028
|
+
while prev and prev.str not in ';{}':
|
|
1029
|
+
if prev.str in '])':
|
|
1030
|
+
prev = prev.link
|
|
1031
|
+
prev = prev.previous
|
|
1032
|
+
if prev and prev.next.str in ['throw', 'return']:
|
|
1033
|
+
return True
|
|
1034
|
+
return False
|
|
1035
|
+
|
|
1036
|
+
|
|
1037
|
+
# Return the token which the value is assigned to
|
|
1038
|
+
def getAssignedVariableToken(vartok):
|
|
1039
|
+
if not vartok:
|
|
1040
|
+
return None
|
|
1041
|
+
parent = vartok.astParent
|
|
1042
|
+
while parent and parent.isArithmeticalOp:
|
|
1043
|
+
parent = parent.astParent
|
|
1044
|
+
if parent and parent.isAssignmentOp:
|
|
1045
|
+
return parent.astOperand1
|
|
1046
|
+
return None
|
|
1047
|
+
|
|
1048
|
+
# If the value is used as a return value, return the function definition
|
|
1049
|
+
def getFunctionUsingReturnValue(valueToken):
|
|
1050
|
+
if not valueToken:
|
|
1051
|
+
return None
|
|
1052
|
+
if not valueToken.astParent:
|
|
1053
|
+
return None
|
|
1054
|
+
operator = valueToken.astParent
|
|
1055
|
+
if operator.str == 'return':
|
|
1056
|
+
return operator.scope.function
|
|
1057
|
+
if operator.isArithmeticalOp:
|
|
1058
|
+
return getFunctionUsingReturnValue(operator)
|
|
1059
|
+
return None
|
|
1060
|
+
|
|
1061
|
+
# Return true if the token follows a specific sequence of token str values
|
|
1062
|
+
def tokenFollowsSequence(token, sequence):
|
|
1063
|
+
if not token:
|
|
1064
|
+
return False
|
|
1065
|
+
for i in reversed(sequence):
|
|
1066
|
+
prev = token.previous
|
|
1067
|
+
if not prev:
|
|
1068
|
+
return False
|
|
1069
|
+
if prev.str != i:
|
|
1070
|
+
return False
|
|
1071
|
+
token = prev
|
|
1072
|
+
return True
|
|
1073
|
+
|
|
1074
|
+
class Define:
|
|
1075
|
+
def __init__(self, directive):
|
|
1076
|
+
self.name = ''
|
|
1077
|
+
self.args = []
|
|
1078
|
+
self.expansionList = ''
|
|
1079
|
+
|
|
1080
|
+
res = re.match(r'#define ([A-Za-z0-9_]+)\(([A-Za-z0-9_, ]+)\)[ ]+(.*)', directive.str)
|
|
1081
|
+
if res:
|
|
1082
|
+
self.name = res.group(1)
|
|
1083
|
+
self.args = res.group(2).strip().split(',')
|
|
1084
|
+
self.expansionList = res.group(3)
|
|
1085
|
+
else:
|
|
1086
|
+
res = re.match(r'#define ([A-Za-z0-9_]+)[ ]+(.*)', directive.str)
|
|
1087
|
+
if res:
|
|
1088
|
+
self.name = res.group(1)
|
|
1089
|
+
self.expansionList = res.group(2)
|
|
1090
|
+
|
|
1091
|
+
def __repr__(self):
|
|
1092
|
+
attrs = ["name", "args", "expansionList"]
|
|
1093
|
+
return "{}({})".format(
|
|
1094
|
+
"Define",
|
|
1095
|
+
", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs))
|
|
1096
|
+
)
|
|
1097
|
+
|
|
1098
|
+
|
|
1099
|
+
def getAddonRules():
|
|
1100
|
+
"""Returns dict of MISRA rules handled by this addon."""
|
|
1101
|
+
addon_rules = []
|
|
1102
|
+
compiled = re.compile(r'.*def[ ]+misra_([0-9]+)_([0-9]+)[(].*')
|
|
1103
|
+
for line in open(__file__):
|
|
1104
|
+
res = compiled.match(line)
|
|
1105
|
+
if res is None:
|
|
1106
|
+
continue
|
|
1107
|
+
addon_rules.append(res.group(1) + '.' + res.group(2))
|
|
1108
|
+
return addon_rules
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
def getCppcheckRules():
|
|
1112
|
+
"""Returns list of rules handled by cppcheck."""
|
|
1113
|
+
return ['1.3', # <most "error">
|
|
1114
|
+
'2.1', # alwaysFalse, duplicateBreak
|
|
1115
|
+
'2.2', # alwaysTrue, redundantCondition, redundantAssignment, redundantAssignInSwitch, unreadVariable
|
|
1116
|
+
'2.6', # unusedLabel
|
|
1117
|
+
'5.3', # shadowVariable
|
|
1118
|
+
'8.3', # funcArgNamesDifferent
|
|
1119
|
+
'8.13', # constPointer
|
|
1120
|
+
'9.1', # uninitvar
|
|
1121
|
+
'14.3', # alwaysTrue, alwaysFalse, compareValueOutOfTypeRangeError
|
|
1122
|
+
'13.2', # unknownEvaluationOrder
|
|
1123
|
+
'13.6', # sizeofCalculation
|
|
1124
|
+
'17.4', # missingReturn
|
|
1125
|
+
'17.5', # argumentSize
|
|
1126
|
+
'18.1', # pointerOutOfBounds
|
|
1127
|
+
'18.2', # comparePointers
|
|
1128
|
+
'18.3', # comparePointers
|
|
1129
|
+
'18.6', # danglingLifetime
|
|
1130
|
+
'19.1', # overlappingWriteUnion, overlappingWriteFunction
|
|
1131
|
+
'20.6', # preprocessorErrorDirective
|
|
1132
|
+
'21.13', # invalidFunctionArg
|
|
1133
|
+
'21.17', # bufferAccessOutOfBounds
|
|
1134
|
+
'21.18', # bufferAccessOutOfBounds
|
|
1135
|
+
'22.1', # memleak, resourceLeak, memleakOnRealloc, leakReturnValNotUsed, leakNoVarFunctionCall
|
|
1136
|
+
'22.2', # autovarInvalidDeallocation
|
|
1137
|
+
'22.3', # incompatibleFileOpen
|
|
1138
|
+
'22.4', # writeReadOnlyFile
|
|
1139
|
+
'22.6' # useClosedFile
|
|
1140
|
+
]
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
def generateTable():
|
|
1144
|
+
# print table
|
|
1145
|
+
numberOfRules = {}
|
|
1146
|
+
numberOfRules[1] = 3
|
|
1147
|
+
numberOfRules[2] = 7
|
|
1148
|
+
numberOfRules[3] = 2
|
|
1149
|
+
numberOfRules[4] = 2
|
|
1150
|
+
numberOfRules[5] = 9
|
|
1151
|
+
numberOfRules[6] = 2
|
|
1152
|
+
numberOfRules[7] = 4
|
|
1153
|
+
numberOfRules[8] = 14
|
|
1154
|
+
numberOfRules[9] = 5
|
|
1155
|
+
numberOfRules[10] = 8
|
|
1156
|
+
numberOfRules[11] = 9
|
|
1157
|
+
numberOfRules[12] = 4
|
|
1158
|
+
numberOfRules[13] = 6
|
|
1159
|
+
numberOfRules[14] = 4
|
|
1160
|
+
numberOfRules[15] = 7
|
|
1161
|
+
numberOfRules[16] = 7
|
|
1162
|
+
numberOfRules[17] = 8
|
|
1163
|
+
numberOfRules[18] = 8
|
|
1164
|
+
numberOfRules[19] = 2
|
|
1165
|
+
numberOfRules[20] = 14
|
|
1166
|
+
numberOfRules[21] = 21
|
|
1167
|
+
numberOfRules[22] = 10
|
|
1168
|
+
|
|
1169
|
+
# Rules that can be checked with compilers:
|
|
1170
|
+
# compiler = ['1.1', '1.2']
|
|
1171
|
+
|
|
1172
|
+
addon = getAddonRules()
|
|
1173
|
+
cppcheck = getCppcheckRules()
|
|
1174
|
+
for i1 in range(1, 23):
|
|
1175
|
+
for i2 in range(1, numberOfRules[i1] + 1):
|
|
1176
|
+
num = str(i1) + '.' + str(i2)
|
|
1177
|
+
s = ''
|
|
1178
|
+
if num in addon:
|
|
1179
|
+
s = 'X (Addon)'
|
|
1180
|
+
elif num in cppcheck:
|
|
1181
|
+
s = 'X (Cppcheck)'
|
|
1182
|
+
num = num + ' '
|
|
1183
|
+
print(num[:8] + s)
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
def remove_file_prefix(file_path, prefix):
|
|
1187
|
+
"""
|
|
1188
|
+
Remove a file path prefix from a give path. leftover
|
|
1189
|
+
directory separators at the beginning of a file
|
|
1190
|
+
after the removal are also stripped.
|
|
1191
|
+
|
|
1192
|
+
Example:
|
|
1193
|
+
'/remove/this/path/file.c'
|
|
1194
|
+
with a prefix of:
|
|
1195
|
+
'/remove/this/path'
|
|
1196
|
+
becomes:
|
|
1197
|
+
file.c
|
|
1198
|
+
"""
|
|
1199
|
+
result = None
|
|
1200
|
+
if file_path.startswith(prefix):
|
|
1201
|
+
result = file_path[len(prefix):]
|
|
1202
|
+
# Remove any leftover directory separators at the
|
|
1203
|
+
# beginning
|
|
1204
|
+
result = result.lstrip('\\/')
|
|
1205
|
+
else:
|
|
1206
|
+
result = file_path
|
|
1207
|
+
return result
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
class Rule(object):
|
|
1211
|
+
"""Class to keep rule text and metadata"""
|
|
1212
|
+
|
|
1213
|
+
MISRA_SEVERITY_LEVELS = ['Required', 'Mandatory', 'Advisory']
|
|
1214
|
+
|
|
1215
|
+
def __init__(self, num1, num2):
|
|
1216
|
+
self.num1 = num1
|
|
1217
|
+
self.num2 = num2
|
|
1218
|
+
self.text = ''
|
|
1219
|
+
self.misra_severity = ''
|
|
1220
|
+
|
|
1221
|
+
@property
|
|
1222
|
+
def num(self):
|
|
1223
|
+
return self.num1 * 100 + self.num2
|
|
1224
|
+
|
|
1225
|
+
@property
|
|
1226
|
+
def misra_severity(self):
|
|
1227
|
+
return self._misra_severity
|
|
1228
|
+
|
|
1229
|
+
@misra_severity.setter
|
|
1230
|
+
def misra_severity(self, val):
|
|
1231
|
+
if val in self.MISRA_SEVERITY_LEVELS:
|
|
1232
|
+
self._misra_severity = val
|
|
1233
|
+
else:
|
|
1234
|
+
self._misra_severity = ''
|
|
1235
|
+
|
|
1236
|
+
@property
|
|
1237
|
+
def cppcheck_severity(self):
|
|
1238
|
+
return 'style'
|
|
1239
|
+
|
|
1240
|
+
def __repr__(self):
|
|
1241
|
+
return "%d.%d (%s)" % (self.num1, self.num2, self.misra_severity)
|
|
1242
|
+
|
|
1243
|
+
|
|
1244
|
+
class MisraSettings(object):
|
|
1245
|
+
"""Hold settings for misra.py script."""
|
|
1246
|
+
|
|
1247
|
+
__slots__ = ["verify", "quiet", "show_summary"]
|
|
1248
|
+
|
|
1249
|
+
def __init__(self, args):
|
|
1250
|
+
"""
|
|
1251
|
+
:param args: Arguments given by argparse.
|
|
1252
|
+
"""
|
|
1253
|
+
self.verify = False
|
|
1254
|
+
self.quiet = False
|
|
1255
|
+
self.show_summary = True
|
|
1256
|
+
|
|
1257
|
+
if args.verify:
|
|
1258
|
+
self.verify = True
|
|
1259
|
+
if args.cli:
|
|
1260
|
+
self.quiet = True
|
|
1261
|
+
self.show_summary = False
|
|
1262
|
+
if args.quiet:
|
|
1263
|
+
self.quiet = True
|
|
1264
|
+
if args.no_summary:
|
|
1265
|
+
self.show_summary = False
|
|
1266
|
+
|
|
1267
|
+
def __repr__(self):
|
|
1268
|
+
attrs = ["verify", "quiet", "show_summary", "verify"]
|
|
1269
|
+
return "{}({})".format(
|
|
1270
|
+
"MisraSettings",
|
|
1271
|
+
", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs))
|
|
1272
|
+
)
|
|
1273
|
+
|
|
1274
|
+
|
|
1275
|
+
class MisraChecker:
|
|
1276
|
+
|
|
1277
|
+
def __init__(self, settings, stdversion="c89"):
|
|
1278
|
+
"""
|
|
1279
|
+
:param settings: misra.py script settings.
|
|
1280
|
+
"""
|
|
1281
|
+
|
|
1282
|
+
self.settings = settings
|
|
1283
|
+
|
|
1284
|
+
# Test validation rules lists
|
|
1285
|
+
self.verify_expected = list()
|
|
1286
|
+
self.verify_actual = list()
|
|
1287
|
+
|
|
1288
|
+
# List of formatted violation messages
|
|
1289
|
+
self.violations = dict()
|
|
1290
|
+
|
|
1291
|
+
# if --rule-texts is specified this dictionary
|
|
1292
|
+
# is loaded with descriptions of each rule
|
|
1293
|
+
# by rule number (in hundreds).
|
|
1294
|
+
# ie rule 1.2 becomes 102
|
|
1295
|
+
self.ruleTexts = dict()
|
|
1296
|
+
|
|
1297
|
+
# Dictionary of dictionaries for rules to suppress
|
|
1298
|
+
# Dict1 is keyed by rule number in the hundreds format of
|
|
1299
|
+
# Major * 100 + minor. ie Rule 5.2 = (5*100) + 2
|
|
1300
|
+
# Dict 2 is keyed by filename. An entry of None means suppress globally.
|
|
1301
|
+
# Each file name entry contains a list of tuples of (lineNumber, symbolName)
|
|
1302
|
+
# or an item of None which indicates suppress rule for the entire file.
|
|
1303
|
+
# The line and symbol name tuple may have None as either of its elements but
|
|
1304
|
+
# should not be None for both.
|
|
1305
|
+
self.suppressedRules = dict()
|
|
1306
|
+
|
|
1307
|
+
# Prefix to ignore when matching suppression files.
|
|
1308
|
+
self.filePrefix = None
|
|
1309
|
+
|
|
1310
|
+
# Number of all violations suppressed per rule
|
|
1311
|
+
self.suppressionStats = dict()
|
|
1312
|
+
|
|
1313
|
+
self.stdversion = stdversion
|
|
1314
|
+
|
|
1315
|
+
self.severity = None
|
|
1316
|
+
|
|
1317
|
+
self.existing_violations = set()
|
|
1318
|
+
|
|
1319
|
+
self._ctu_summary_typedefs = False
|
|
1320
|
+
self._ctu_summary_tagnames = False
|
|
1321
|
+
self._ctu_summary_identifiers = False
|
|
1322
|
+
self._ctu_summary_usage = False
|
|
1323
|
+
|
|
1324
|
+
self.path_premium_addon = None
|
|
1325
|
+
|
|
1326
|
+
def __repr__(self):
|
|
1327
|
+
attrs = ["settings", "verify_expected", "verify_actual", "violations",
|
|
1328
|
+
"ruleTexts", "suppressedRules", "filePrefix",
|
|
1329
|
+
"suppressionStats", "stdversion", "severity"]
|
|
1330
|
+
return "{}({})".format(
|
|
1331
|
+
"MisraChecker",
|
|
1332
|
+
", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs))
|
|
1333
|
+
)
|
|
1334
|
+
|
|
1335
|
+
def get_num_significant_naming_chars(self, cfg):
|
|
1336
|
+
if cfg.standards and cfg.standards.c == "c89":
|
|
1337
|
+
return 31
|
|
1338
|
+
else:
|
|
1339
|
+
return 63
|
|
1340
|
+
|
|
1341
|
+
def _save_ctu_summary_typedefs(self, dumpfile, typedef_info):
|
|
1342
|
+
if self._ctu_summary_typedefs:
|
|
1343
|
+
return
|
|
1344
|
+
|
|
1345
|
+
self._ctu_summary_typedefs = True
|
|
1346
|
+
|
|
1347
|
+
summary = []
|
|
1348
|
+
for ti in typedef_info:
|
|
1349
|
+
summary.append({ 'name': ti.name, 'file': ti.file, 'line': ti.linenr, 'column': ti.column, 'used': ti.used })
|
|
1350
|
+
if len(summary) > 0:
|
|
1351
|
+
cppcheckdata.reportSummary(dumpfile, 'MisraTypedefInfo', summary)
|
|
1352
|
+
|
|
1353
|
+
def _save_ctu_summary_tagnames(self, dumpfile, cfg):
|
|
1354
|
+
if self._ctu_summary_tagnames:
|
|
1355
|
+
return
|
|
1356
|
+
|
|
1357
|
+
self._ctu_summary_tagnames = True
|
|
1358
|
+
|
|
1359
|
+
summary = []
|
|
1360
|
+
# structs/enums
|
|
1361
|
+
for scope in cfg.scopes:
|
|
1362
|
+
if scope.className is None:
|
|
1363
|
+
continue
|
|
1364
|
+
if scope.type not in ('Struct', 'Enum'):
|
|
1365
|
+
continue
|
|
1366
|
+
used = False
|
|
1367
|
+
tok = scope.bodyEnd
|
|
1368
|
+
while tok:
|
|
1369
|
+
if tok.str == scope.className:
|
|
1370
|
+
used = True
|
|
1371
|
+
break
|
|
1372
|
+
tok = tok.next
|
|
1373
|
+
summary.append({'name': scope.className, 'used':used, 'file': scope.bodyStart.file, 'line': scope.bodyStart.linenr, 'column': scope.bodyStart.column})
|
|
1374
|
+
if len(summary) > 0:
|
|
1375
|
+
cppcheckdata.reportSummary(dumpfile, 'MisraTagName', summary)
|
|
1376
|
+
|
|
1377
|
+
def _save_ctu_summary_identifiers(self, dumpfile, cfg):
|
|
1378
|
+
if self._ctu_summary_identifiers:
|
|
1379
|
+
return
|
|
1380
|
+
self._ctu_summary_identifiers = True
|
|
1381
|
+
|
|
1382
|
+
external_identifiers = []
|
|
1383
|
+
internal_identifiers = []
|
|
1384
|
+
local_identifiers = []
|
|
1385
|
+
|
|
1386
|
+
def identifier(nameToken):
|
|
1387
|
+
return {'name':nameToken.str, 'file':nameToken.file, 'line':nameToken.linenr, 'column':nameToken.column}
|
|
1388
|
+
|
|
1389
|
+
names = []
|
|
1390
|
+
|
|
1391
|
+
for var in cfg.variables:
|
|
1392
|
+
if var.nameToken is None:
|
|
1393
|
+
continue
|
|
1394
|
+
if var.access != 'Global':
|
|
1395
|
+
if var.nameToken.str in names:
|
|
1396
|
+
continue
|
|
1397
|
+
names.append(var.nameToken.str)
|
|
1398
|
+
local_identifiers.append(identifier(var.nameToken))
|
|
1399
|
+
elif var.isStatic:
|
|
1400
|
+
names.append(var.nameToken.str)
|
|
1401
|
+
i = identifier(var.nameToken)
|
|
1402
|
+
i['inlinefunc'] = False
|
|
1403
|
+
internal_identifiers.append(i)
|
|
1404
|
+
else:
|
|
1405
|
+
names.append(var.nameToken.str)
|
|
1406
|
+
i = identifier(var.nameToken)
|
|
1407
|
+
i['decl'] = var.isExtern
|
|
1408
|
+
external_identifiers.append(i)
|
|
1409
|
+
|
|
1410
|
+
for func in cfg.functions:
|
|
1411
|
+
if func.tokenDef is None:
|
|
1412
|
+
continue
|
|
1413
|
+
if func.isStatic:
|
|
1414
|
+
i = identifier(func.tokenDef)
|
|
1415
|
+
i['inlinefunc'] = func.isInlineKeyword
|
|
1416
|
+
internal_identifiers.append(i)
|
|
1417
|
+
else:
|
|
1418
|
+
if func.token is None:
|
|
1419
|
+
i = identifier(func.tokenDef)
|
|
1420
|
+
else:
|
|
1421
|
+
i = identifier(func.token)
|
|
1422
|
+
i['decl'] = func.token is None
|
|
1423
|
+
external_identifiers.append(i)
|
|
1424
|
+
|
|
1425
|
+
cppcheckdata.reportSummary(dumpfile, 'MisraExternalIdentifiers', external_identifiers)
|
|
1426
|
+
cppcheckdata.reportSummary(dumpfile, 'MisraInternalIdentifiers', internal_identifiers)
|
|
1427
|
+
cppcheckdata.reportSummary(dumpfile, 'MisraLocalIdentifiers', local_identifiers)
|
|
1428
|
+
|
|
1429
|
+
def _save_ctu_summary_usage(self, dumpfile, cfg):
|
|
1430
|
+
if self._ctu_summary_usage:
|
|
1431
|
+
return
|
|
1432
|
+
self._ctu_summary_usage = True
|
|
1433
|
+
|
|
1434
|
+
names = []
|
|
1435
|
+
for token in cfg.tokenlist:
|
|
1436
|
+
if not token.isName:
|
|
1437
|
+
continue
|
|
1438
|
+
if token.function and token.scope.isExecutable:
|
|
1439
|
+
if (not token.function.isStatic) and (token.str not in names):
|
|
1440
|
+
names.append({'name': token.str, 'file': token.file})
|
|
1441
|
+
elif token.variable:
|
|
1442
|
+
if token == token.variable.nameToken:
|
|
1443
|
+
continue
|
|
1444
|
+
if token.variable.access == 'Global' and (not token.variable.isStatic) and (token.str not in names):
|
|
1445
|
+
names.append({'name': token.str, 'file': token.file})
|
|
1446
|
+
|
|
1447
|
+
if len(names) > 0:
|
|
1448
|
+
cppcheckdata.reportSummary(dumpfile, 'MisraUsage', names)
|
|
1449
|
+
|
|
1450
|
+
|
|
1451
|
+
def misra_1_4(self, cfg):
|
|
1452
|
+
for token in cfg.tokenlist:
|
|
1453
|
+
if token.str in ('_Atomic', '_Noreturn', '_Generic', '_Thread_local', '_Alignas', '_Alignof'):
|
|
1454
|
+
self.reportError(token, 1, 4)
|
|
1455
|
+
if token.str.endswith('_s') and isFunctionCall(token.next):
|
|
1456
|
+
# See C specification C11 - Annex K, page 578
|
|
1457
|
+
if token.str in ('tmpfile_s', 'tmpnam_s', 'fopen_s', 'freopen_s', 'fprintf_s', 'fscanf_s', 'printf_s', 'scanf_s',
|
|
1458
|
+
'snprintf_s', 'sprintf_s', 'sscanf_s', 'vfprintf_s', 'vfscanf_s', 'vprintf_s', 'vscanf_s',
|
|
1459
|
+
'vsnprintf_s', 'vsprintf_s', 'vsscanf_s', 'gets_s', 'set_constraint_handler_s', 'abort_handler_s',
|
|
1460
|
+
'ignore_handler_s', 'getenv_s', 'bsearch_s', 'qsort_s', 'wctomb_s', 'mbstowcs_s', 'wcstombs_s',
|
|
1461
|
+
'memcpy_s', 'memmove_s', 'strcpy_s', 'strncpy_s', 'strcat_s', 'strncat_s', 'strtok_s', 'memset_s',
|
|
1462
|
+
'strerror_s', 'strerrorlen_s', 'strnlen_s', 'asctime_s', 'ctime_s', 'gmtime_s', 'localtime_s',
|
|
1463
|
+
'fwprintf_s', 'fwscanf_s', 'snwprintf_s', 'swprintf_s', 'swscanf_s', 'vfwprintf_s', 'vfwscanf_s',
|
|
1464
|
+
'vsnwprintf_s', 'vswprintf_s', 'vswscanf_s', 'vwprintf_s', 'vwscanf_s', 'wprintf_s', 'wscanf_s',
|
|
1465
|
+
'wcscpy_s', 'wcsncpy_s', 'wmemcpy_s', 'wmemmove_s', 'wcscat_s', 'wcsncat_s', 'wcstok_s', 'wcsnlen_s',
|
|
1466
|
+
'wcrtomb_s', 'mbsrtowcs_s', 'wcsrtombs_s'):
|
|
1467
|
+
self.reportError(token, 1, 4)
|
|
1468
|
+
|
|
1469
|
+
def misra_2_2(self, cfg):
|
|
1470
|
+
for token in cfg.tokenlist:
|
|
1471
|
+
if token.isExpandedMacro:
|
|
1472
|
+
continue
|
|
1473
|
+
if (token.str in '+-') and token.astOperand2:
|
|
1474
|
+
if simpleMatch(token.astOperand1, '0'):
|
|
1475
|
+
self.reportError(token.astOperand1, 2, 2)
|
|
1476
|
+
elif simpleMatch(token.astOperand2, '0'):
|
|
1477
|
+
self.reportError(token.astOperand2, 2, 2)
|
|
1478
|
+
if token.str == '*' and token.astOperand2:
|
|
1479
|
+
if simpleMatch(token.astOperand2, '0'):
|
|
1480
|
+
self.reportError(token.astOperand1, 2, 2)
|
|
1481
|
+
elif simpleMatch(token.astOperand1, '0'):
|
|
1482
|
+
self.reportError(token.astOperand2, 2, 2)
|
|
1483
|
+
elif simpleMatch(token.astOperand1, '1'):
|
|
1484
|
+
self.reportError(token.astOperand1, 2, 2)
|
|
1485
|
+
elif simpleMatch(token.astOperand2, '1'):
|
|
1486
|
+
self.reportError(token.astOperand2, 2, 2)
|
|
1487
|
+
|
|
1488
|
+
def misra_2_3(self, dumpfile, typedefInfo):
|
|
1489
|
+
self._save_ctu_summary_typedefs(dumpfile, typedefInfo)
|
|
1490
|
+
|
|
1491
|
+
def misra_2_4(self, dumpfile, cfg):
|
|
1492
|
+
self._save_ctu_summary_tagnames(dumpfile, cfg)
|
|
1493
|
+
|
|
1494
|
+
def misra_2_5(self, dumpfile, cfg):
|
|
1495
|
+
used_macros = list()
|
|
1496
|
+
for m in cfg.macro_usage:
|
|
1497
|
+
used_macros.append(m.name)
|
|
1498
|
+
summary = []
|
|
1499
|
+
for directive in cfg.directives:
|
|
1500
|
+
res = re.match(r'#define[ \t]+([a-zA-Z_][a-zA-Z_0-9]*).*', directive.str)
|
|
1501
|
+
if res:
|
|
1502
|
+
macro_name = res.group(1)
|
|
1503
|
+
summary.append({'name': macro_name, 'used': (macro_name in used_macros), 'file': directive.file, 'line': directive.linenr, 'column': directive.column})
|
|
1504
|
+
if len(summary) > 0:
|
|
1505
|
+
cppcheckdata.reportSummary(dumpfile, 'MisraMacro', summary)
|
|
1506
|
+
|
|
1507
|
+
def misra_2_7(self, data):
|
|
1508
|
+
for func in data.functions:
|
|
1509
|
+
# Skip function with no parameter
|
|
1510
|
+
if len(func.argument) == 0:
|
|
1511
|
+
continue
|
|
1512
|
+
# Setup list of function parameters
|
|
1513
|
+
func_param_list = list()
|
|
1514
|
+
for arg in func.argument:
|
|
1515
|
+
func_arg = func.argument[arg]
|
|
1516
|
+
if func_arg.typeStartToken and func_arg.typeStartToken.str == '...':
|
|
1517
|
+
continue
|
|
1518
|
+
func_param_list.append(func_arg)
|
|
1519
|
+
# Search for scope of current function
|
|
1520
|
+
for scope in data.scopes:
|
|
1521
|
+
if (scope.type == "Function") and (scope.function == func):
|
|
1522
|
+
# Search function body: remove referenced function parameter from list
|
|
1523
|
+
token = scope.bodyStart
|
|
1524
|
+
while token.next is not None and token != scope.bodyEnd and len(func_param_list) > 0:
|
|
1525
|
+
if token.variable is not None and token.variable in func_param_list:
|
|
1526
|
+
func_param_list.remove(token.variable)
|
|
1527
|
+
token = token.next
|
|
1528
|
+
# Emit a warning for each unused variable, but no more that one warning per line
|
|
1529
|
+
reported_linenrs = set()
|
|
1530
|
+
for func_param in func_param_list:
|
|
1531
|
+
if func_param.nameToken:
|
|
1532
|
+
linenr = func_param.nameToken
|
|
1533
|
+
if linenr not in reported_linenrs:
|
|
1534
|
+
self.reportError(func_param.nameToken, 2, 7)
|
|
1535
|
+
reported_linenrs.add(linenr)
|
|
1536
|
+
else:
|
|
1537
|
+
linenr = func.tokenDef.linenr
|
|
1538
|
+
if linenr not in reported_linenrs:
|
|
1539
|
+
self.reportError(func.tokenDef, 2, 7)
|
|
1540
|
+
reported_linenrs.add(linenr)
|
|
1541
|
+
|
|
1542
|
+
def misra_3_1(self, rawTokens):
|
|
1543
|
+
for token in rawTokens:
|
|
1544
|
+
starts_with_double_slash = token.str.startswith('//')
|
|
1545
|
+
if token.str.startswith('/*') or starts_with_double_slash:
|
|
1546
|
+
s = token.str.lstrip('/')
|
|
1547
|
+
if ((not starts_with_double_slash) and '//' in s) or '/*' in s:
|
|
1548
|
+
self.reportError(token, 3, 1)
|
|
1549
|
+
|
|
1550
|
+
def misra_3_2(self, rawTokens):
|
|
1551
|
+
for token in rawTokens:
|
|
1552
|
+
if token.str.startswith('//'):
|
|
1553
|
+
# Check for comment ends with trigraph which might be replaced
|
|
1554
|
+
# by a backslash.
|
|
1555
|
+
if token.str.endswith('??/'):
|
|
1556
|
+
self.reportError(token, 3, 2)
|
|
1557
|
+
# Check for comment which has been merged with subsequent line
|
|
1558
|
+
# because it ends with backslash.
|
|
1559
|
+
# The last backslash is no more part of the comment token thus
|
|
1560
|
+
# check if next token exists and compare line numbers.
|
|
1561
|
+
elif (token.next is not None) and (token.linenr == token.next.linenr):
|
|
1562
|
+
self.reportError(token, 3, 2)
|
|
1563
|
+
|
|
1564
|
+
def misra_4_1(self, rawTokens):
|
|
1565
|
+
for token in rawTokens:
|
|
1566
|
+
if (token.str[0] != '"') and (token.str[0] != '\''):
|
|
1567
|
+
continue
|
|
1568
|
+
if len(token.str) < 3:
|
|
1569
|
+
continue
|
|
1570
|
+
|
|
1571
|
+
delimiter = token.str[0]
|
|
1572
|
+
symbols = token.str[1:-1]
|
|
1573
|
+
|
|
1574
|
+
# No closing delimiter. This will not compile.
|
|
1575
|
+
if token.str[-1] != delimiter:
|
|
1576
|
+
continue
|
|
1577
|
+
|
|
1578
|
+
if len(symbols) < 2:
|
|
1579
|
+
continue
|
|
1580
|
+
|
|
1581
|
+
if not hasNumericEscapeSequence(symbols):
|
|
1582
|
+
continue
|
|
1583
|
+
|
|
1584
|
+
# String literals that contains one or more escape sequences. All of them should be
|
|
1585
|
+
# terminated.
|
|
1586
|
+
for sequence in ['\\' + t for t in symbols.split('\\')][1:]:
|
|
1587
|
+
if (isHexEscapeSequence(sequence) or isOctalEscapeSequence(sequence) or
|
|
1588
|
+
isSimpleEscapeSequence(sequence)):
|
|
1589
|
+
continue
|
|
1590
|
+
else:
|
|
1591
|
+
self.reportError(token, 4, 1)
|
|
1592
|
+
|
|
1593
|
+
def misra_4_2(self, rawTokens):
|
|
1594
|
+
for token in rawTokens:
|
|
1595
|
+
if (token.str[0] != '"') or (token.str[-1] != '"'):
|
|
1596
|
+
continue
|
|
1597
|
+
# Check for trigraph sequence as defined by ISO/IEC 9899:1999
|
|
1598
|
+
for sequence in ['??=', '??(', '??/', '??)', '??\'', '??<', '??!', '??>', '??-']:
|
|
1599
|
+
if sequence in token.str[1:-1]:
|
|
1600
|
+
# First trigraph sequence match, report error and leave loop.
|
|
1601
|
+
self.reportError(token, 4, 2)
|
|
1602
|
+
break
|
|
1603
|
+
|
|
1604
|
+
def misra_5_1(self, data):
|
|
1605
|
+
long_vars = {}
|
|
1606
|
+
num_sign_chars = self.get_num_significant_naming_chars(data)
|
|
1607
|
+
for var in data.variables:
|
|
1608
|
+
if var.nameToken is None:
|
|
1609
|
+
continue
|
|
1610
|
+
if len(var.nameToken.str) <= num_sign_chars:
|
|
1611
|
+
continue
|
|
1612
|
+
if not hasExternalLinkage(var):
|
|
1613
|
+
continue
|
|
1614
|
+
long_vars.setdefault(var.nameToken.str[:num_sign_chars], []).append(var.nameToken)
|
|
1615
|
+
for name_prefix in long_vars:
|
|
1616
|
+
tokens = long_vars[name_prefix]
|
|
1617
|
+
if len(tokens) < 2:
|
|
1618
|
+
continue
|
|
1619
|
+
for tok in sorted(tokens, key=lambda t: (t.linenr, t.column))[1:]:
|
|
1620
|
+
self.reportError(tok, 5, 1)
|
|
1621
|
+
|
|
1622
|
+
def misra_5_2(self, data):
|
|
1623
|
+
scopeVars = {}
|
|
1624
|
+
num_sign_chars = self.get_num_significant_naming_chars(data)
|
|
1625
|
+
for var in data.variables:
|
|
1626
|
+
if var.nameToken is None:
|
|
1627
|
+
continue
|
|
1628
|
+
if len(var.nameToken.str) <= num_sign_chars:
|
|
1629
|
+
continue
|
|
1630
|
+
if var.nameToken.scope not in scopeVars:
|
|
1631
|
+
scopeVars.setdefault(var.nameToken.scope, {})["varlist"] = []
|
|
1632
|
+
scopeVars.setdefault(var.nameToken.scope, {})["scopelist"] = []
|
|
1633
|
+
scopeVars[var.nameToken.scope]["varlist"].append(var)
|
|
1634
|
+
for scope in data.scopes:
|
|
1635
|
+
if scope.nestedIn and scope.className:
|
|
1636
|
+
if scope.nestedIn not in scopeVars:
|
|
1637
|
+
scopeVars.setdefault(scope.nestedIn, {})["varlist"] = []
|
|
1638
|
+
scopeVars.setdefault(scope.nestedIn, {})["scopelist"] = []
|
|
1639
|
+
scopeVars[scope.nestedIn]["scopelist"].append(scope)
|
|
1640
|
+
for scope in scopeVars:
|
|
1641
|
+
if len(scopeVars[scope]["varlist"]) <= 1:
|
|
1642
|
+
continue
|
|
1643
|
+
for i, variable1 in enumerate(scopeVars[scope]["varlist"]):
|
|
1644
|
+
for variable2 in scopeVars[scope]["varlist"][i + 1:]:
|
|
1645
|
+
if variable1.isArgument and variable2.isArgument:
|
|
1646
|
+
continue
|
|
1647
|
+
if hasExternalLinkage(variable1) or hasExternalLinkage(variable2):
|
|
1648
|
+
continue
|
|
1649
|
+
if (variable1.nameToken.str[:num_sign_chars] == variable2.nameToken.str[:num_sign_chars] and
|
|
1650
|
+
variable1 is not variable2):
|
|
1651
|
+
if int(variable1.nameToken.linenr) > int(variable2.nameToken.linenr):
|
|
1652
|
+
self.reportError(variable1.nameToken, 5, 2)
|
|
1653
|
+
else:
|
|
1654
|
+
self.reportError(variable2.nameToken, 5, 2)
|
|
1655
|
+
for innerscope in scopeVars[scope]["scopelist"]:
|
|
1656
|
+
if variable1.nameToken.str[:num_sign_chars] == innerscope.className[:num_sign_chars]:
|
|
1657
|
+
if int(variable1.nameToken.linenr) > int(innerscope.bodyStart.linenr):
|
|
1658
|
+
self.reportError(variable1.nameToken, 5, 2)
|
|
1659
|
+
else:
|
|
1660
|
+
self.reportError(innerscope.bodyStart, 5, 2)
|
|
1661
|
+
if len(scopeVars[scope]["scopelist"]) <= 1:
|
|
1662
|
+
continue
|
|
1663
|
+
for i, scopename1 in enumerate(scopeVars[scope]["scopelist"]):
|
|
1664
|
+
for scopename2 in scopeVars[scope]["scopelist"][i + 1:]:
|
|
1665
|
+
if scopename1.className[:num_sign_chars] == scopename2.className[:num_sign_chars]:
|
|
1666
|
+
if int(scopename1.bodyStart.linenr) > int(scopename2.bodyStart.linenr):
|
|
1667
|
+
self.reportError(scopename1.bodyStart, 5, 2)
|
|
1668
|
+
else:
|
|
1669
|
+
self.reportError(scopename2.bodyStart, 5, 2)
|
|
1670
|
+
|
|
1671
|
+
def misra_5_4(self, data):
|
|
1672
|
+
num_sign_chars = self.get_num_significant_naming_chars(data)
|
|
1673
|
+
macro = {}
|
|
1674
|
+
compile_name = re.compile(r'#define ([a-zA-Z0-9_]+)')
|
|
1675
|
+
compile_param = re.compile(r'#define ([a-zA-Z0-9_]+)[(]([a-zA-Z0-9_, ]+)[)]')
|
|
1676
|
+
short_names = {}
|
|
1677
|
+
macro_w_arg = []
|
|
1678
|
+
for dir in data.directives:
|
|
1679
|
+
res1 = compile_name.match(dir.str)
|
|
1680
|
+
if res1:
|
|
1681
|
+
if dir not in macro:
|
|
1682
|
+
macro.setdefault(dir, {})["name"] = []
|
|
1683
|
+
macro.setdefault(dir, {})["params"] = []
|
|
1684
|
+
full_name = res1.group(1)
|
|
1685
|
+
macro[dir]["name"] = full_name
|
|
1686
|
+
short_name = full_name[:num_sign_chars]
|
|
1687
|
+
if short_name in short_names:
|
|
1688
|
+
_dir = short_names[short_name]
|
|
1689
|
+
if full_name != macro[_dir]["name"]:
|
|
1690
|
+
self.reportError(dir, 5, 4)
|
|
1691
|
+
else:
|
|
1692
|
+
short_names[short_name] = dir
|
|
1693
|
+
res2 = compile_param.match(dir.str)
|
|
1694
|
+
if res2:
|
|
1695
|
+
res_gp2 = res2.group(2).split(",")
|
|
1696
|
+
res_gp2 = [macroname.replace(" ", "") for macroname in res_gp2]
|
|
1697
|
+
macro[dir]["params"].extend(res_gp2)
|
|
1698
|
+
macro_w_arg.append(dir)
|
|
1699
|
+
for mvar in macro_w_arg:
|
|
1700
|
+
for i, macroparam1 in enumerate(macro[mvar]["params"]):
|
|
1701
|
+
for j, macroparam2 in enumerate(macro[mvar]["params"]):
|
|
1702
|
+
if j > i and macroparam1[:num_sign_chars] == macroparam2[:num_sign_chars]:
|
|
1703
|
+
self.reportError(mvar, 5, 4)
|
|
1704
|
+
param = macroparam1
|
|
1705
|
+
if param[:num_sign_chars] in short_names:
|
|
1706
|
+
m_var1 = short_names[param[:num_sign_chars]]
|
|
1707
|
+
if m_var1.linenr > mvar.linenr:
|
|
1708
|
+
self.reportError(m_var1, 5, 4)
|
|
1709
|
+
else:
|
|
1710
|
+
self.reportError(mvar, 5, 4)
|
|
1711
|
+
|
|
1712
|
+
def misra_5_5(self, data):
|
|
1713
|
+
num_sign_chars = self.get_num_significant_naming_chars(data)
|
|
1714
|
+
macroNames = {}
|
|
1715
|
+
compiled = re.compile(r'#define ([A-Za-z0-9_]+)')
|
|
1716
|
+
for dir in data.directives:
|
|
1717
|
+
res = compiled.match(dir.str)
|
|
1718
|
+
if res:
|
|
1719
|
+
macroNames[res.group(1)[:num_sign_chars]] = dir
|
|
1720
|
+
for var in data.variables:
|
|
1721
|
+
if var.nameToken and var.nameToken.str[:num_sign_chars] in macroNames:
|
|
1722
|
+
self.reportError(var.nameToken, 5, 5)
|
|
1723
|
+
for scope in data.scopes:
|
|
1724
|
+
if scope.className and scope.className[:num_sign_chars] in macroNames:
|
|
1725
|
+
self.reportError(scope.bodyStart, 5, 5)
|
|
1726
|
+
|
|
1727
|
+
|
|
1728
|
+
def misra_5_6(self, dumpfile, typedefInfo):
|
|
1729
|
+
self._save_ctu_summary_typedefs(dumpfile, typedefInfo)
|
|
1730
|
+
|
|
1731
|
+
def misra_5_7(self, dumpfile, cfg):
|
|
1732
|
+
self._save_ctu_summary_tagnames(dumpfile, cfg)
|
|
1733
|
+
|
|
1734
|
+
def misra_5_8(self, dumpfile, cfg):
|
|
1735
|
+
self._save_ctu_summary_identifiers(dumpfile, cfg)
|
|
1736
|
+
|
|
1737
|
+
def misra_5_9(self, dumpfile, cfg):
|
|
1738
|
+
self._save_ctu_summary_identifiers(dumpfile, cfg)
|
|
1739
|
+
|
|
1740
|
+
def misra_6_1(self, data):
|
|
1741
|
+
# Bitfield type must be bool or explicitly signed/unsigned int
|
|
1742
|
+
for token in data.tokenlist:
|
|
1743
|
+
if not token.valueType:
|
|
1744
|
+
continue
|
|
1745
|
+
if token.valueType.bits == 0:
|
|
1746
|
+
continue
|
|
1747
|
+
if not token.variable:
|
|
1748
|
+
continue
|
|
1749
|
+
if not token.scope:
|
|
1750
|
+
continue
|
|
1751
|
+
if token.scope.type not in 'Struct':
|
|
1752
|
+
continue
|
|
1753
|
+
|
|
1754
|
+
if data.standards.c == 'c89':
|
|
1755
|
+
if token.valueType.type != 'int' and not isUnsignedType(token.variable.typeStartToken.str):
|
|
1756
|
+
self.reportError(token, 6, 1)
|
|
1757
|
+
elif data.standards.c == 'c99':
|
|
1758
|
+
if token.valueType.type == 'bool':
|
|
1759
|
+
continue
|
|
1760
|
+
|
|
1761
|
+
isExplicitlySignedOrUnsigned = False
|
|
1762
|
+
typeToken = token.variable.typeStartToken
|
|
1763
|
+
while typeToken:
|
|
1764
|
+
if typeToken.isUnsigned or typeToken.isSigned or isUnsignedType(typeToken.str):
|
|
1765
|
+
isExplicitlySignedOrUnsigned = True
|
|
1766
|
+
break
|
|
1767
|
+
|
|
1768
|
+
if typeToken is token.variable.typeEndToken:
|
|
1769
|
+
break
|
|
1770
|
+
|
|
1771
|
+
typeToken = typeToken.next
|
|
1772
|
+
|
|
1773
|
+
if not isExplicitlySignedOrUnsigned:
|
|
1774
|
+
self.reportError(token, 6, 1)
|
|
1775
|
+
|
|
1776
|
+
|
|
1777
|
+
def misra_6_2(self, data):
|
|
1778
|
+
# Bitfields of size 1 can not be signed
|
|
1779
|
+
for token in data.tokenlist:
|
|
1780
|
+
if not token.valueType:
|
|
1781
|
+
continue
|
|
1782
|
+
if not token.scope:
|
|
1783
|
+
continue
|
|
1784
|
+
if token.scope.type not in 'Struct':
|
|
1785
|
+
continue
|
|
1786
|
+
if token.valueType.bits == 1 and token.valueType.sign == 'signed':
|
|
1787
|
+
self.reportError(token, 6, 2)
|
|
1788
|
+
|
|
1789
|
+
|
|
1790
|
+
def misra_7_1(self, rawTokens):
|
|
1791
|
+
compiled = re.compile(r'^0[0-7]+$')
|
|
1792
|
+
for tok in rawTokens:
|
|
1793
|
+
if compiled.match(tok.str):
|
|
1794
|
+
self.reportError(tok, 7, 1)
|
|
1795
|
+
|
|
1796
|
+
def misra_7_2(self, data):
|
|
1797
|
+
for token in data.tokenlist:
|
|
1798
|
+
if token.isInt and ('U' not in token.str.upper()) and token.valueType and token.valueType.sign == 'unsigned':
|
|
1799
|
+
self.reportError(token, 7, 2)
|
|
1800
|
+
|
|
1801
|
+
def misra_7_3(self, rawTokens):
|
|
1802
|
+
compiled = re.compile(r'^[0-9.]+[Uu]*l+[Uu]*$')
|
|
1803
|
+
for tok in rawTokens:
|
|
1804
|
+
if compiled.match(tok.str):
|
|
1805
|
+
self.reportError(tok, 7, 3)
|
|
1806
|
+
|
|
1807
|
+
def misra_7_4(self, data):
|
|
1808
|
+
# A string literal shall not be assigned to an object unless the object's type
|
|
1809
|
+
# is constant.
|
|
1810
|
+
def reportErrorIfVariableIsNotConst(variable, stringLiteral):
|
|
1811
|
+
if variable.valueType:
|
|
1812
|
+
if (variable.valueType.constness % 2) != 1:
|
|
1813
|
+
self.reportError(stringLiteral, 7, 4)
|
|
1814
|
+
|
|
1815
|
+
for token in data.tokenlist:
|
|
1816
|
+
if token.isString:
|
|
1817
|
+
# Check normal variable assignment
|
|
1818
|
+
variable = getAssignedVariableToken(token)
|
|
1819
|
+
if variable:
|
|
1820
|
+
reportErrorIfVariableIsNotConst(variable, token)
|
|
1821
|
+
|
|
1822
|
+
# Check use as return value
|
|
1823
|
+
function = getFunctionUsingReturnValue(token)
|
|
1824
|
+
if function:
|
|
1825
|
+
# "Primitive" test since there is no info available on return value type
|
|
1826
|
+
if not tokenFollowsSequence(function.tokenDef, ['const', 'char', '*']):
|
|
1827
|
+
self.reportError(token, 7, 4)
|
|
1828
|
+
|
|
1829
|
+
# Check use as function parameter
|
|
1830
|
+
if isFunctionCall(token) and token.astOperand1 and token.astOperand1.function:
|
|
1831
|
+
functionDeclaration = token.astOperand1.function
|
|
1832
|
+
|
|
1833
|
+
if functionDeclaration.tokenDef:
|
|
1834
|
+
if functionDeclaration.tokenDef is token.astOperand1:
|
|
1835
|
+
# Token is not a function call, but it is the definition of the function
|
|
1836
|
+
continue
|
|
1837
|
+
|
|
1838
|
+
parametersUsed = getArguments(token)
|
|
1839
|
+
for i in range(len(parametersUsed)):
|
|
1840
|
+
usedParameter = parametersUsed[i]
|
|
1841
|
+
parameterDefinition = functionDeclaration.argument.get(i+1)
|
|
1842
|
+
|
|
1843
|
+
if usedParameter.isString and parameterDefinition and parameterDefinition.nameToken:
|
|
1844
|
+
reportErrorIfVariableIsNotConst(parameterDefinition.nameToken, usedParameter)
|
|
1845
|
+
|
|
1846
|
+
def misra_8_1(self, cfg):
|
|
1847
|
+
for token in cfg.tokenlist:
|
|
1848
|
+
if token.isImplicitInt and not token.isUnsigned and not token.isSigned:
|
|
1849
|
+
self.reportError(token, 8, 1)
|
|
1850
|
+
|
|
1851
|
+
def misra_8_2(self, data, rawTokens):
|
|
1852
|
+
def getFollowingRawTokens(rawTokens, token, count):
|
|
1853
|
+
following =[]
|
|
1854
|
+
for rawToken in rawTokens:
|
|
1855
|
+
if (rawToken.file == token.file and
|
|
1856
|
+
rawToken.linenr == token.linenr and
|
|
1857
|
+
rawToken.column == token.column):
|
|
1858
|
+
for _ in range(count):
|
|
1859
|
+
rawToken = rawToken.next
|
|
1860
|
+
# Skip comments
|
|
1861
|
+
while rawToken and (rawToken.str.startswith('/*') or rawToken.str.startswith('//')):
|
|
1862
|
+
rawToken = rawToken.next
|
|
1863
|
+
if rawToken is None:
|
|
1864
|
+
break
|
|
1865
|
+
following.append(rawToken)
|
|
1866
|
+
return following
|
|
1867
|
+
|
|
1868
|
+
# Zero arguments should be in form ( void )
|
|
1869
|
+
def checkZeroArguments(func, startCall, endCall):
|
|
1870
|
+
if not startCall.isRemovedVoidParameter and len(func.argument) == 0:
|
|
1871
|
+
if func.tokenDef.next:
|
|
1872
|
+
self.reportError(func.tokenDef.next, 8, 2)
|
|
1873
|
+
else:
|
|
1874
|
+
self.reportError(func.tokenDef, 8, 2)
|
|
1875
|
+
|
|
1876
|
+
def checkDeclarationArgumentsViolations(func, startCall, endCall):
|
|
1877
|
+
# Collect the tokens for the arguments in function definition
|
|
1878
|
+
argNameTokens = set()
|
|
1879
|
+
for arg in func.argument:
|
|
1880
|
+
argument = func.argument[arg]
|
|
1881
|
+
typeStartToken = argument.typeStartToken
|
|
1882
|
+
if typeStartToken is None:
|
|
1883
|
+
continue
|
|
1884
|
+
nameToken = argument.nameToken
|
|
1885
|
+
if nameToken is None:
|
|
1886
|
+
continue
|
|
1887
|
+
argNameTokens.add(nameToken)
|
|
1888
|
+
|
|
1889
|
+
# Check if we have the same number of variables in both the
|
|
1890
|
+
# declaration and the definition.
|
|
1891
|
+
#
|
|
1892
|
+
# TODO: We actually need to check if the names of the arguments are
|
|
1893
|
+
# the same. But we can't do this because we have no links to
|
|
1894
|
+
# variables in the arguments in function definition in the dump file.
|
|
1895
|
+
foundVariables = 0
|
|
1896
|
+
while startCall and startCall != endCall:
|
|
1897
|
+
if startCall.varId:
|
|
1898
|
+
foundVariables += 1
|
|
1899
|
+
startCall = startCall.next
|
|
1900
|
+
|
|
1901
|
+
if len(argNameTokens) != foundVariables:
|
|
1902
|
+
if func.tokenDef.next:
|
|
1903
|
+
self.reportError(func.tokenDef.next, 8, 2)
|
|
1904
|
+
else:
|
|
1905
|
+
self.reportError(func.tokenDef, 8, 2)
|
|
1906
|
+
|
|
1907
|
+
def checkDefinitionArgumentsViolations(func, startCall, endCall):
|
|
1908
|
+
for arg in func.argument:
|
|
1909
|
+
argument = func.argument[arg]
|
|
1910
|
+
typeStartToken = argument.typeStartToken
|
|
1911
|
+
if typeStartToken is None:
|
|
1912
|
+
continue
|
|
1913
|
+
|
|
1914
|
+
# Arguments should have a name unless variable length arg
|
|
1915
|
+
nameToken = argument.nameToken
|
|
1916
|
+
if nameToken is None and typeStartToken.str != '...':
|
|
1917
|
+
self.reportError(typeStartToken, 8, 2)
|
|
1918
|
+
|
|
1919
|
+
# Type declaration on next line (old style declaration list) is not allowed
|
|
1920
|
+
if typeStartToken.linenr > endCall.linenr:
|
|
1921
|
+
self.reportError(typeStartToken, 8, 2)
|
|
1922
|
+
|
|
1923
|
+
# Check arguments in function declaration
|
|
1924
|
+
for func in data.functions:
|
|
1925
|
+
|
|
1926
|
+
# Check arguments in function definition
|
|
1927
|
+
tokenImpl = func.token
|
|
1928
|
+
if tokenImpl:
|
|
1929
|
+
startCall = tokenImpl.next
|
|
1930
|
+
if startCall is None or startCall.str != '(':
|
|
1931
|
+
continue
|
|
1932
|
+
endCall = startCall.link
|
|
1933
|
+
if endCall is None or endCall.str != ')':
|
|
1934
|
+
continue
|
|
1935
|
+
checkZeroArguments(func, startCall, endCall)
|
|
1936
|
+
checkDefinitionArgumentsViolations(func, startCall, endCall)
|
|
1937
|
+
|
|
1938
|
+
# Check arguments in function declaration
|
|
1939
|
+
tokenDef = func.tokenDef
|
|
1940
|
+
if tokenDef:
|
|
1941
|
+
startCall = func.tokenDef.next
|
|
1942
|
+
if startCall is None or startCall.str != '(':
|
|
1943
|
+
continue
|
|
1944
|
+
endCall = startCall.link
|
|
1945
|
+
if endCall is None or endCall.str != ')':
|
|
1946
|
+
continue
|
|
1947
|
+
checkZeroArguments(func, startCall, endCall)
|
|
1948
|
+
if tokenImpl:
|
|
1949
|
+
checkDeclarationArgumentsViolations(func, startCall, endCall)
|
|
1950
|
+
else:
|
|
1951
|
+
# When there is no function definition, we should execute
|
|
1952
|
+
# its checks for the declaration token. The point is that without
|
|
1953
|
+
# a known definition we have no Function.argument list required
|
|
1954
|
+
# for declaration check.
|
|
1955
|
+
checkDefinitionArgumentsViolations(func, startCall, endCall)
|
|
1956
|
+
|
|
1957
|
+
# Check arguments in pointer declarations
|
|
1958
|
+
for var in data.variables:
|
|
1959
|
+
if not var.isPointer:
|
|
1960
|
+
continue
|
|
1961
|
+
|
|
1962
|
+
if var.nameToken is None:
|
|
1963
|
+
continue
|
|
1964
|
+
|
|
1965
|
+
rawTokensFollowingPtr = getFollowingRawTokens(rawTokens, var.nameToken, 3)
|
|
1966
|
+
if len(rawTokensFollowingPtr) != 3:
|
|
1967
|
+
continue
|
|
1968
|
+
|
|
1969
|
+
# Compliant: returnType (*ptrName) ( ArgType )
|
|
1970
|
+
# Non-compliant: returnType (*ptrName) ( )
|
|
1971
|
+
if (rawTokensFollowingPtr[0].str == ')' and
|
|
1972
|
+
rawTokensFollowingPtr[1].str == '(' and
|
|
1973
|
+
rawTokensFollowingPtr[2].str == ')'):
|
|
1974
|
+
self.reportError(var.nameToken, 8, 2)
|
|
1975
|
+
|
|
1976
|
+
|
|
1977
|
+
def misra_8_4(self, cfg):
|
|
1978
|
+
for func in cfg.functions:
|
|
1979
|
+
if func.isStatic:
|
|
1980
|
+
continue
|
|
1981
|
+
if func.token is None:
|
|
1982
|
+
continue
|
|
1983
|
+
if not is_source_file(func.token.file):
|
|
1984
|
+
continue
|
|
1985
|
+
if func.token != func.tokenDef:
|
|
1986
|
+
continue
|
|
1987
|
+
if func.tokenDef.str == 'main':
|
|
1988
|
+
continue
|
|
1989
|
+
self.reportError(func.tokenDef, 8, 4)
|
|
1990
|
+
|
|
1991
|
+
extern_vars = []
|
|
1992
|
+
var_defs = []
|
|
1993
|
+
|
|
1994
|
+
for var in cfg.variables:
|
|
1995
|
+
if not var.isGlobal:
|
|
1996
|
+
continue
|
|
1997
|
+
if var.isStatic:
|
|
1998
|
+
continue
|
|
1999
|
+
if var.nameToken is None:
|
|
2000
|
+
continue
|
|
2001
|
+
if var.isExtern:
|
|
2002
|
+
extern_vars.append(var.nameToken.str)
|
|
2003
|
+
else:
|
|
2004
|
+
var_defs.append(var.nameToken)
|
|
2005
|
+
for vartok in var_defs:
|
|
2006
|
+
if vartok.str not in extern_vars:
|
|
2007
|
+
self.reportError(vartok, 8, 4)
|
|
2008
|
+
|
|
2009
|
+
def misra_8_5(self, dumpfile, cfg):
|
|
2010
|
+
self._save_ctu_summary_identifiers(dumpfile, cfg)
|
|
2011
|
+
|
|
2012
|
+
def misra_8_6(self, dumpfile, cfg):
|
|
2013
|
+
self._save_ctu_summary_identifiers(dumpfile, cfg)
|
|
2014
|
+
|
|
2015
|
+
def misra_8_7(self, dumpfile, cfg):
|
|
2016
|
+
self._save_ctu_summary_usage(dumpfile, cfg)
|
|
2017
|
+
|
|
2018
|
+
def misra_8_8(self, cfg):
|
|
2019
|
+
vars = {}
|
|
2020
|
+
for var in cfg.variables:
|
|
2021
|
+
if var.access != 'Global':
|
|
2022
|
+
continue
|
|
2023
|
+
if var.nameToken is None:
|
|
2024
|
+
continue
|
|
2025
|
+
varname = var.nameToken.str
|
|
2026
|
+
if varname in vars:
|
|
2027
|
+
vars[varname].append(var)
|
|
2028
|
+
else:
|
|
2029
|
+
vars[varname] = [var]
|
|
2030
|
+
for varname, varlist in vars.items():
|
|
2031
|
+
static_var = None
|
|
2032
|
+
extern_var = None
|
|
2033
|
+
for var in varlist:
|
|
2034
|
+
if var.isStatic:
|
|
2035
|
+
static_var = var
|
|
2036
|
+
elif var.isExtern:
|
|
2037
|
+
extern_var = var
|
|
2038
|
+
if static_var and extern_var:
|
|
2039
|
+
self.reportError(extern_var.nameToken, 8, 8)
|
|
2040
|
+
|
|
2041
|
+
def misra_8_9(self, cfg):
|
|
2042
|
+
variables = {}
|
|
2043
|
+
for scope in cfg.scopes:
|
|
2044
|
+
if scope.type != 'Function':
|
|
2045
|
+
continue
|
|
2046
|
+
variables_used_in_scope = []
|
|
2047
|
+
tok = scope.bodyStart
|
|
2048
|
+
while tok != scope.bodyEnd:
|
|
2049
|
+
if tok.variable and tok.variable.access == 'Global' and tok.variable.isStatic:
|
|
2050
|
+
if tok.variable not in variables_used_in_scope:
|
|
2051
|
+
variables_used_in_scope.append(tok.variable)
|
|
2052
|
+
tok = tok.next
|
|
2053
|
+
for var in variables_used_in_scope:
|
|
2054
|
+
if var in variables:
|
|
2055
|
+
variables[var] += 1
|
|
2056
|
+
else:
|
|
2057
|
+
variables[var] = 1
|
|
2058
|
+
for var, count in variables.items():
|
|
2059
|
+
if count == 1:
|
|
2060
|
+
self.reportError(var.nameToken, 8, 9)
|
|
2061
|
+
|
|
2062
|
+
|
|
2063
|
+
def misra_8_10(self, cfg):
|
|
2064
|
+
for func in cfg.functions:
|
|
2065
|
+
if func.isInlineKeyword and not func.isStatic:
|
|
2066
|
+
self.reportError(func.tokenDef, 8, 10)
|
|
2067
|
+
|
|
2068
|
+
def misra_8_11(self, data):
|
|
2069
|
+
for var in data.variables:
|
|
2070
|
+
if var.isExtern and simpleMatch(var.nameToken.next, '[ ]') and var.nameToken.scope.type == 'Global':
|
|
2071
|
+
self.reportError(var.nameToken, 8, 11)
|
|
2072
|
+
|
|
2073
|
+
def misra_8_12(self, data):
|
|
2074
|
+
for scope in data.scopes:
|
|
2075
|
+
if scope.type != 'Enum':
|
|
2076
|
+
continue
|
|
2077
|
+
enum_values = []
|
|
2078
|
+
implicit_enum_values = []
|
|
2079
|
+
e_token = scope.bodyStart.next
|
|
2080
|
+
while e_token != scope.bodyEnd:
|
|
2081
|
+
if e_token.str == '(':
|
|
2082
|
+
e_token = e_token.link
|
|
2083
|
+
continue
|
|
2084
|
+
if e_token.previous.str not in ',{':
|
|
2085
|
+
e_token = e_token.next
|
|
2086
|
+
continue
|
|
2087
|
+
if e_token.isName and e_token.values and e_token.valueType and e_token.valueType.typeScope == scope:
|
|
2088
|
+
token_values = [v.intvalue for v in e_token.values]
|
|
2089
|
+
enum_values += token_values
|
|
2090
|
+
if e_token.next.str != "=":
|
|
2091
|
+
implicit_enum_values += token_values
|
|
2092
|
+
e_token = e_token.next
|
|
2093
|
+
for implicit_enum_value in implicit_enum_values:
|
|
2094
|
+
if enum_values.count(implicit_enum_value) != 1:
|
|
2095
|
+
self.reportError(scope.bodyStart, 8, 12)
|
|
2096
|
+
|
|
2097
|
+
def misra_8_14(self, rawTokens):
|
|
2098
|
+
for token in rawTokens:
|
|
2099
|
+
if token.str == 'restrict':
|
|
2100
|
+
self.reportError(token, 8, 14)
|
|
2101
|
+
|
|
2102
|
+
def misra_9_2(self, data):
|
|
2103
|
+
misra_9.misra_9_x(self, data, 902)
|
|
2104
|
+
|
|
2105
|
+
def misra_9_3(self, data):
|
|
2106
|
+
misra_9.misra_9_x(self, data, 903)
|
|
2107
|
+
|
|
2108
|
+
def misra_9_4(self, data):
|
|
2109
|
+
misra_9.misra_9_x(self, data, 904)
|
|
2110
|
+
|
|
2111
|
+
def misra_9_5(self, data, rawTokens):
|
|
2112
|
+
misra_9.misra_9_x(self, data, 905, rawTokens)
|
|
2113
|
+
#for token in rawTokens:
|
|
2114
|
+
# if simpleMatch(token, '[ ] = { ['):
|
|
2115
|
+
# self.reportError(token, 9, 5)
|
|
2116
|
+
|
|
2117
|
+
def misra_10_1(self, data):
|
|
2118
|
+
for token in data.tokenlist:
|
|
2119
|
+
if not token.isOp:
|
|
2120
|
+
continue
|
|
2121
|
+
|
|
2122
|
+
for t1, t2 in itertools.product(
|
|
2123
|
+
list(getTernaryOperandsRecursive(token.astOperand1) or [token.astOperand1]),
|
|
2124
|
+
list(getTernaryOperandsRecursive(token.astOperand2) or [token.astOperand2]),
|
|
2125
|
+
):
|
|
2126
|
+
e1 = getEssentialTypeCategory(t1)
|
|
2127
|
+
e2 = getEssentialTypeCategory(t2)
|
|
2128
|
+
if not e1 or not e2:
|
|
2129
|
+
continue
|
|
2130
|
+
if token.str in ('<<', '>>'):
|
|
2131
|
+
if not isUnsignedType(e1):
|
|
2132
|
+
self.reportError(token, 10, 1)
|
|
2133
|
+
elif not isUnsignedType(e2) and not token.astOperand2.isNumber:
|
|
2134
|
+
self.reportError(token, 10, 1)
|
|
2135
|
+
elif token.str in ('~', '&', '|', '^'):
|
|
2136
|
+
e1_et = getEssentialType(token.astOperand1)
|
|
2137
|
+
e2_et = getEssentialType(token.astOperand2)
|
|
2138
|
+
if e1_et == 'char' or e2_et == 'char':
|
|
2139
|
+
self.reportError(token, 10, 1)
|
|
2140
|
+
|
|
2141
|
+
def misra_10_2(self, data):
|
|
2142
|
+
def isEssentiallySignedOrUnsigned(op):
|
|
2143
|
+
e = getEssentialType(op)
|
|
2144
|
+
return e and (e.split(' ')[0] in ('unsigned', 'signed'))
|
|
2145
|
+
|
|
2146
|
+
def isEssentiallyChar(op):
|
|
2147
|
+
if op is None:
|
|
2148
|
+
return False
|
|
2149
|
+
if op.str == '+':
|
|
2150
|
+
return isEssentiallyChar(op.astOperand1) or isEssentiallyChar(op.astOperand2)
|
|
2151
|
+
return op.isChar
|
|
2152
|
+
|
|
2153
|
+
for token in data.tokenlist:
|
|
2154
|
+
if token.str not in ('+', '-'):
|
|
2155
|
+
continue
|
|
2156
|
+
|
|
2157
|
+
if (not isEssentiallyChar(token.astOperand1)) and (not isEssentiallyChar(token.astOperand2)):
|
|
2158
|
+
continue
|
|
2159
|
+
|
|
2160
|
+
if token.str == '+':
|
|
2161
|
+
if isEssentiallyChar(token.astOperand1) and not isEssentiallySignedOrUnsigned(token.astOperand2):
|
|
2162
|
+
self.reportError(token, 10, 2)
|
|
2163
|
+
if isEssentiallyChar(token.astOperand2) and not isEssentiallySignedOrUnsigned(token.astOperand1):
|
|
2164
|
+
self.reportError(token, 10, 2)
|
|
2165
|
+
|
|
2166
|
+
if token.str == '-':
|
|
2167
|
+
e1 = getEssentialType(token.astOperand1)
|
|
2168
|
+
if e1 and e1.split(' ')[-1] != 'char':
|
|
2169
|
+
self.reportError(token, 10, 2)
|
|
2170
|
+
if not isEssentiallyChar(token.astOperand2) and not isEssentiallySignedOrUnsigned(token.astOperand2):
|
|
2171
|
+
self.reportError(token, 10, 2)
|
|
2172
|
+
|
|
2173
|
+
def misra_10_3(self, cfg):
|
|
2174
|
+
def get_category(essential_type):
|
|
2175
|
+
if essential_type:
|
|
2176
|
+
if essential_type in ('bool', 'char'):
|
|
2177
|
+
return essential_type
|
|
2178
|
+
if essential_type.split(' ')[-1] in ('float', 'double'):
|
|
2179
|
+
return 'floating'
|
|
2180
|
+
if essential_type.split(' ')[0] in ('unsigned', 'signed'):
|
|
2181
|
+
return essential_type.split(' ')[0]
|
|
2182
|
+
return None
|
|
2183
|
+
for tok in cfg.tokenlist:
|
|
2184
|
+
if tok.isAssignmentOp:
|
|
2185
|
+
lhs = getEssentialType(tok.astOperand1)
|
|
2186
|
+
rhs = getEssentialType(tok.astOperand2)
|
|
2187
|
+
#print(lhs)
|
|
2188
|
+
#print(rhs)
|
|
2189
|
+
if lhs is None or rhs is None:
|
|
2190
|
+
continue
|
|
2191
|
+
lhs_category = get_category(lhs)
|
|
2192
|
+
rhs_category = get_category(rhs)
|
|
2193
|
+
if lhs_category and rhs_category and lhs_category != rhs_category and rhs_category not in ('signed','unsigned'):
|
|
2194
|
+
self.reportError(tok, 10, 3)
|
|
2195
|
+
if bitsOfEssentialType(lhs) < bitsOfEssentialType(rhs):
|
|
2196
|
+
self.reportError(tok, 10, 3)
|
|
2197
|
+
|
|
2198
|
+
|
|
2199
|
+
def misra_10_4(self, data):
|
|
2200
|
+
op = {'+', '-', '*', '/', '%', '&', '|', '^', '+=', '-=', ':'}
|
|
2201
|
+
for token in data.tokenlist:
|
|
2202
|
+
if token.str not in op and not token.isComparisonOp:
|
|
2203
|
+
continue
|
|
2204
|
+
if not token.astOperand1 or not token.astOperand2:
|
|
2205
|
+
continue
|
|
2206
|
+
if not token.astOperand1.valueType or not token.astOperand2.valueType:
|
|
2207
|
+
continue
|
|
2208
|
+
if ((token.astOperand1.str in op or token.astOperand1.isComparisonOp) and
|
|
2209
|
+
(token.astOperand2.str in op or token.astOperand1.isComparisonOp)):
|
|
2210
|
+
e1, e2 = getEssentialCategorylist(token.astOperand1.astOperand2, token.astOperand2.astOperand1)
|
|
2211
|
+
elif token.astOperand1.str in op or token.astOperand1.isComparisonOp:
|
|
2212
|
+
e1, e2 = getEssentialCategorylist(token.astOperand1.astOperand2, token.astOperand2)
|
|
2213
|
+
elif token.astOperand2.str in op or token.astOperand2.isComparisonOp:
|
|
2214
|
+
e1, e2 = getEssentialCategorylist(token.astOperand1, token.astOperand2.astOperand1)
|
|
2215
|
+
else:
|
|
2216
|
+
e1, e2 = getEssentialCategorylist(token.astOperand1, token.astOperand2)
|
|
2217
|
+
if token.str == "+=" or token.str == "+":
|
|
2218
|
+
if e1 == "char" and (e2 == "signed" or e2 == "unsigned"):
|
|
2219
|
+
continue
|
|
2220
|
+
if e2 == "char" and (e1 == "signed" or e1 == "unsigned"):
|
|
2221
|
+
continue
|
|
2222
|
+
if token.str == "-=" or token.str == "-":
|
|
2223
|
+
if e1 == "char" and (e2 == "signed" or e2 == "unsigned"):
|
|
2224
|
+
continue
|
|
2225
|
+
if e1 and e2 and (e1.find('Anonymous') != -1 and (e2 == "signed" or e2 == "unsigned")):
|
|
2226
|
+
continue
|
|
2227
|
+
if e1 and e2 and (e2.find('Anonymous') != -1 and (e1 == "signed" or e1 == "unsigned")):
|
|
2228
|
+
continue
|
|
2229
|
+
if e1 and e2 and e1 != e2:
|
|
2230
|
+
self.reportError(token, 10, 4)
|
|
2231
|
+
|
|
2232
|
+
def misra_10_5(self, cfg):
|
|
2233
|
+
def _get_essential_category(token):
|
|
2234
|
+
essential_type = getEssentialType(token)
|
|
2235
|
+
#print(essential_type)
|
|
2236
|
+
if essential_type:
|
|
2237
|
+
if essential_type in ('bool', 'char'):
|
|
2238
|
+
return essential_type
|
|
2239
|
+
if essential_type.split(' ')[-1] in ('float', 'double'):
|
|
2240
|
+
return 'floating'
|
|
2241
|
+
if essential_type.split(' ')[0] in ('unsigned', 'signed'):
|
|
2242
|
+
return essential_type.split(' ')[0]
|
|
2243
|
+
return None
|
|
2244
|
+
for token in cfg.tokenlist:
|
|
2245
|
+
if not isCast(token):
|
|
2246
|
+
continue
|
|
2247
|
+
to_type = _get_essential_category(token)
|
|
2248
|
+
#print(to_type)
|
|
2249
|
+
if to_type is None:
|
|
2250
|
+
continue
|
|
2251
|
+
from_type = _get_essential_category(token.astOperand1)
|
|
2252
|
+
#print(from_type)
|
|
2253
|
+
if from_type is None:
|
|
2254
|
+
continue
|
|
2255
|
+
if to_type == from_type:
|
|
2256
|
+
continue
|
|
2257
|
+
if to_type == 'bool' or from_type == 'bool':
|
|
2258
|
+
if token.astOperand1.isInt and token.astOperand1.getKnownIntValue() == 1:
|
|
2259
|
+
# Exception
|
|
2260
|
+
continue
|
|
2261
|
+
self.reportError(token, 10, 5)
|
|
2262
|
+
continue
|
|
2263
|
+
if to_type == 'enum':
|
|
2264
|
+
self.reportError(token, 10, 5)
|
|
2265
|
+
continue
|
|
2266
|
+
if from_type == 'float' and to_type == 'char':
|
|
2267
|
+
self.reportError(token, 10, 5)
|
|
2268
|
+
continue
|
|
2269
|
+
if from_type == 'char' and to_type == 'float':
|
|
2270
|
+
self.reportError(token, 10, 5)
|
|
2271
|
+
continue
|
|
2272
|
+
|
|
2273
|
+
def misra_10_6(self, data):
|
|
2274
|
+
for token in data.tokenlist:
|
|
2275
|
+
if token.str != '=' or not token.astOperand1 or not token.astOperand2:
|
|
2276
|
+
continue
|
|
2277
|
+
if not is_composite_expr(token.astOperand2):
|
|
2278
|
+
continue
|
|
2279
|
+
vt1 = token.astOperand1.valueType
|
|
2280
|
+
vt2 = token.astOperand2.valueType
|
|
2281
|
+
if not vt1 or vt1.pointer > 0:
|
|
2282
|
+
continue
|
|
2283
|
+
if not vt2 or vt2.pointer > 0:
|
|
2284
|
+
continue
|
|
2285
|
+
try:
|
|
2286
|
+
if isCast(token.astOperand2):
|
|
2287
|
+
e = vt2.type
|
|
2288
|
+
else:
|
|
2289
|
+
e = getEssentialType(token.astOperand2)
|
|
2290
|
+
if not e:
|
|
2291
|
+
continue
|
|
2292
|
+
lhsbits = vt1.bits if vt1.bits else bitsOfEssentialType(vt1.type)
|
|
2293
|
+
if lhsbits > bitsOfEssentialType(e):
|
|
2294
|
+
self.reportError(token, 10, 6)
|
|
2295
|
+
except ValueError:
|
|
2296
|
+
pass
|
|
2297
|
+
|
|
2298
|
+
def misra_10_7(self, cfg):
|
|
2299
|
+
for token in cfg.tokenlist:
|
|
2300
|
+
if token.astOperand1 is None or token.astOperand2 is None:
|
|
2301
|
+
continue
|
|
2302
|
+
if not token.isArithmeticalOp:
|
|
2303
|
+
continue
|
|
2304
|
+
parent = token.astParent
|
|
2305
|
+
if parent is None:
|
|
2306
|
+
continue
|
|
2307
|
+
if not parent.isArithmeticalOp:
|
|
2308
|
+
if not parent.isAssignmentOp:
|
|
2309
|
+
continue
|
|
2310
|
+
if parent.str == '=':
|
|
2311
|
+
continue
|
|
2312
|
+
token_type = getEssentialType(token)
|
|
2313
|
+
if token_type is None:
|
|
2314
|
+
continue
|
|
2315
|
+
sibling = parent.astOperand1 if (token == parent.astOperand2) else parent.astOperand2
|
|
2316
|
+
sibling_type = getEssentialType(sibling)
|
|
2317
|
+
if sibling_type is None:
|
|
2318
|
+
continue
|
|
2319
|
+
b1 = bitsOfEssentialType(token_type)
|
|
2320
|
+
b2 = bitsOfEssentialType(sibling_type)
|
|
2321
|
+
if b1 > 0 and b1 < b2:
|
|
2322
|
+
self.reportError(token, 10, 7)
|
|
2323
|
+
|
|
2324
|
+
def misra_10_8(self, data):
|
|
2325
|
+
for token in data.tokenlist:
|
|
2326
|
+
if not isCast(token):
|
|
2327
|
+
continue
|
|
2328
|
+
if not token.valueType or token.valueType.pointer > 0:
|
|
2329
|
+
continue
|
|
2330
|
+
if not token.astOperand1.valueType or token.astOperand1.valueType.pointer > 0:
|
|
2331
|
+
continue
|
|
2332
|
+
if not token.astOperand1.astOperand1:
|
|
2333
|
+
continue
|
|
2334
|
+
if token.astOperand1.str not in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":", '~'):
|
|
2335
|
+
continue
|
|
2336
|
+
if token.astOperand1.str != '~' and not token.astOperand1.astOperand2:
|
|
2337
|
+
continue
|
|
2338
|
+
if token.astOperand1.str == '~':
|
|
2339
|
+
e2 = getEssentialTypeCategory(token.astOperand1.astOperand1)
|
|
2340
|
+
else:
|
|
2341
|
+
e2, e3 = getEssentialCategorylist(token.astOperand1.astOperand1, token.astOperand1.astOperand2)
|
|
2342
|
+
if e2 != e3:
|
|
2343
|
+
continue
|
|
2344
|
+
e1 = getEssentialTypeCategory(token)
|
|
2345
|
+
if e1 != e2:
|
|
2346
|
+
self.reportError(token, 10, 8)
|
|
2347
|
+
else:
|
|
2348
|
+
try:
|
|
2349
|
+
e = getEssentialType(token.astOperand1)
|
|
2350
|
+
if not e:
|
|
2351
|
+
continue
|
|
2352
|
+
if bitsOfEssentialType(token.valueType.type) > bitsOfEssentialType(e):
|
|
2353
|
+
self.reportError(token, 10, 8)
|
|
2354
|
+
except ValueError:
|
|
2355
|
+
pass
|
|
2356
|
+
|
|
2357
|
+
def misra_11_1(self, data):
|
|
2358
|
+
for token in data.tokenlist:
|
|
2359
|
+
to_from = get_type_conversion_to_from(token)
|
|
2360
|
+
if to_from is None:
|
|
2361
|
+
continue
|
|
2362
|
+
from_type = get_function_pointer_type(to_from[1])
|
|
2363
|
+
if from_type is None:
|
|
2364
|
+
continue
|
|
2365
|
+
to_type = get_function_pointer_type(to_from[0])
|
|
2366
|
+
if to_type is None or to_type != from_type:
|
|
2367
|
+
self.reportError(token, 11, 1)
|
|
2368
|
+
|
|
2369
|
+
def misra_11_2(self, data):
|
|
2370
|
+
def get_pointer_type(type_token):
|
|
2371
|
+
while type_token and (type_token.str in ('const', 'struct')):
|
|
2372
|
+
type_token = type_token.next
|
|
2373
|
+
if type_token is None:
|
|
2374
|
+
return None
|
|
2375
|
+
if not type_token.isName:
|
|
2376
|
+
return None
|
|
2377
|
+
return type_token if (type_token.next and type_token.next.str == '*') else None
|
|
2378
|
+
|
|
2379
|
+
incomplete_types = []
|
|
2380
|
+
|
|
2381
|
+
for token in data.tokenlist:
|
|
2382
|
+
if token.str == 'struct' and token.next and token.next.next and token.next.isName and token.next.next.str == ';':
|
|
2383
|
+
incomplete_types.append(token.next.str)
|
|
2384
|
+
to_from = get_type_conversion_to_from(token)
|
|
2385
|
+
if to_from is None:
|
|
2386
|
+
continue
|
|
2387
|
+
to_pointer_type_token = get_pointer_type(to_from[0])
|
|
2388
|
+
if to_pointer_type_token is None:
|
|
2389
|
+
continue
|
|
2390
|
+
from_pointer_type_token = get_pointer_type(to_from[1])
|
|
2391
|
+
if from_pointer_type_token is None:
|
|
2392
|
+
continue
|
|
2393
|
+
if to_pointer_type_token.str == from_pointer_type_token.str:
|
|
2394
|
+
continue
|
|
2395
|
+
if from_pointer_type_token.typeScope is None and (from_pointer_type_token.str in incomplete_types):
|
|
2396
|
+
self.reportError(token, 11, 2)
|
|
2397
|
+
elif to_pointer_type_token.typeScope is None and (to_pointer_type_token.str in incomplete_types):
|
|
2398
|
+
self.reportError(token, 11, 2)
|
|
2399
|
+
|
|
2400
|
+
def misra_11_3(self, data):
|
|
2401
|
+
for token in data.tokenlist:
|
|
2402
|
+
if not isCast(token):
|
|
2403
|
+
continue
|
|
2404
|
+
vt1 = token.valueType
|
|
2405
|
+
vt2 = token.astOperand1.valueType
|
|
2406
|
+
if not vt1 or not vt2:
|
|
2407
|
+
continue
|
|
2408
|
+
if vt1.type == 'void' or vt2.type == 'void':
|
|
2409
|
+
continue
|
|
2410
|
+
if (vt1.pointer > 0 and vt1.type == 'record' and
|
|
2411
|
+
vt2.pointer > 0 and vt2.type == 'record' and
|
|
2412
|
+
vt1.typeScopeId != vt2.typeScopeId):
|
|
2413
|
+
self.reportError(token, 11, 3)
|
|
2414
|
+
elif (vt1.pointer == vt2.pointer and vt1.pointer > 0 and
|
|
2415
|
+
vt1.type != vt2.type and vt1.type != 'char'):
|
|
2416
|
+
self.reportError(token, 11, 3)
|
|
2417
|
+
|
|
2418
|
+
def misra_11_4(self, data):
|
|
2419
|
+
for token in data.tokenlist:
|
|
2420
|
+
if not isCast(token):
|
|
2421
|
+
continue
|
|
2422
|
+
vt1 = token.valueType
|
|
2423
|
+
vt2 = token.astOperand1.valueType
|
|
2424
|
+
if not vt1 or not vt2:
|
|
2425
|
+
continue
|
|
2426
|
+
if vt2.pointer > 0 and vt1.pointer == 0 and (vt1.isIntegral() or vt1.isEnum()) and vt2.type != 'void':
|
|
2427
|
+
self.reportError(token, 11, 4)
|
|
2428
|
+
elif vt1.pointer > 0 and vt2.pointer == 0 and (vt2.isIntegral() or vt2.isEnum()) and vt1.type != 'void':
|
|
2429
|
+
self.reportError(token, 11, 4)
|
|
2430
|
+
|
|
2431
|
+
def misra_11_5(self, data):
|
|
2432
|
+
for token in data.tokenlist:
|
|
2433
|
+
if not isCast(token):
|
|
2434
|
+
if token.astOperand1 and token.astOperand2 and token.str == "=" and token.next.str != "(":
|
|
2435
|
+
vt1 = token.astOperand1.valueType
|
|
2436
|
+
vt2 = token.astOperand2.valueType
|
|
2437
|
+
if not vt1 or not vt2:
|
|
2438
|
+
continue
|
|
2439
|
+
if vt1.pointer > 0 and vt1.type != 'void' and vt2.pointer == vt1.pointer and vt2.type == 'void':
|
|
2440
|
+
self.reportError(token, 11, 5)
|
|
2441
|
+
continue
|
|
2442
|
+
if token.astOperand1.astOperand1 and token.astOperand1.astOperand1.str in (
|
|
2443
|
+
'malloc', 'calloc', 'realloc', 'free'):
|
|
2444
|
+
continue
|
|
2445
|
+
vt1 = token.valueType
|
|
2446
|
+
vt2 = token.astOperand1.valueType
|
|
2447
|
+
if not vt1 or not vt2:
|
|
2448
|
+
continue
|
|
2449
|
+
if vt1.pointer > 0 and vt1.type != 'void' and vt2.pointer == vt1.pointer and vt2.type == 'void':
|
|
2450
|
+
self.reportError(token, 11, 5)
|
|
2451
|
+
|
|
2452
|
+
def misra_11_6(self, data):
|
|
2453
|
+
for token in data.tokenlist:
|
|
2454
|
+
if not isCast(token):
|
|
2455
|
+
continue
|
|
2456
|
+
if token.astOperand1.astOperand1:
|
|
2457
|
+
continue
|
|
2458
|
+
vt1 = token.valueType
|
|
2459
|
+
vt2 = token.astOperand1.valueType
|
|
2460
|
+
if not vt1 or not vt2:
|
|
2461
|
+
continue
|
|
2462
|
+
if vt1.pointer == 1 and vt1.type == 'void' and vt2.pointer == 0 and token.astOperand1.str != "0":
|
|
2463
|
+
self.reportError(token, 11, 6)
|
|
2464
|
+
elif vt1.pointer == 0 and vt1.type != 'void' and vt2.pointer == 1 and vt2.type == 'void':
|
|
2465
|
+
self.reportError(token, 11, 6)
|
|
2466
|
+
|
|
2467
|
+
def misra_11_7(self, data):
|
|
2468
|
+
for token in data.tokenlist:
|
|
2469
|
+
if not isCast(token):
|
|
2470
|
+
continue
|
|
2471
|
+
vt1 = token.valueType
|
|
2472
|
+
vt2 = token.astOperand1.valueType
|
|
2473
|
+
if not vt1 or not vt2:
|
|
2474
|
+
continue
|
|
2475
|
+
if token.astOperand1.astOperand1:
|
|
2476
|
+
continue
|
|
2477
|
+
if (vt2.pointer > 0 and vt1.pointer == 0 and
|
|
2478
|
+
not vt1.isIntegral() and not vt1.isEnum() and
|
|
2479
|
+
vt1.type != 'void'):
|
|
2480
|
+
self.reportError(token, 11, 7)
|
|
2481
|
+
elif (vt1.pointer > 0 and vt2.pointer == 0 and
|
|
2482
|
+
not vt2.isIntegral() and not vt2.isEnum() and
|
|
2483
|
+
vt1.type != 'void'):
|
|
2484
|
+
self.reportError(token, 11, 7)
|
|
2485
|
+
|
|
2486
|
+
def misra_11_8(self, data):
|
|
2487
|
+
# TODO: reuse code in CERT-EXP05
|
|
2488
|
+
for token in data.tokenlist:
|
|
2489
|
+
if isCast(token):
|
|
2490
|
+
# C-style cast
|
|
2491
|
+
if not token.valueType:
|
|
2492
|
+
continue
|
|
2493
|
+
if not token.astOperand1.valueType:
|
|
2494
|
+
continue
|
|
2495
|
+
if token.valueType.pointer == 0:
|
|
2496
|
+
continue
|
|
2497
|
+
if token.astOperand1.valueType.pointer == 0:
|
|
2498
|
+
continue
|
|
2499
|
+
const1 = token.valueType.constness
|
|
2500
|
+
const2 = token.astOperand1.valueType.constness
|
|
2501
|
+
if (const1 % 2) < (const2 % 2):
|
|
2502
|
+
self.reportError(token, 11, 8)
|
|
2503
|
+
|
|
2504
|
+
elif token.str == '(' and token.astOperand1 and token.astOperand2 and token.astOperand1.function:
|
|
2505
|
+
# Function call
|
|
2506
|
+
function = token.astOperand1.function
|
|
2507
|
+
arguments = getArguments(token)
|
|
2508
|
+
for argnr, argvar in function.argument.items():
|
|
2509
|
+
if argnr < 1 or argnr > len(arguments):
|
|
2510
|
+
continue
|
|
2511
|
+
if not argvar.isPointer:
|
|
2512
|
+
continue
|
|
2513
|
+
argtok = arguments[argnr - 1]
|
|
2514
|
+
if not argtok.valueType:
|
|
2515
|
+
continue
|
|
2516
|
+
if argtok.valueType.pointer == 0:
|
|
2517
|
+
continue
|
|
2518
|
+
const1 = argvar.constness
|
|
2519
|
+
const2 = arguments[argnr - 1].valueType.constness
|
|
2520
|
+
if (const1 % 2) < (const2 % 2):
|
|
2521
|
+
self.reportError(token, 11, 8)
|
|
2522
|
+
|
|
2523
|
+
def misra_11_9(self, data):
|
|
2524
|
+
for token in data.tokenlist:
|
|
2525
|
+
if token.astOperand1 and token.astOperand2 and token.str in ["=", "==", "!=", "?", ":"]:
|
|
2526
|
+
vt1 = token.astOperand1.valueType
|
|
2527
|
+
vt2 = token.astOperand2.valueType
|
|
2528
|
+
if not vt1 or not vt2:
|
|
2529
|
+
continue
|
|
2530
|
+
if vt1.pointer > 0 and vt2.pointer == 0 and token.astOperand2.str == "NULL":
|
|
2531
|
+
continue
|
|
2532
|
+
if (token.astOperand2.values and vt1.pointer > 0 and
|
|
2533
|
+
vt2.pointer == 0 and token.astOperand2.values):
|
|
2534
|
+
if token.astOperand2.getValue(0):
|
|
2535
|
+
self.reportError(token, 11, 9)
|
|
2536
|
+
|
|
2537
|
+
def misra_12_1_sizeof(self, rawTokens):
|
|
2538
|
+
state = 0
|
|
2539
|
+
compiled = re.compile(r'^[a-zA-Z_]')
|
|
2540
|
+
for tok in rawTokens:
|
|
2541
|
+
if tok.str.startswith('//') or tok.str.startswith('/*'):
|
|
2542
|
+
continue
|
|
2543
|
+
if tok.str == 'sizeof':
|
|
2544
|
+
state = 1
|
|
2545
|
+
elif state == 1:
|
|
2546
|
+
if compiled.match(tok.str):
|
|
2547
|
+
state = 2
|
|
2548
|
+
else:
|
|
2549
|
+
state = 0
|
|
2550
|
+
elif state == 2:
|
|
2551
|
+
if tok.str in ('+', '-', '*', '/', '%'):
|
|
2552
|
+
self.reportError(tok, 12, 1)
|
|
2553
|
+
else:
|
|
2554
|
+
state = 0
|
|
2555
|
+
|
|
2556
|
+
def misra_12_1(self, data):
|
|
2557
|
+
for token in data.tokenlist:
|
|
2558
|
+
p = getPrecedence(token)
|
|
2559
|
+
if p < 2 or p > 12:
|
|
2560
|
+
continue
|
|
2561
|
+
p1 = getPrecedence(token.astOperand1)
|
|
2562
|
+
if p < p1 <= 12 and numberOfParentheses(token.astOperand1, token):
|
|
2563
|
+
self.reportError(token, 12, 1)
|
|
2564
|
+
continue
|
|
2565
|
+
p2 = getPrecedence(token.astOperand2)
|
|
2566
|
+
if p < p2 <= 12 and numberOfParentheses(token, token.astOperand2):
|
|
2567
|
+
self.reportError(token, 12, 1)
|
|
2568
|
+
continue
|
|
2569
|
+
|
|
2570
|
+
def misra_12_2(self, data):
|
|
2571
|
+
for token in data.tokenlist:
|
|
2572
|
+
if not (token.str in ('<<', '>>')):
|
|
2573
|
+
continue
|
|
2574
|
+
if (not token.astOperand2) or (not token.astOperand2.values):
|
|
2575
|
+
continue
|
|
2576
|
+
maxval = 0
|
|
2577
|
+
for val in token.astOperand2.values:
|
|
2578
|
+
if val.intvalue and val.intvalue > maxval:
|
|
2579
|
+
maxval = val.intvalue
|
|
2580
|
+
if maxval == 0:
|
|
2581
|
+
continue
|
|
2582
|
+
sz = bitsOfEssentialType(getEssentialType(token.astOperand1))
|
|
2583
|
+
if sz <= 0:
|
|
2584
|
+
continue
|
|
2585
|
+
if maxval >= sz:
|
|
2586
|
+
self.reportError(token, 12, 2)
|
|
2587
|
+
|
|
2588
|
+
def misra_12_3(self, data):
|
|
2589
|
+
for token in data.tokenlist:
|
|
2590
|
+
if token.str == ';' and (token.isSplittedVarDeclComma is True):
|
|
2591
|
+
self.reportError(token, 12, 3)
|
|
2592
|
+
if token.str == ',' and token.astParent and token.astParent.str == ';':
|
|
2593
|
+
self.reportError(token, 12, 3)
|
|
2594
|
+
if token.str == ',' and token.astParent is None:
|
|
2595
|
+
if token.scope.type in ('Class', 'Struct'):
|
|
2596
|
+
# Is this initlist..
|
|
2597
|
+
tok = token
|
|
2598
|
+
while tok and tok.str == ',':
|
|
2599
|
+
tok = tok.next
|
|
2600
|
+
if tok and tok.next and tok.isName and tok.next.str == '(':
|
|
2601
|
+
tok = tok.next.link.next
|
|
2602
|
+
if tok.str == '{':
|
|
2603
|
+
# This comma is used in initlist, do not warn
|
|
2604
|
+
continue
|
|
2605
|
+
prev = token.previous
|
|
2606
|
+
while prev:
|
|
2607
|
+
if prev.str == ';':
|
|
2608
|
+
self.reportError(token, 12, 3)
|
|
2609
|
+
break
|
|
2610
|
+
elif prev.str in ')}]':
|
|
2611
|
+
prev = prev.link
|
|
2612
|
+
elif prev.str in '({[':
|
|
2613
|
+
break
|
|
2614
|
+
prev = prev.previous
|
|
2615
|
+
|
|
2616
|
+
def misra_12_4(self, cfg):
|
|
2617
|
+
for expr in cfg.tokenlist:
|
|
2618
|
+
if not expr.astOperand2 or not expr.astOperand1:
|
|
2619
|
+
continue
|
|
2620
|
+
if expr.valueType is None:
|
|
2621
|
+
continue
|
|
2622
|
+
if expr.valueType.sign is None or expr.valueType.sign != 'unsigned':
|
|
2623
|
+
continue
|
|
2624
|
+
if expr.valueType.pointer > 0:
|
|
2625
|
+
continue
|
|
2626
|
+
if not expr.valueType.isIntegral():
|
|
2627
|
+
continue
|
|
2628
|
+
op1 = expr.astOperand1.getKnownIntValue()
|
|
2629
|
+
if op1 is None:
|
|
2630
|
+
continue
|
|
2631
|
+
op2 = expr.astOperand2.getKnownIntValue()
|
|
2632
|
+
if op2 is None:
|
|
2633
|
+
continue
|
|
2634
|
+
bits = bitsOfEssentialType('unsigned ' + expr.valueType.type)
|
|
2635
|
+
if bits <= 0 or bits >= 64:
|
|
2636
|
+
continue
|
|
2637
|
+
max_value = (1 << bits) - 1
|
|
2638
|
+
if not is_constant_integer_expression(expr):
|
|
2639
|
+
continue
|
|
2640
|
+
if expr.str == '+' and op1 + op2 > max_value:
|
|
2641
|
+
self.reportError(expr, 12, 4)
|
|
2642
|
+
elif expr.str == '-' and op1 - op2 < 0:
|
|
2643
|
+
self.reportError(expr, 12, 4)
|
|
2644
|
+
elif expr.str == '*' and op1 * op2 > max_value:
|
|
2645
|
+
self.reportError(expr, 12, 4)
|
|
2646
|
+
|
|
2647
|
+
|
|
2648
|
+
def misra_13_1(self, data):
|
|
2649
|
+
for token in data.tokenlist:
|
|
2650
|
+
if simpleMatch(token, ") {") and token.next.astParent == token.link:
|
|
2651
|
+
pass
|
|
2652
|
+
elif not simpleMatch(token, '= {'):
|
|
2653
|
+
continue
|
|
2654
|
+
init = token.next
|
|
2655
|
+
end = init.link
|
|
2656
|
+
if not end:
|
|
2657
|
+
continue # syntax is broken
|
|
2658
|
+
|
|
2659
|
+
tn = init
|
|
2660
|
+
while tn and tn != end:
|
|
2661
|
+
if tn.str == '[' and tn.link:
|
|
2662
|
+
tn = tn.link
|
|
2663
|
+
if tn and tn.next and tn.next.str == '=':
|
|
2664
|
+
tn = tn.next.next
|
|
2665
|
+
continue
|
|
2666
|
+
else:
|
|
2667
|
+
break
|
|
2668
|
+
if tn.str == '.' and tn.next and tn.next.isName:
|
|
2669
|
+
tn = tn.next
|
|
2670
|
+
if tn.next and tn.next.str == '=':
|
|
2671
|
+
tn = tn.next.next
|
|
2672
|
+
continue
|
|
2673
|
+
if tn.str in {'++', '--'} or tn.isAssignmentOp:
|
|
2674
|
+
self.reportError(init, 13, 1)
|
|
2675
|
+
tn = tn.next
|
|
2676
|
+
|
|
2677
|
+
def misra_13_3(self, data):
|
|
2678
|
+
for token in data.tokenlist:
|
|
2679
|
+
if token.str not in ('++', '--'):
|
|
2680
|
+
continue
|
|
2681
|
+
astTop = token
|
|
2682
|
+
while astTop.astParent and astTop.astParent.str not in (',', ';'):
|
|
2683
|
+
astTop = astTop.astParent
|
|
2684
|
+
if countSideEffects(astTop) >= 2:
|
|
2685
|
+
self.reportError(astTop, 13, 3)
|
|
2686
|
+
|
|
2687
|
+
def misra_13_4(self, data):
|
|
2688
|
+
for token in data.tokenlist:
|
|
2689
|
+
if token.str != '=':
|
|
2690
|
+
continue
|
|
2691
|
+
if not token.astParent:
|
|
2692
|
+
continue
|
|
2693
|
+
if token.astOperand1.str == '[' and token.astOperand1.previous.str in ('{', ','):
|
|
2694
|
+
continue
|
|
2695
|
+
if not (token.astParent.str in [',', ';', '{']):
|
|
2696
|
+
self.reportError(token, 13, 4)
|
|
2697
|
+
|
|
2698
|
+
def misra_13_5(self, data):
|
|
2699
|
+
for token in data.tokenlist:
|
|
2700
|
+
if token.isLogicalOp and countSideEffectsRecursive(token.astOperand2) > 0:
|
|
2701
|
+
self.reportError(token, 13, 5)
|
|
2702
|
+
|
|
2703
|
+
def misra_13_6(self, data):
|
|
2704
|
+
for token in data.tokenlist:
|
|
2705
|
+
if token.str == 'sizeof' and countSideEffectsRecursive(token.next) > 0:
|
|
2706
|
+
self.reportError(token, 13, 6)
|
|
2707
|
+
|
|
2708
|
+
def misra_14_1(self, data):
|
|
2709
|
+
for token in data.tokenlist:
|
|
2710
|
+
if token.str == 'for':
|
|
2711
|
+
exprs = getForLoopExpressions(token)
|
|
2712
|
+
if not exprs:
|
|
2713
|
+
continue
|
|
2714
|
+
for counter in findCounterTokens(exprs[1]):
|
|
2715
|
+
if counter.valueType and counter.valueType.isFloat():
|
|
2716
|
+
self.reportError(token, 14, 1)
|
|
2717
|
+
elif token.str == 'while':
|
|
2718
|
+
if isFloatCounterInWhileLoop(token):
|
|
2719
|
+
self.reportError(token, 14, 1)
|
|
2720
|
+
|
|
2721
|
+
def misra_14_2(self, data):
|
|
2722
|
+
for token in data.tokenlist:
|
|
2723
|
+
if token.str == 'for':
|
|
2724
|
+
expressions = getForLoopExpressions(token)
|
|
2725
|
+
if not expressions:
|
|
2726
|
+
continue
|
|
2727
|
+
if expressions[0] and not expressions[0].isAssignmentOp:
|
|
2728
|
+
self.reportError(token, 14, 2)
|
|
2729
|
+
if countSideEffectsRecursive(expressions[1]) > 0:
|
|
2730
|
+
self.reportError(token, 14, 2)
|
|
2731
|
+
if countSideEffectsRecursive(expressions[2]) > 1:
|
|
2732
|
+
self.reportError(token, 14, 2)
|
|
2733
|
+
|
|
2734
|
+
counter_vars_first_clause, counter_vars_exit_modified = getForLoopCounterVariables(token)
|
|
2735
|
+
if len(counter_vars_exit_modified) == 0:
|
|
2736
|
+
# if it's not possible to identify a loop counter, all 3 clauses must be empty
|
|
2737
|
+
for idx in range(len(expressions)):
|
|
2738
|
+
if expressions[idx]:
|
|
2739
|
+
self.reportError(token, 14, 2)
|
|
2740
|
+
break
|
|
2741
|
+
elif len(counter_vars_exit_modified) > 1:
|
|
2742
|
+
# there shall be a single loop counter
|
|
2743
|
+
self.reportError(token, 14, 2)
|
|
2744
|
+
else: # len(counter_vars_exit_modified) == 1:
|
|
2745
|
+
loop_counter = counter_vars_exit_modified.pop()
|
|
2746
|
+
# if the first clause is not empty, then it shall (declare and) initialize the loop counter
|
|
2747
|
+
if expressions[0] is not None and loop_counter not in counter_vars_first_clause:
|
|
2748
|
+
self.reportError(token, 14, 2)
|
|
2749
|
+
|
|
2750
|
+
# Inspect modification of loop counter in loop body
|
|
2751
|
+
body_scope = token.next.link.next.scope
|
|
2752
|
+
if not body_scope:
|
|
2753
|
+
continue
|
|
2754
|
+
tn = body_scope.bodyStart
|
|
2755
|
+
while tn and tn != body_scope.bodyEnd:
|
|
2756
|
+
if tn.variable == loop_counter:
|
|
2757
|
+
if tn.next:
|
|
2758
|
+
# TODO: Check modifications in function calls
|
|
2759
|
+
if countSideEffectsRecursive(tn.next) > 0:
|
|
2760
|
+
self.reportError(tn, 14, 2)
|
|
2761
|
+
tn = tn.next
|
|
2762
|
+
|
|
2763
|
+
def misra_14_4(self, data):
|
|
2764
|
+
for token in data.tokenlist:
|
|
2765
|
+
if token.str != '(':
|
|
2766
|
+
continue
|
|
2767
|
+
if not token.astOperand1 or not (token.astOperand1.str in ['if', 'while']):
|
|
2768
|
+
continue
|
|
2769
|
+
if not isBoolExpression(token.astOperand2):
|
|
2770
|
+
self.reportError(token, 14, 4)
|
|
2771
|
+
|
|
2772
|
+
def misra_15_1(self, data):
|
|
2773
|
+
for token in data.tokenlist:
|
|
2774
|
+
if token.str == "goto":
|
|
2775
|
+
self.reportError(token, 15, 1)
|
|
2776
|
+
|
|
2777
|
+
def misra_15_2(self, data):
|
|
2778
|
+
for token in data.tokenlist:
|
|
2779
|
+
if token.str != 'goto':
|
|
2780
|
+
continue
|
|
2781
|
+
if (not token.next) or (not token.next.isName):
|
|
2782
|
+
continue
|
|
2783
|
+
if not findGotoLabel(token):
|
|
2784
|
+
self.reportError(token, 15, 2)
|
|
2785
|
+
|
|
2786
|
+
def misra_15_3(self, data):
|
|
2787
|
+
for token in data.tokenlist:
|
|
2788
|
+
if token.str != 'goto':
|
|
2789
|
+
continue
|
|
2790
|
+
if (not token.next) or (not token.next.isName):
|
|
2791
|
+
continue
|
|
2792
|
+
tok = findGotoLabel(token)
|
|
2793
|
+
if not tok:
|
|
2794
|
+
continue
|
|
2795
|
+
scope = token.scope
|
|
2796
|
+
while scope and scope != tok.scope:
|
|
2797
|
+
scope = scope.nestedIn
|
|
2798
|
+
if not scope:
|
|
2799
|
+
self.reportError(token, 15, 3)
|
|
2800
|
+
# Jump crosses from one switch-clause to another is non-compliant
|
|
2801
|
+
elif scope.type == 'Switch':
|
|
2802
|
+
# Search for start of a current case block
|
|
2803
|
+
tcase_start = token
|
|
2804
|
+
while tcase_start and tcase_start.str not in ('case', 'default'):
|
|
2805
|
+
tcase_start = tcase_start.previous
|
|
2806
|
+
# Make sure that goto label doesn't occurs in the other
|
|
2807
|
+
# switch-clauses
|
|
2808
|
+
if tcase_start:
|
|
2809
|
+
t = scope.bodyStart
|
|
2810
|
+
in_this_case = False
|
|
2811
|
+
while t and t != scope.bodyEnd:
|
|
2812
|
+
if t == tcase_start:
|
|
2813
|
+
in_this_case = True
|
|
2814
|
+
if in_this_case and t.str not in ('case', 'default'):
|
|
2815
|
+
in_this_case = False
|
|
2816
|
+
if t == tok and not in_this_case:
|
|
2817
|
+
self.reportError(token, 15, 3)
|
|
2818
|
+
break
|
|
2819
|
+
t = t.next
|
|
2820
|
+
|
|
2821
|
+
def misra_15_4(self, data):
|
|
2822
|
+
# Return a list of scopes affected by a break or goto
|
|
2823
|
+
def getLoopsAffectedByBreak(knownLoops, scope, isGoto):
|
|
2824
|
+
if scope and scope.type and scope.type not in ['Global', 'Function']:
|
|
2825
|
+
if not isGoto and scope.type == 'Switch':
|
|
2826
|
+
return
|
|
2827
|
+
if scope.type in ['For', 'While', 'Do']:
|
|
2828
|
+
knownLoops.append(scope)
|
|
2829
|
+
if not isGoto:
|
|
2830
|
+
return
|
|
2831
|
+
getLoopsAffectedByBreak(knownLoops, scope.nestedIn, isGoto)
|
|
2832
|
+
|
|
2833
|
+
loopWithBreaks = {}
|
|
2834
|
+
for token in data.tokenlist:
|
|
2835
|
+
if token.str not in ['break', 'goto']:
|
|
2836
|
+
continue
|
|
2837
|
+
|
|
2838
|
+
affectedLoopScopes = []
|
|
2839
|
+
getLoopsAffectedByBreak(affectedLoopScopes, token.scope, token.str == 'goto')
|
|
2840
|
+
for scope in affectedLoopScopes:
|
|
2841
|
+
if scope in loopWithBreaks:
|
|
2842
|
+
loopWithBreaks[scope] += 1
|
|
2843
|
+
else:
|
|
2844
|
+
loopWithBreaks[scope] = 1
|
|
2845
|
+
|
|
2846
|
+
for scope, breakCount in loopWithBreaks.items():
|
|
2847
|
+
if breakCount > 1:
|
|
2848
|
+
self.reportError(scope.bodyStart, 15, 4)
|
|
2849
|
+
|
|
2850
|
+
def misra_15_5(self, data):
|
|
2851
|
+
for token in data.tokenlist:
|
|
2852
|
+
if token.str == 'return' and token.scope.type != 'Function':
|
|
2853
|
+
self.reportError(token, 15, 5)
|
|
2854
|
+
|
|
2855
|
+
def misra_15_6(self, rawTokens):
|
|
2856
|
+
state = 0
|
|
2857
|
+
indent = 0
|
|
2858
|
+
tok1 = None
|
|
2859
|
+
def tokAt(tok,i):
|
|
2860
|
+
while i < 0 and tok:
|
|
2861
|
+
tok = tok.previous
|
|
2862
|
+
if tok.str.startswith('//') or tok.str.startswith('/*'):
|
|
2863
|
+
continue
|
|
2864
|
+
i += 1
|
|
2865
|
+
while i > 0 and tok:
|
|
2866
|
+
tok = tok.next
|
|
2867
|
+
if tok.str.startswith('//') or tok.str.startswith('/*'):
|
|
2868
|
+
continue
|
|
2869
|
+
i -= 1
|
|
2870
|
+
return tok
|
|
2871
|
+
|
|
2872
|
+
def strtokens(tok, i1, i2):
|
|
2873
|
+
tok1 = tokAt(tok, i1)
|
|
2874
|
+
tok2 = tokAt(tok, i2)
|
|
2875
|
+
tok = tok1
|
|
2876
|
+
s = ''
|
|
2877
|
+
while tok != tok2:
|
|
2878
|
+
if tok.str.startswith('//') or tok.str.startswith('/*'):
|
|
2879
|
+
tok = tok.next
|
|
2880
|
+
continue
|
|
2881
|
+
s += ' ' + tok.str
|
|
2882
|
+
tok = tok.next
|
|
2883
|
+
s += ' ' + tok.str
|
|
2884
|
+
return s[1:]
|
|
2885
|
+
|
|
2886
|
+
for token in rawTokens:
|
|
2887
|
+
if token.str in ['if', 'for', 'while']:
|
|
2888
|
+
if strtokens(token,-1,0) == '# if':
|
|
2889
|
+
continue
|
|
2890
|
+
if strtokens(token,-1,0) == "} while":
|
|
2891
|
+
# is there a 'do { .. } while'?
|
|
2892
|
+
start = rawlink(tokAt(token,-1))
|
|
2893
|
+
if start and strtokens(start, -1, 0) == 'do {':
|
|
2894
|
+
continue
|
|
2895
|
+
if state == 2:
|
|
2896
|
+
self.reportError(tok1, 15, 6)
|
|
2897
|
+
state = 1
|
|
2898
|
+
indent = 0
|
|
2899
|
+
tok1 = token
|
|
2900
|
+
elif token.str == 'else':
|
|
2901
|
+
if strtokens(token,-1,0) == '# else':
|
|
2902
|
+
continue
|
|
2903
|
+
if strtokens(token,0,1) == 'else if':
|
|
2904
|
+
continue
|
|
2905
|
+
if state == 2:
|
|
2906
|
+
self.reportError(tok1, 15, 6)
|
|
2907
|
+
state = 2
|
|
2908
|
+
indent = 0
|
|
2909
|
+
tok1 = token
|
|
2910
|
+
elif state == 1:
|
|
2911
|
+
if indent == 0 and token.str != '(':
|
|
2912
|
+
state = 0
|
|
2913
|
+
continue
|
|
2914
|
+
if token.str == '(':
|
|
2915
|
+
indent = indent + 1
|
|
2916
|
+
elif token.str == ')':
|
|
2917
|
+
if indent == 0:
|
|
2918
|
+
state = 0
|
|
2919
|
+
elif indent == 1:
|
|
2920
|
+
state = 2
|
|
2921
|
+
indent = indent - 1
|
|
2922
|
+
elif state == 2:
|
|
2923
|
+
if token.str.startswith('//') or token.str.startswith('/*'):
|
|
2924
|
+
continue
|
|
2925
|
+
state = 0
|
|
2926
|
+
if token.str not in ('{', '#'):
|
|
2927
|
+
self.reportError(tok1, 15, 6)
|
|
2928
|
+
|
|
2929
|
+
def misra_15_7(self, data):
|
|
2930
|
+
for scope in data.scopes:
|
|
2931
|
+
if scope.type != 'Else':
|
|
2932
|
+
continue
|
|
2933
|
+
if not simpleMatch(scope.bodyStart, '{ if ('):
|
|
2934
|
+
continue
|
|
2935
|
+
if scope.bodyStart.column > 0:
|
|
2936
|
+
continue
|
|
2937
|
+
tok = scope.bodyStart.next.next.link
|
|
2938
|
+
if not simpleMatch(tok, ') {'):
|
|
2939
|
+
continue
|
|
2940
|
+
tok = tok.next.link
|
|
2941
|
+
if not simpleMatch(tok, '} else'):
|
|
2942
|
+
self.reportError(tok, 15, 7)
|
|
2943
|
+
|
|
2944
|
+
def misra_16_1(self, cfg):
|
|
2945
|
+
for scope in cfg.scopes:
|
|
2946
|
+
if scope.type != 'Switch':
|
|
2947
|
+
continue
|
|
2948
|
+
in_case_or_default = False
|
|
2949
|
+
tok = scope.bodyStart.next
|
|
2950
|
+
while tok != scope.bodyEnd:
|
|
2951
|
+
if not in_case_or_default:
|
|
2952
|
+
if tok.str not in ('case', 'default'):
|
|
2953
|
+
self.reportError(tok, 16, 1)
|
|
2954
|
+
else:
|
|
2955
|
+
in_case_or_default = True
|
|
2956
|
+
else:
|
|
2957
|
+
if simpleMatch(tok, 'break ;'):
|
|
2958
|
+
in_case_or_default = False
|
|
2959
|
+
tok = tok.next
|
|
2960
|
+
if tok.str == '{':
|
|
2961
|
+
tok = tok.link
|
|
2962
|
+
if tok.scope.type == 'Unconditional' and simpleMatch(tok.previous.previous, 'break ;'):
|
|
2963
|
+
in_case_or_default = False
|
|
2964
|
+
tok = tok.next
|
|
2965
|
+
|
|
2966
|
+
def misra_16_2(self, data):
|
|
2967
|
+
for token in data.tokenlist:
|
|
2968
|
+
if token.str == 'case' and token.scope.type != 'Switch':
|
|
2969
|
+
self.reportError(token, 16, 2)
|
|
2970
|
+
|
|
2971
|
+
def misra_16_3(self, rawTokens):
|
|
2972
|
+
STATE_NONE = 0 # default state, not in switch case/default block
|
|
2973
|
+
STATE_BREAK = 1 # break/comment is seen but not its ';'
|
|
2974
|
+
STATE_OK = 2 # a case/default is allowed (we have seen 'break;'/'comment'/'{'/attribute)
|
|
2975
|
+
STATE_SWITCH = 3 # walking through switch statement scope
|
|
2976
|
+
|
|
2977
|
+
define = None
|
|
2978
|
+
state = STATE_NONE
|
|
2979
|
+
end_switch_token = None # end '}' for the switch scope
|
|
2980
|
+
for token in rawTokens:
|
|
2981
|
+
if simpleMatch(token, '# define'):
|
|
2982
|
+
define = token
|
|
2983
|
+
if define:
|
|
2984
|
+
if token.linenr != define.linenr:
|
|
2985
|
+
define = None
|
|
2986
|
+
else:
|
|
2987
|
+
continue
|
|
2988
|
+
|
|
2989
|
+
# Find switch scope borders
|
|
2990
|
+
if token.str == 'switch':
|
|
2991
|
+
state = STATE_SWITCH
|
|
2992
|
+
if state == STATE_SWITCH:
|
|
2993
|
+
if token.str == '{':
|
|
2994
|
+
end_switch_token = findRawLink(token)
|
|
2995
|
+
else:
|
|
2996
|
+
continue
|
|
2997
|
+
|
|
2998
|
+
if token.str == 'break' or token.str == 'return' or token.str == 'throw':
|
|
2999
|
+
state = STATE_BREAK
|
|
3000
|
+
elif token.str == ';':
|
|
3001
|
+
if state == STATE_BREAK:
|
|
3002
|
+
state = STATE_OK
|
|
3003
|
+
elif token.next and token.next == end_switch_token:
|
|
3004
|
+
self.reportError(token.next, 16, 3)
|
|
3005
|
+
else:
|
|
3006
|
+
state = STATE_NONE
|
|
3007
|
+
elif token.str.startswith('/*') or token.str.startswith('//'):
|
|
3008
|
+
if 'fallthrough' in token.str.lower():
|
|
3009
|
+
state = STATE_OK
|
|
3010
|
+
elif simpleMatch(token, '[ [ fallthrough ] ] ;'):
|
|
3011
|
+
state = STATE_BREAK
|
|
3012
|
+
elif token.str == '{':
|
|
3013
|
+
state = STATE_OK
|
|
3014
|
+
elif token.str == '}' and state == STATE_OK:
|
|
3015
|
+
# is this {} an unconditional block of code?
|
|
3016
|
+
prev = findRawLink(token)
|
|
3017
|
+
if prev:
|
|
3018
|
+
prev = prev.previous
|
|
3019
|
+
while prev and prev.str[:2] in ('//', '/*'):
|
|
3020
|
+
prev = prev.previous
|
|
3021
|
+
if (prev is None) or (prev.str not in ':;{}'):
|
|
3022
|
+
state = STATE_NONE
|
|
3023
|
+
elif token.str == 'case' or token.str == 'default':
|
|
3024
|
+
if state != STATE_OK:
|
|
3025
|
+
self.reportError(token, 16, 3)
|
|
3026
|
+
state = STATE_OK
|
|
3027
|
+
|
|
3028
|
+
def misra_16_4(self, data):
|
|
3029
|
+
for token in data.tokenlist:
|
|
3030
|
+
if token.str != 'switch':
|
|
3031
|
+
continue
|
|
3032
|
+
if not simpleMatch(token, 'switch ('):
|
|
3033
|
+
continue
|
|
3034
|
+
if not simpleMatch(token.next.link, ') {'):
|
|
3035
|
+
continue
|
|
3036
|
+
startTok = token.next.link.next
|
|
3037
|
+
tok = startTok.next
|
|
3038
|
+
while tok and tok.str != '}':
|
|
3039
|
+
if tok.str == '{':
|
|
3040
|
+
tok = tok.link
|
|
3041
|
+
elif tok.str == 'default':
|
|
3042
|
+
break
|
|
3043
|
+
tok = tok.next
|
|
3044
|
+
if tok and tok.str != 'default':
|
|
3045
|
+
self.reportError(token, 16, 4)
|
|
3046
|
+
|
|
3047
|
+
def misra_16_5(self, data):
|
|
3048
|
+
for token in data.tokenlist:
|
|
3049
|
+
if token.str != 'default':
|
|
3050
|
+
continue
|
|
3051
|
+
if token.previous and token.previous.str == '{':
|
|
3052
|
+
continue
|
|
3053
|
+
tok2 = token
|
|
3054
|
+
while tok2:
|
|
3055
|
+
if tok2.str in ('}', 'case'):
|
|
3056
|
+
break
|
|
3057
|
+
if tok2.str == '{':
|
|
3058
|
+
tok2 = tok2.link
|
|
3059
|
+
tok2 = tok2.next
|
|
3060
|
+
if tok2 and tok2.str == 'case':
|
|
3061
|
+
self.reportError(token, 16, 5)
|
|
3062
|
+
|
|
3063
|
+
def misra_16_6(self, data):
|
|
3064
|
+
for token in data.tokenlist:
|
|
3065
|
+
if not (simpleMatch(token, 'switch (') and simpleMatch(token.next.link, ') {')):
|
|
3066
|
+
continue
|
|
3067
|
+
tok = token.next.link.next.next
|
|
3068
|
+
count = 0
|
|
3069
|
+
while tok:
|
|
3070
|
+
if tok.str in ['break', 'return', 'throw']:
|
|
3071
|
+
count = count + 1
|
|
3072
|
+
elif tok.str == '{':
|
|
3073
|
+
tok = tok.link
|
|
3074
|
+
if isNoReturnScope(tok):
|
|
3075
|
+
count = count + 1
|
|
3076
|
+
elif tok.str == '}':
|
|
3077
|
+
break
|
|
3078
|
+
tok = tok.next
|
|
3079
|
+
if count < 2:
|
|
3080
|
+
self.reportError(token, 16, 6)
|
|
3081
|
+
|
|
3082
|
+
def misra_16_7(self, data):
|
|
3083
|
+
for token in data.tokenlist:
|
|
3084
|
+
if simpleMatch(token, 'switch (') and isBoolExpression(token.next.astOperand2):
|
|
3085
|
+
self.reportError(token, 16, 7)
|
|
3086
|
+
|
|
3087
|
+
def misra_17_1(self, data):
|
|
3088
|
+
for token in data.tokenlist:
|
|
3089
|
+
if isFunctionCall(token) and token.astOperand1.str in (
|
|
3090
|
+
'va_list', 'va_arg', 'va_start', 'va_end', 'va_copy'):
|
|
3091
|
+
self.reportError(token, 17, 1)
|
|
3092
|
+
elif token.str == 'va_list':
|
|
3093
|
+
self.reportError(token, 17, 1)
|
|
3094
|
+
|
|
3095
|
+
def misra_17_2(self, data):
|
|
3096
|
+
# find recursions..
|
|
3097
|
+
def find_recursive_call(search_for_function, direct_call, calls_map, visited=None):
|
|
3098
|
+
if visited is None:
|
|
3099
|
+
visited = set()
|
|
3100
|
+
if direct_call == search_for_function:
|
|
3101
|
+
return True
|
|
3102
|
+
for indirect_call in calls_map.get(direct_call, []):
|
|
3103
|
+
if indirect_call == search_for_function:
|
|
3104
|
+
return True
|
|
3105
|
+
if indirect_call in visited:
|
|
3106
|
+
# This has already been handled
|
|
3107
|
+
continue
|
|
3108
|
+
visited.add(indirect_call)
|
|
3109
|
+
if find_recursive_call(search_for_function, indirect_call, calls_map, visited):
|
|
3110
|
+
return True
|
|
3111
|
+
return False
|
|
3112
|
+
|
|
3113
|
+
# List functions called in each function
|
|
3114
|
+
function_calls = {}
|
|
3115
|
+
for scope in data.scopes:
|
|
3116
|
+
if scope.type != 'Function':
|
|
3117
|
+
continue
|
|
3118
|
+
calls = []
|
|
3119
|
+
tok = scope.bodyStart
|
|
3120
|
+
while tok != scope.bodyEnd:
|
|
3121
|
+
tok = tok.next
|
|
3122
|
+
if not isFunctionCall(tok, data.standards.c):
|
|
3123
|
+
continue
|
|
3124
|
+
f = tok.astOperand1.function
|
|
3125
|
+
if f is not None and f not in calls:
|
|
3126
|
+
calls.append(f)
|
|
3127
|
+
function_calls[scope.function] = calls
|
|
3128
|
+
|
|
3129
|
+
# Report warnings for all recursions..
|
|
3130
|
+
for func in function_calls:
|
|
3131
|
+
for call in function_calls[func]:
|
|
3132
|
+
if not find_recursive_call(func, call, function_calls):
|
|
3133
|
+
# Function call is not recursive
|
|
3134
|
+
continue
|
|
3135
|
+
# Warn about all functions calls..
|
|
3136
|
+
for scope in data.scopes:
|
|
3137
|
+
if scope.type != 'Function' or scope.function != func:
|
|
3138
|
+
continue
|
|
3139
|
+
tok = scope.bodyStart
|
|
3140
|
+
while tok != scope.bodyEnd:
|
|
3141
|
+
if tok.function and tok.function == call:
|
|
3142
|
+
self.reportError(tok, 17, 2)
|
|
3143
|
+
tok = tok.next
|
|
3144
|
+
|
|
3145
|
+
def misra_17_3(self, cfg):
|
|
3146
|
+
for w in cfg.clang_warnings:
|
|
3147
|
+
if w['message'].endswith('[-Wimplicit-function-declaration]'):
|
|
3148
|
+
self.reportError(cppcheckdata.Location(w), 17, 3)
|
|
3149
|
+
|
|
3150
|
+
def misra_17_6(self, rawTokens):
|
|
3151
|
+
for token in rawTokens:
|
|
3152
|
+
if simpleMatch(token, '[ static'):
|
|
3153
|
+
self.reportError(token, 17, 6)
|
|
3154
|
+
|
|
3155
|
+
def misra_17_7(self, data):
|
|
3156
|
+
for token in data.tokenlist:
|
|
3157
|
+
if not token.scope.isExecutable:
|
|
3158
|
+
continue
|
|
3159
|
+
if token.str != '(' or token.astParent:
|
|
3160
|
+
continue
|
|
3161
|
+
if not token.previous.isName or token.previous.varId:
|
|
3162
|
+
continue
|
|
3163
|
+
if token.valueType is None:
|
|
3164
|
+
continue
|
|
3165
|
+
if token.valueType.type == 'void' and token.valueType.pointer == 0:
|
|
3166
|
+
continue
|
|
3167
|
+
self.reportError(token, 17, 7)
|
|
3168
|
+
|
|
3169
|
+
def misra_17_8(self, data):
|
|
3170
|
+
for token in data.tokenlist:
|
|
3171
|
+
if not (token.isAssignmentOp or (token.str in ('++', '--'))):
|
|
3172
|
+
continue
|
|
3173
|
+
if not token.astOperand1:
|
|
3174
|
+
continue
|
|
3175
|
+
var = token.astOperand1.variable
|
|
3176
|
+
if var and var.isArgument:
|
|
3177
|
+
self.reportError(token, 17, 8)
|
|
3178
|
+
|
|
3179
|
+
def misra_18_4(self, data):
|
|
3180
|
+
for token in data.tokenlist:
|
|
3181
|
+
if token.str not in ('+', '-', '+=', '-='):
|
|
3182
|
+
continue
|
|
3183
|
+
if token.astOperand1 is None or token.astOperand2 is None:
|
|
3184
|
+
continue
|
|
3185
|
+
vt1 = token.astOperand1.valueType
|
|
3186
|
+
vt2 = token.astOperand2.valueType
|
|
3187
|
+
if vt1 and vt1.pointer > 0:
|
|
3188
|
+
self.reportError(token, 18, 4)
|
|
3189
|
+
elif vt2 and vt2.pointer > 0:
|
|
3190
|
+
self.reportError(token, 18, 4)
|
|
3191
|
+
|
|
3192
|
+
def misra_18_5(self, data):
|
|
3193
|
+
for var in data.variables:
|
|
3194
|
+
if not var.isPointer:
|
|
3195
|
+
continue
|
|
3196
|
+
typetok = var.nameToken
|
|
3197
|
+
count = 0
|
|
3198
|
+
while typetok:
|
|
3199
|
+
if typetok.str == '*':
|
|
3200
|
+
count = count + 1
|
|
3201
|
+
elif not typetok.isName:
|
|
3202
|
+
break
|
|
3203
|
+
typetok = typetok.previous
|
|
3204
|
+
if count > 2:
|
|
3205
|
+
self.reportError(var.nameToken, 18, 5)
|
|
3206
|
+
|
|
3207
|
+
def misra_18_7(self, data):
|
|
3208
|
+
for scope in data.scopes:
|
|
3209
|
+
if scope.type != 'Struct':
|
|
3210
|
+
continue
|
|
3211
|
+
|
|
3212
|
+
token = scope.bodyStart.next
|
|
3213
|
+
while token != scope.bodyEnd and token is not None:
|
|
3214
|
+
# Handle nested structures to not duplicate an error.
|
|
3215
|
+
if token.str == '{':
|
|
3216
|
+
token = token.link
|
|
3217
|
+
|
|
3218
|
+
if cppcheckdata.simpleMatch(token, "[ ]"):
|
|
3219
|
+
self.reportError(token, 18, 7)
|
|
3220
|
+
break
|
|
3221
|
+
token = token.next
|
|
3222
|
+
|
|
3223
|
+
def misra_18_8(self, data):
|
|
3224
|
+
for var in data.variables:
|
|
3225
|
+
if not var.isArray or not var.isLocal:
|
|
3226
|
+
continue
|
|
3227
|
+
# TODO Array dimensions are not available in dump, must look in tokens
|
|
3228
|
+
typetok = var.nameToken.next
|
|
3229
|
+
if not typetok or typetok.str != '[':
|
|
3230
|
+
continue
|
|
3231
|
+
# Unknown define or syntax error
|
|
3232
|
+
if not typetok.astOperand2:
|
|
3233
|
+
continue
|
|
3234
|
+
if not isConstantExpression(typetok.astOperand2):
|
|
3235
|
+
self.reportError(var.nameToken, 18, 8)
|
|
3236
|
+
|
|
3237
|
+
def misra_19_2(self, data):
|
|
3238
|
+
for token in data.tokenlist:
|
|
3239
|
+
if token.str == 'union':
|
|
3240
|
+
self.reportError(token, 19, 2)
|
|
3241
|
+
|
|
3242
|
+
def misra_20_1(self, data):
|
|
3243
|
+
token_in_file = {}
|
|
3244
|
+
for token in data.tokenlist:
|
|
3245
|
+
if token.file not in token_in_file:
|
|
3246
|
+
token_in_file[token.file] = int(token.linenr)
|
|
3247
|
+
else:
|
|
3248
|
+
token_in_file[token.file] = min(token_in_file[token.file], int(token.linenr))
|
|
3249
|
+
|
|
3250
|
+
for directive in data.directives:
|
|
3251
|
+
if not directive.str.startswith('#include'):
|
|
3252
|
+
continue
|
|
3253
|
+
if directive.file not in token_in_file:
|
|
3254
|
+
continue
|
|
3255
|
+
if token_in_file[directive.file] < int(directive.linenr):
|
|
3256
|
+
self.reportError(directive, 20, 1)
|
|
3257
|
+
|
|
3258
|
+
def misra_20_2(self, data):
|
|
3259
|
+
for directive in data.directives:
|
|
3260
|
+
if not directive.str.startswith('#include '):
|
|
3261
|
+
continue
|
|
3262
|
+
for pattern in ('\\', '//', '/*', ',', "'"):
|
|
3263
|
+
if pattern in directive.str:
|
|
3264
|
+
self.reportError(directive, 20, 2)
|
|
3265
|
+
break
|
|
3266
|
+
|
|
3267
|
+
def misra_20_3(self, data):
|
|
3268
|
+
for directive in data.directives:
|
|
3269
|
+
if not directive.str.startswith('#include '):
|
|
3270
|
+
continue
|
|
3271
|
+
|
|
3272
|
+
words = directive.str.split(' ')
|
|
3273
|
+
|
|
3274
|
+
# If include directive contains more than two words, here would be
|
|
3275
|
+
# violation anyway.
|
|
3276
|
+
if len(words) > 2:
|
|
3277
|
+
self.reportError(directive, 20, 3)
|
|
3278
|
+
|
|
3279
|
+
# Handle include directives with not quoted argument
|
|
3280
|
+
elif len(words) > 1:
|
|
3281
|
+
filename = words[1]
|
|
3282
|
+
if not ((filename.startswith('"') and
|
|
3283
|
+
filename.endswith('"')) or
|
|
3284
|
+
(filename.startswith('<') and
|
|
3285
|
+
filename.endswith('>'))):
|
|
3286
|
+
# We are handle only directly included files in the
|
|
3287
|
+
# following format: #include file.h
|
|
3288
|
+
# Cases with macro expansion provided by MISRA document are
|
|
3289
|
+
# skipped because we don't always have access to directive
|
|
3290
|
+
# definition.
|
|
3291
|
+
if '.' in filename:
|
|
3292
|
+
self.reportError(directive, 20, 3)
|
|
3293
|
+
|
|
3294
|
+
def misra_20_4(self, data):
|
|
3295
|
+
for directive in data.directives:
|
|
3296
|
+
res = re.search(r'#define ([a-z][a-z0-9_]+)', directive.str)
|
|
3297
|
+
if res and isKeyword(res.group(1), data.standards.c):
|
|
3298
|
+
self.reportError(directive, 20, 4)
|
|
3299
|
+
|
|
3300
|
+
def misra_20_5(self, data):
|
|
3301
|
+
for directive in data.directives:
|
|
3302
|
+
if directive.str.startswith('#undef '):
|
|
3303
|
+
self.reportError(directive, 20, 5)
|
|
3304
|
+
|
|
3305
|
+
def misra_20_7(self, data):
|
|
3306
|
+
def find_string_concat(exp, arg, directive_args):
|
|
3307
|
+
# Handle concatenation of string literals, e.g.:
|
|
3308
|
+
# #define MACRO(A, B) (A " " B)
|
|
3309
|
+
# Addon should not report errors for both macro arguments.
|
|
3310
|
+
arg_pos = exp.find(arg, 0)
|
|
3311
|
+
need_check = False
|
|
3312
|
+
skip_next = False
|
|
3313
|
+
state_in_string = False
|
|
3314
|
+
pos_search = arg_pos + 1
|
|
3315
|
+
directive_args = [a.strip() for a in directive_args if a != arg]
|
|
3316
|
+
arg = arg.strip()
|
|
3317
|
+
while pos_search < len(exp):
|
|
3318
|
+
if exp[pos_search] == '"':
|
|
3319
|
+
if state_in_string:
|
|
3320
|
+
state_in_string = False
|
|
3321
|
+
else:
|
|
3322
|
+
state_in_string = True
|
|
3323
|
+
pos_search += 1
|
|
3324
|
+
elif exp[pos_search].isalnum():
|
|
3325
|
+
word = ""
|
|
3326
|
+
while pos_search < len(exp) and exp[pos_search].isalnum():
|
|
3327
|
+
word += exp[pos_search]
|
|
3328
|
+
pos_search += 1
|
|
3329
|
+
if word == arg:
|
|
3330
|
+
pos_search += 1
|
|
3331
|
+
elif word in directive_args:
|
|
3332
|
+
skip_next = True
|
|
3333
|
+
break
|
|
3334
|
+
elif exp[pos_search] == ' ':
|
|
3335
|
+
pos_search += 1
|
|
3336
|
+
elif state_in_string:
|
|
3337
|
+
pos_search += 1
|
|
3338
|
+
else:
|
|
3339
|
+
need_check = True
|
|
3340
|
+
break
|
|
3341
|
+
return need_check, skip_next
|
|
3342
|
+
|
|
3343
|
+
for directive in data.directives:
|
|
3344
|
+
d = Define(directive)
|
|
3345
|
+
exp = '(' + d.expansionList + ')'
|
|
3346
|
+
skip_next = False
|
|
3347
|
+
for arg in d.args:
|
|
3348
|
+
if skip_next:
|
|
3349
|
+
_, skip_next = find_string_concat(exp, arg, d.args)
|
|
3350
|
+
continue
|
|
3351
|
+
need_check, skip_next = find_string_concat(exp, arg, d.args)
|
|
3352
|
+
if not need_check:
|
|
3353
|
+
continue
|
|
3354
|
+
|
|
3355
|
+
pos = 0
|
|
3356
|
+
while pos < len(exp):
|
|
3357
|
+
pos = exp.find(arg, pos)
|
|
3358
|
+
if pos < 0:
|
|
3359
|
+
break
|
|
3360
|
+
# is 'arg' used at position pos
|
|
3361
|
+
pos1 = pos - 1
|
|
3362
|
+
pos2 = pos + len(arg)
|
|
3363
|
+
pos = pos2
|
|
3364
|
+
if pos1 >= 0 and (isalnum(exp[pos1]) or exp[pos1] == '_'):
|
|
3365
|
+
continue
|
|
3366
|
+
if pos2 < len(exp) and (isalnum(exp[pos2]) or exp[pos2] == '_'):
|
|
3367
|
+
continue
|
|
3368
|
+
|
|
3369
|
+
while pos1 >= 0 and exp[pos1] == ' ':
|
|
3370
|
+
pos1 -= 1
|
|
3371
|
+
if exp[pos1] == '#':
|
|
3372
|
+
continue
|
|
3373
|
+
if exp[pos1] not in '([,.':
|
|
3374
|
+
self.reportError(directive, 20, 7)
|
|
3375
|
+
break
|
|
3376
|
+
while pos2 < len(exp) and exp[pos2] == ' ':
|
|
3377
|
+
pos2 += 1
|
|
3378
|
+
if pos2 < len(exp) and exp[pos2] not in ')]#,':
|
|
3379
|
+
self.reportError(directive, 20, 7)
|
|
3380
|
+
break
|
|
3381
|
+
|
|
3382
|
+
def misra_20_8(self, cfg):
|
|
3383
|
+
for cond in cfg.preprocessor_if_conditions:
|
|
3384
|
+
#print(cond)
|
|
3385
|
+
if cond.result and cond.result not in (0,1):
|
|
3386
|
+
self.reportError(cond, 20, 8)
|
|
3387
|
+
|
|
3388
|
+
def misra_20_9(self, cfg):
|
|
3389
|
+
for cond in cfg.preprocessor_if_conditions:
|
|
3390
|
+
if cond.E is None:
|
|
3391
|
+
continue
|
|
3392
|
+
defined = []
|
|
3393
|
+
for directive in cfg.directives:
|
|
3394
|
+
if directive.file == cond.file and directive.linenr == cond.linenr:
|
|
3395
|
+
for name in re.findall(r'[^_a-zA-Z0-9]defined[ ]*\([ ]*([_a-zA-Z0-9]+)[ ]*\)', directive.str):
|
|
3396
|
+
defined.append(name)
|
|
3397
|
+
for name in re.findall(r'[^_a-zA-Z0-9]defined[ ]*([_a-zA-Z0-9]+)', directive.str):
|
|
3398
|
+
defined.append(name)
|
|
3399
|
+
break
|
|
3400
|
+
for s in cond.E.split(' '):
|
|
3401
|
+
if (s[0] >= 'A' and s[0] <= 'Z') or (s[0] >= 'a' and s[0] <= 'z'):
|
|
3402
|
+
if isKeyword(s):
|
|
3403
|
+
continue
|
|
3404
|
+
if s in defined:
|
|
3405
|
+
continue
|
|
3406
|
+
self.reportError(cond, 20, 9)
|
|
3407
|
+
|
|
3408
|
+
def misra_20_10(self, data):
|
|
3409
|
+
for directive in data.directives:
|
|
3410
|
+
d = Define(directive)
|
|
3411
|
+
if d.expansionList.find('#') >= 0:
|
|
3412
|
+
self.reportError(directive, 20, 10)
|
|
3413
|
+
|
|
3414
|
+
def misra_20_11(self, cfg):
|
|
3415
|
+
for directive in cfg.directives:
|
|
3416
|
+
d = Define(directive)
|
|
3417
|
+
for arg in d.args:
|
|
3418
|
+
res = re.search(r'[^#]#[ ]*%s[ ]*##' % arg, ' ' + d.expansionList)
|
|
3419
|
+
if res:
|
|
3420
|
+
self.reportError(directive, 20, 11)
|
|
3421
|
+
|
|
3422
|
+
def misra_20_12(self, cfg):
|
|
3423
|
+
def _is_hash_hash_op(expansion_list, arg):
|
|
3424
|
+
return re.search(r'##[ ]*%s[^a-zA-Z0-9_]' % arg, expansion_list) or \
|
|
3425
|
+
re.search(r'[^a-zA-Z0-9_]%s[ ]*##' % arg, expansion_list)
|
|
3426
|
+
|
|
3427
|
+
def _is_other_op(expansion_list, arg):
|
|
3428
|
+
pos = expansion_list.find(arg)
|
|
3429
|
+
while pos >= 0:
|
|
3430
|
+
pos1 = pos - 1
|
|
3431
|
+
pos2 = pos + len(arg)
|
|
3432
|
+
pos = expansion_list.find(arg, pos2)
|
|
3433
|
+
if isalnum(expansion_list[pos1]) or expansion_list[pos1] == '_':
|
|
3434
|
+
continue
|
|
3435
|
+
if isalnum(expansion_list[pos2]) or expansion_list[pos2] == '_':
|
|
3436
|
+
continue
|
|
3437
|
+
while expansion_list[pos1] == ' ':
|
|
3438
|
+
pos1 = pos1 - 1
|
|
3439
|
+
if expansion_list[pos1] == '#':
|
|
3440
|
+
continue
|
|
3441
|
+
while expansion_list[pos2] == ' ':
|
|
3442
|
+
pos2 = pos2 + 1
|
|
3443
|
+
if expansion_list[pos2] == '#':
|
|
3444
|
+
continue
|
|
3445
|
+
return True
|
|
3446
|
+
return False
|
|
3447
|
+
|
|
3448
|
+
def _is_arg_macro_usage(directive, arg):
|
|
3449
|
+
for macro_usage in cfg.macro_usage:
|
|
3450
|
+
if macro_usage.file == directive.file and macro_usage.linenr == directive.linenr:
|
|
3451
|
+
for macro_usage_arg in cfg.macro_usage:
|
|
3452
|
+
if macro_usage_arg == macro_usage:
|
|
3453
|
+
continue
|
|
3454
|
+
if (macro_usage.usefile == macro_usage_arg.usefile and
|
|
3455
|
+
macro_usage.uselinenr == macro_usage_arg.uselinenr and
|
|
3456
|
+
macro_usage.usecolumn == macro_usage_arg.usecolumn):
|
|
3457
|
+
# TODO: check arg better
|
|
3458
|
+
return True
|
|
3459
|
+
return False
|
|
3460
|
+
|
|
3461
|
+
for directive in cfg.directives:
|
|
3462
|
+
define = Define(directive)
|
|
3463
|
+
expansion_list = '(%s)' % define.expansionList
|
|
3464
|
+
for arg in define.args:
|
|
3465
|
+
if not _is_hash_hash_op(expansion_list, arg):
|
|
3466
|
+
continue
|
|
3467
|
+
if not _is_other_op(expansion_list, arg):
|
|
3468
|
+
continue
|
|
3469
|
+
if _is_arg_macro_usage(directive, arg):
|
|
3470
|
+
self.reportError(directive, 20, 12)
|
|
3471
|
+
break
|
|
3472
|
+
|
|
3473
|
+
def misra_20_13(self, data):
|
|
3474
|
+
dir_pattern = re.compile(r'#[ ]*([^ (<]*)')
|
|
3475
|
+
for directive in data.directives:
|
|
3476
|
+
dir = directive.str
|
|
3477
|
+
mo = dir_pattern.match(dir)
|
|
3478
|
+
if mo:
|
|
3479
|
+
dir = mo.group(1)
|
|
3480
|
+
if dir not in ['define', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef', 'include',
|
|
3481
|
+
'pragma', 'undef', 'warning']:
|
|
3482
|
+
self.reportError(directive, 20, 13)
|
|
3483
|
+
|
|
3484
|
+
def misra_20_14(self, data):
|
|
3485
|
+
# stack for #if blocks. contains the #if directive until the corresponding #endif is seen.
|
|
3486
|
+
# the size increases when there are inner #if directives.
|
|
3487
|
+
ifStack = []
|
|
3488
|
+
for directive in data.directives:
|
|
3489
|
+
if directive.str.startswith('#if ') or directive.str.startswith('#ifdef ') or directive.str.startswith(
|
|
3490
|
+
'#ifndef '):
|
|
3491
|
+
ifStack.append(directive)
|
|
3492
|
+
elif directive.str == '#else' or directive.str.startswith('#elif '):
|
|
3493
|
+
if len(ifStack) == 0:
|
|
3494
|
+
self.reportError(directive, 20, 14)
|
|
3495
|
+
ifStack.append(directive)
|
|
3496
|
+
elif directive.file != ifStack[-1].file:
|
|
3497
|
+
self.reportError(directive, 20, 14)
|
|
3498
|
+
elif directive.str == '#endif':
|
|
3499
|
+
if len(ifStack) == 0:
|
|
3500
|
+
self.reportError(directive, 20, 14)
|
|
3501
|
+
elif directive.file != ifStack[-1].file:
|
|
3502
|
+
self.reportError(directive, 20, 14)
|
|
3503
|
+
ifStack.pop()
|
|
3504
|
+
|
|
3505
|
+
def misra_21_1(self, data):
|
|
3506
|
+
re_forbidden_macro = re.compile(r'#(?:define|undef) _[_A-Z]+')
|
|
3507
|
+
re_macro_name = re.compile(r'#(?:define|undef) (.+)[ $]')
|
|
3508
|
+
|
|
3509
|
+
for d in data.directives:
|
|
3510
|
+
# Search for forbidden identifiers
|
|
3511
|
+
m = re.search(re_forbidden_macro, d.str)
|
|
3512
|
+
if m:
|
|
3513
|
+
self.reportError(d, 21, 1)
|
|
3514
|
+
continue
|
|
3515
|
+
|
|
3516
|
+
# Search standard library identifiers in macro names
|
|
3517
|
+
m = re.search(re_macro_name, d.str)
|
|
3518
|
+
if not m:
|
|
3519
|
+
continue
|
|
3520
|
+
name = m.group(1)
|
|
3521
|
+
if isStdLibId(name, data.standards.c):
|
|
3522
|
+
self.reportError(d, 21, 1)
|
|
3523
|
+
|
|
3524
|
+
def misra_21_2(self, cfg):
|
|
3525
|
+
for directive in cfg.directives:
|
|
3526
|
+
define = Define(directive)
|
|
3527
|
+
if re.match(r'_+BUILTIN_.*', define.name.upper()):
|
|
3528
|
+
self.reportError(directive, 21, 2)
|
|
3529
|
+
for func in cfg.functions:
|
|
3530
|
+
if isStdLibId(func.name, cfg.standards.c):
|
|
3531
|
+
tok = func.tokenDef if func.tokenDef else func.token
|
|
3532
|
+
self.reportError(tok, 21, 2)
|
|
3533
|
+
|
|
3534
|
+
def misra_21_3(self, data):
|
|
3535
|
+
for token in data.tokenlist:
|
|
3536
|
+
if isFunctionCall(token) and (token.astOperand1.str in ('malloc', 'calloc', 'realloc', 'free')):
|
|
3537
|
+
self.reportError(token, 21, 3)
|
|
3538
|
+
|
|
3539
|
+
def misra_21_4(self, data):
|
|
3540
|
+
directive = findInclude(data.directives, '<setjmp.h>')
|
|
3541
|
+
if directive:
|
|
3542
|
+
self.reportError(directive, 21, 4)
|
|
3543
|
+
|
|
3544
|
+
def misra_21_5(self, data):
|
|
3545
|
+
directive = findInclude(data.directives, '<signal.h>')
|
|
3546
|
+
if directive:
|
|
3547
|
+
self.reportError(directive, 21, 5)
|
|
3548
|
+
|
|
3549
|
+
def misra_21_6(self, data):
|
|
3550
|
+
dir_stdio = findInclude(data.directives, '<stdio.h>')
|
|
3551
|
+
dir_wchar = findInclude(data.directives, '<wchar.h>')
|
|
3552
|
+
if dir_stdio:
|
|
3553
|
+
self.reportError(dir_stdio, 21, 6)
|
|
3554
|
+
if dir_wchar:
|
|
3555
|
+
self.reportError(dir_wchar, 21, 6)
|
|
3556
|
+
|
|
3557
|
+
def misra_21_7(self, data):
|
|
3558
|
+
for token in data.tokenlist:
|
|
3559
|
+
if isFunctionCall(token) and (token.astOperand1.str in ('atof', 'atoi', 'atol', 'atoll')):
|
|
3560
|
+
self.reportError(token, 21, 7)
|
|
3561
|
+
|
|
3562
|
+
def misra_21_8(self, data):
|
|
3563
|
+
for token in data.tokenlist:
|
|
3564
|
+
if isFunctionCall(token) and (token.astOperand1.str in ('abort', 'exit', 'getenv')):
|
|
3565
|
+
self.reportError(token, 21, 8)
|
|
3566
|
+
|
|
3567
|
+
def misra_21_9(self, data):
|
|
3568
|
+
for token in data.tokenlist:
|
|
3569
|
+
if (token.str in ('bsearch', 'qsort')) and token.next and token.next.str == '(':
|
|
3570
|
+
self.reportError(token, 21, 9)
|
|
3571
|
+
|
|
3572
|
+
def misra_21_10(self, data):
|
|
3573
|
+
directive = findInclude(data.directives, '<time.h>')
|
|
3574
|
+
if directive:
|
|
3575
|
+
self.reportError(directive, 21, 10)
|
|
3576
|
+
|
|
3577
|
+
for token in data.tokenlist:
|
|
3578
|
+
if (token.str == 'wcsftime') and token.next and token.next.str == '(':
|
|
3579
|
+
self.reportError(token, 21, 10)
|
|
3580
|
+
|
|
3581
|
+
def misra_21_11(self, data):
|
|
3582
|
+
directive = findInclude(data.directives, '<tgmath.h>')
|
|
3583
|
+
if directive:
|
|
3584
|
+
self.reportError(directive, 21, 11)
|
|
3585
|
+
|
|
3586
|
+
def misra_21_12(self, data):
|
|
3587
|
+
if findInclude(data.directives, '<fenv.h>'):
|
|
3588
|
+
for token in data.tokenlist:
|
|
3589
|
+
if token.str == 'fexcept_t' and token.isName:
|
|
3590
|
+
self.reportError(token, 21, 12)
|
|
3591
|
+
if isFunctionCall(token) and (token.astOperand1.str in (
|
|
3592
|
+
'feclearexcept',
|
|
3593
|
+
'fegetexceptflag',
|
|
3594
|
+
'feraiseexcept',
|
|
3595
|
+
'fesetexceptflag',
|
|
3596
|
+
'fetestexcept')):
|
|
3597
|
+
self.reportError(token, 21, 12)
|
|
3598
|
+
|
|
3599
|
+
def misra_21_14(self, data):
|
|
3600
|
+
# buffers used in strcpy/strlen/etc function calls
|
|
3601
|
+
string_buffers = []
|
|
3602
|
+
for token in data.tokenlist:
|
|
3603
|
+
if token.str[0] == 's' and isFunctionCall(token.next):
|
|
3604
|
+
name, args = cppcheckdata.get_function_call_name_args(token)
|
|
3605
|
+
if name is None:
|
|
3606
|
+
continue
|
|
3607
|
+
def _get_string_buffers(match, args, argnum):
|
|
3608
|
+
if not match:
|
|
3609
|
+
return []
|
|
3610
|
+
ret = []
|
|
3611
|
+
for a in argnum:
|
|
3612
|
+
if a < len(args):
|
|
3613
|
+
arg = args[a]
|
|
3614
|
+
while arg and arg.str in ('.', '::'):
|
|
3615
|
+
arg = arg.astOperand2
|
|
3616
|
+
if arg and arg.varId != 0 and arg.varId not in ret:
|
|
3617
|
+
ret.append(arg.varId)
|
|
3618
|
+
return ret
|
|
3619
|
+
string_buffers += _get_string_buffers(name == 'strcpy', args, [0, 1])
|
|
3620
|
+
string_buffers += _get_string_buffers(name == 'strncpy', args, [0, 1])
|
|
3621
|
+
string_buffers += _get_string_buffers(name == 'strlen', args, [0])
|
|
3622
|
+
string_buffers += _get_string_buffers(name == 'strcmp', args, [0, 1])
|
|
3623
|
+
string_buffers += _get_string_buffers(name == 'sprintf', args, [0])
|
|
3624
|
+
string_buffers += _get_string_buffers(name == 'snprintf', args, [0, 3])
|
|
3625
|
+
|
|
3626
|
+
for token in data.tokenlist:
|
|
3627
|
+
if token.str != 'memcmp':
|
|
3628
|
+
continue
|
|
3629
|
+
name, args = cppcheckdata.get_function_call_name_args(token)
|
|
3630
|
+
if name is None:
|
|
3631
|
+
continue
|
|
3632
|
+
if len(args) != 3:
|
|
3633
|
+
continue
|
|
3634
|
+
for arg in args[:2]:
|
|
3635
|
+
if arg.str[-1] == '\"':
|
|
3636
|
+
self.reportError(arg, 21, 14)
|
|
3637
|
+
continue
|
|
3638
|
+
while arg and arg.str in ('.', '::'):
|
|
3639
|
+
arg = arg.astOperand2
|
|
3640
|
+
if arg and arg.varId and arg.varId in string_buffers:
|
|
3641
|
+
self.reportError(arg, 21, 14)
|
|
3642
|
+
|
|
3643
|
+
def misra_21_15(self, data):
|
|
3644
|
+
for token in data.tokenlist:
|
|
3645
|
+
if token.str not in ('memcpy', 'memmove', 'memcmp'):
|
|
3646
|
+
continue
|
|
3647
|
+
name, args = cppcheckdata.get_function_call_name_args(token)
|
|
3648
|
+
if name is None:
|
|
3649
|
+
continue
|
|
3650
|
+
if len(args) != 3:
|
|
3651
|
+
continue
|
|
3652
|
+
if args[0].valueType is None or args[1].valueType is None:
|
|
3653
|
+
continue
|
|
3654
|
+
if args[0].valueType.type == args[1].valueType.type:
|
|
3655
|
+
continue
|
|
3656
|
+
if args[0].valueType.type == 'void' or args[1].valueType.type == 'void':
|
|
3657
|
+
continue
|
|
3658
|
+
self.reportError(token, 21, 15)
|
|
3659
|
+
|
|
3660
|
+
def misra_21_16(self, cfg):
|
|
3661
|
+
for token in cfg.tokenlist:
|
|
3662
|
+
if token.str != 'memcmp':
|
|
3663
|
+
continue
|
|
3664
|
+
name, args = cppcheckdata.get_function_call_name_args(token)
|
|
3665
|
+
if name is None:
|
|
3666
|
+
continue
|
|
3667
|
+
if len(args) != 3:
|
|
3668
|
+
continue
|
|
3669
|
+
for arg in args[:2]:
|
|
3670
|
+
if arg.valueType is None:
|
|
3671
|
+
continue
|
|
3672
|
+
if arg.valueType.pointer > 1:
|
|
3673
|
+
continue
|
|
3674
|
+
if arg.valueType.sign in ('unsigned', 'signed'):
|
|
3675
|
+
continue
|
|
3676
|
+
if arg.valueType.isEnum():
|
|
3677
|
+
continue
|
|
3678
|
+
self.reportError(token, 21, 16)
|
|
3679
|
+
|
|
3680
|
+
def misra_21_19(self, cfg):
|
|
3681
|
+
for token in cfg.tokenlist:
|
|
3682
|
+
if token.str in ('localeconv', 'getenv', 'setlocale', 'strerror') and simpleMatch(token.next, '('):
|
|
3683
|
+
name, _ = cppcheckdata.get_function_call_name_args(token)
|
|
3684
|
+
if name is None or name != token.str:
|
|
3685
|
+
continue
|
|
3686
|
+
parent = token.next
|
|
3687
|
+
while simpleMatch(parent.astParent, '+'):
|
|
3688
|
+
parent = parent.astParent
|
|
3689
|
+
# x = f()
|
|
3690
|
+
if simpleMatch(parent.astParent, '=') and parent == parent.astParent.astOperand2:
|
|
3691
|
+
lhs = parent.astParent.astOperand1
|
|
3692
|
+
if lhs and lhs.valueType and lhs.valueType.pointer > 0 and lhs.valueType.constness == 0:
|
|
3693
|
+
self.reportError(token, 21, 19)
|
|
3694
|
+
if token.str == '=':
|
|
3695
|
+
lhs = token.astOperand1
|
|
3696
|
+
while simpleMatch(lhs, '*') and lhs.astOperand2 is None:
|
|
3697
|
+
lhs = lhs.astOperand1
|
|
3698
|
+
if not simpleMatch(lhs, '.'):
|
|
3699
|
+
continue
|
|
3700
|
+
while simpleMatch(lhs, '.'):
|
|
3701
|
+
lhs = lhs.astOperand1
|
|
3702
|
+
if lhs and lhs.variable and simpleMatch(lhs.variable.typeStartToken, 'lconv'):
|
|
3703
|
+
self.reportError(token, 21, 19)
|
|
3704
|
+
|
|
3705
|
+
def misra_21_20(self, cfg):
|
|
3706
|
+
assigned = {}
|
|
3707
|
+
invalid = []
|
|
3708
|
+
for token in cfg.tokenlist:
|
|
3709
|
+
# No sophisticated data flow analysis, bail out if control flow is "interrupted"
|
|
3710
|
+
if token.str in ('{', '}', 'break', 'continue', 'return'):
|
|
3711
|
+
assigned = {}
|
|
3712
|
+
invalid = []
|
|
3713
|
+
continue
|
|
3714
|
+
|
|
3715
|
+
# When pointer is assigned, remove it from 'assigned' and 'invalid'
|
|
3716
|
+
if token.varId and token.varId > 0 and simpleMatch(token.next, '='):
|
|
3717
|
+
for name in assigned.keys():
|
|
3718
|
+
while token.varId in assigned[name]:
|
|
3719
|
+
assigned[name].remove(token.varId)
|
|
3720
|
+
while token.varId in invalid:
|
|
3721
|
+
invalid.remove(token.varId)
|
|
3722
|
+
continue
|
|
3723
|
+
|
|
3724
|
+
# Calling dangerous function
|
|
3725
|
+
if token.str in ('asctime', 'ctime', 'gmtime', 'localtime', 'localeconv', 'getenv', 'setlocale', 'strerror'):
|
|
3726
|
+
name, args = cppcheckdata.get_function_call_name_args(token)
|
|
3727
|
+
if name and name == token.str:
|
|
3728
|
+
# make assigned pointers invalid
|
|
3729
|
+
for varId in assigned.get(name, ()):
|
|
3730
|
+
if varId not in invalid:
|
|
3731
|
+
invalid.append(varId)
|
|
3732
|
+
|
|
3733
|
+
# assign pointer
|
|
3734
|
+
parent = token.next
|
|
3735
|
+
while parent.astParent and (parent.astParent.str == '+' or isCast(parent.astParent)):
|
|
3736
|
+
parent = parent.astParent
|
|
3737
|
+
if simpleMatch(parent.astParent, '='):
|
|
3738
|
+
eq = parent.astParent
|
|
3739
|
+
vartok = eq.previous
|
|
3740
|
+
if vartok and vartok.varId and vartok.varId > 0:
|
|
3741
|
+
if name not in assigned:
|
|
3742
|
+
assigned[name] = [vartok.varId]
|
|
3743
|
+
elif vartok.varId not in assigned[name]:
|
|
3744
|
+
assigned[name].append(vartok.varId)
|
|
3745
|
+
continue
|
|
3746
|
+
|
|
3747
|
+
# taking value of invalid pointer..
|
|
3748
|
+
if token.astParent and token.varId:
|
|
3749
|
+
if token.varId in invalid:
|
|
3750
|
+
self.reportError(token, 21, 20)
|
|
3751
|
+
|
|
3752
|
+
def misra_21_21(self, cfg):
|
|
3753
|
+
for token in cfg.tokenlist:
|
|
3754
|
+
if token.str == 'system':
|
|
3755
|
+
name, args = cppcheckdata.get_function_call_name_args(token)
|
|
3756
|
+
if name == 'system' and len(args) == 1:
|
|
3757
|
+
self.reportError(token, 21, 21)
|
|
3758
|
+
|
|
3759
|
+
def misra_22_5(self, cfg):
|
|
3760
|
+
for token in cfg.tokenlist:
|
|
3761
|
+
if token.isUnaryOp("*") or (token.isBinaryOp() and token.str == '.'):
|
|
3762
|
+
fileptr = token.astOperand1
|
|
3763
|
+
if fileptr.variable and cppcheckdata.simpleMatch(fileptr.variable.typeStartToken, 'FILE *'):
|
|
3764
|
+
self.reportError(token, 22, 5)
|
|
3765
|
+
|
|
3766
|
+
def misra_22_7(self, cfg):
|
|
3767
|
+
for eofToken in cfg.tokenlist:
|
|
3768
|
+
if eofToken.str != 'EOF':
|
|
3769
|
+
continue
|
|
3770
|
+
if eofToken.astParent is None or not eofToken.astParent.isComparisonOp:
|
|
3771
|
+
continue
|
|
3772
|
+
if eofToken.astParent.astOperand1 == eofToken:
|
|
3773
|
+
eofTokenSibling = eofToken.astParent.astOperand2
|
|
3774
|
+
else:
|
|
3775
|
+
eofTokenSibling = eofToken.astParent.astOperand1
|
|
3776
|
+
while isCast(eofTokenSibling) and eofTokenSibling.valueType and eofTokenSibling.valueType.type and eofTokenSibling.valueType.type == 'int':
|
|
3777
|
+
eofTokenSibling = eofTokenSibling.astOperand2 if eofTokenSibling.astOperand2 else eofTokenSibling.astOperand1
|
|
3778
|
+
if eofTokenSibling is not None and eofTokenSibling.valueType and eofTokenSibling.valueType and eofTokenSibling.valueType.type in ('bool', 'char', 'short'):
|
|
3779
|
+
self.reportError(eofToken, 22, 7)
|
|
3780
|
+
|
|
3781
|
+
def misra_22_8(self, cfg):
|
|
3782
|
+
is_zero = False
|
|
3783
|
+
for token in cfg.tokenlist:
|
|
3784
|
+
if simpleMatch(token, 'errno = 0'):
|
|
3785
|
+
is_zero = True
|
|
3786
|
+
if token.str == '(' and not simpleMatch(token.link, ') {'):
|
|
3787
|
+
name, _ = cppcheckdata.get_function_call_name_args(token.previous)
|
|
3788
|
+
if name is None:
|
|
3789
|
+
continue
|
|
3790
|
+
if is_errno_setting_function(name):
|
|
3791
|
+
if not is_zero:
|
|
3792
|
+
self.reportError(token, 22, 8)
|
|
3793
|
+
else:
|
|
3794
|
+
is_zero = False
|
|
3795
|
+
|
|
3796
|
+
def misra_22_9(self, cfg):
|
|
3797
|
+
errno_is_set = False
|
|
3798
|
+
for token in cfg.tokenlist:
|
|
3799
|
+
if token.str == '(' and not simpleMatch(token.link, ') {'):
|
|
3800
|
+
name, args = cppcheckdata.get_function_call_name_args(token.previous)
|
|
3801
|
+
if name is None:
|
|
3802
|
+
continue
|
|
3803
|
+
errno_is_set = is_errno_setting_function(name)
|
|
3804
|
+
if errno_is_set and token.str in '{};':
|
|
3805
|
+
errno_is_set = False
|
|
3806
|
+
tok = token.next
|
|
3807
|
+
while tok and tok.str not in ('{','}',';','errno'):
|
|
3808
|
+
tok = tok.next
|
|
3809
|
+
if tok is None or tok.str != 'errno':
|
|
3810
|
+
self.reportError(token, 22, 9)
|
|
3811
|
+
elif (tok.astParent is None) or (not tok.astParent.isComparisonOp):
|
|
3812
|
+
self.reportError(token, 22, 9)
|
|
3813
|
+
|
|
3814
|
+
def misra_22_10(self, cfg):
|
|
3815
|
+
last_function_call = None
|
|
3816
|
+
for token in cfg.tokenlist:
|
|
3817
|
+
if token.str == '(' and not simpleMatch(token.link, ') {'):
|
|
3818
|
+
name, args = cppcheckdata.get_function_call_name_args(token.previous)
|
|
3819
|
+
last_function_call = name
|
|
3820
|
+
if token.str == '}':
|
|
3821
|
+
last_function_call = None
|
|
3822
|
+
if token.str == 'errno' and token.astParent and token.astParent.isComparisonOp:
|
|
3823
|
+
if last_function_call is None:
|
|
3824
|
+
self.reportError(token, 22, 10)
|
|
3825
|
+
elif not is_errno_setting_function(last_function_call):
|
|
3826
|
+
self.reportError(token, 22, 10)
|
|
3827
|
+
|
|
3828
|
+
|
|
3829
|
+
def get_verify_expected(self):
|
|
3830
|
+
"""Return the list of expected violations in the verify test"""
|
|
3831
|
+
return self.verify_expected
|
|
3832
|
+
|
|
3833
|
+
def get_verify_actual(self):
|
|
3834
|
+
"""Return the list of actual violations in for the verify test"""
|
|
3835
|
+
return self.verify_actual
|
|
3836
|
+
|
|
3837
|
+
def get_violations(self, violation_type=None):
|
|
3838
|
+
"""Return the list of violations for a normal checker run"""
|
|
3839
|
+
if violation_type is None:
|
|
3840
|
+
return self.violations.items()
|
|
3841
|
+
else:
|
|
3842
|
+
return self.violations[violation_type]
|
|
3843
|
+
|
|
3844
|
+
def get_violation_types(self):
|
|
3845
|
+
"""Return the list of violations for a normal checker run"""
|
|
3846
|
+
return self.violations.keys()
|
|
3847
|
+
|
|
3848
|
+
def addSuppressedRule(self, ruleNum,
|
|
3849
|
+
fileName=None,
|
|
3850
|
+
lineNumber=None,
|
|
3851
|
+
symbolName=None):
|
|
3852
|
+
"""
|
|
3853
|
+
Add a suppression to the suppressions data structure
|
|
3854
|
+
|
|
3855
|
+
Suppressions are stored in a dictionary of dictionaries that
|
|
3856
|
+
contains a list of tuples.
|
|
3857
|
+
|
|
3858
|
+
The first dictionary is keyed by the MISRA rule in hundreds
|
|
3859
|
+
format. The value of that dictionary is a dictionary of filenames.
|
|
3860
|
+
If the value is None then the rule is assumed to be suppressed for
|
|
3861
|
+
all files.
|
|
3862
|
+
If the filename exists then the value of that dictionary contains a list
|
|
3863
|
+
with the scope of the suppression. If the list contains an item of None
|
|
3864
|
+
then the rule is assumed to be suppressed for the entire file. Otherwise
|
|
3865
|
+
the list contains line number, symbol name tuples.
|
|
3866
|
+
For each tuple either line number or symbol name can can be none.
|
|
3867
|
+
|
|
3868
|
+
"""
|
|
3869
|
+
normalized_filename = None
|
|
3870
|
+
|
|
3871
|
+
if fileName is not None:
|
|
3872
|
+
normalized_filename = os.path.expanduser(fileName)
|
|
3873
|
+
normalized_filename = os.path.normpath(normalized_filename)
|
|
3874
|
+
|
|
3875
|
+
if lineNumber is not None or symbolName is not None:
|
|
3876
|
+
line_symbol = (lineNumber, symbolName)
|
|
3877
|
+
else:
|
|
3878
|
+
line_symbol = None
|
|
3879
|
+
|
|
3880
|
+
# If the rule is not in the dict already then add it
|
|
3881
|
+
if ruleNum not in self.suppressedRules:
|
|
3882
|
+
ruleItemList = list()
|
|
3883
|
+
ruleItemList.append(line_symbol)
|
|
3884
|
+
|
|
3885
|
+
fileDict = dict()
|
|
3886
|
+
fileDict[normalized_filename] = ruleItemList
|
|
3887
|
+
|
|
3888
|
+
self.suppressedRules[ruleNum] = fileDict
|
|
3889
|
+
|
|
3890
|
+
# Rule is added. Done.
|
|
3891
|
+
return
|
|
3892
|
+
|
|
3893
|
+
# Rule existed in the dictionary. Check for
|
|
3894
|
+
# filename entries.
|
|
3895
|
+
|
|
3896
|
+
# Get the dictionary for the rule number
|
|
3897
|
+
fileDict = self.suppressedRules[ruleNum]
|
|
3898
|
+
|
|
3899
|
+
# If the filename is not in the dict already add it
|
|
3900
|
+
if normalized_filename not in fileDict:
|
|
3901
|
+
ruleItemList = list()
|
|
3902
|
+
ruleItemList.append(line_symbol)
|
|
3903
|
+
|
|
3904
|
+
fileDict[normalized_filename] = ruleItemList
|
|
3905
|
+
|
|
3906
|
+
# Rule is added with a file scope. Done
|
|
3907
|
+
return
|
|
3908
|
+
|
|
3909
|
+
# Rule has a matching filename. Get the rule item list.
|
|
3910
|
+
|
|
3911
|
+
# Check the lists of rule items
|
|
3912
|
+
# to see if this (lineNumber, symbolName) combination
|
|
3913
|
+
# or None already exists.
|
|
3914
|
+
ruleItemList = fileDict[normalized_filename]
|
|
3915
|
+
|
|
3916
|
+
if line_symbol is None:
|
|
3917
|
+
# is it already in the list?
|
|
3918
|
+
if line_symbol not in ruleItemList:
|
|
3919
|
+
ruleItemList.append(line_symbol)
|
|
3920
|
+
else:
|
|
3921
|
+
# Check the list looking for matches
|
|
3922
|
+
matched = False
|
|
3923
|
+
for each in ruleItemList:
|
|
3924
|
+
if each is not None:
|
|
3925
|
+
if (each[0] == line_symbol[0]) and (each[1] == line_symbol[1]):
|
|
3926
|
+
matched = True
|
|
3927
|
+
|
|
3928
|
+
# Append the rule item if it was not already found
|
|
3929
|
+
if not matched:
|
|
3930
|
+
ruleItemList.append(line_symbol)
|
|
3931
|
+
|
|
3932
|
+
def isRuleSuppressed(self, file_path, linenr, ruleNum):
|
|
3933
|
+
"""
|
|
3934
|
+
Check to see if a rule is suppressed.
|
|
3935
|
+
|
|
3936
|
+
:param ruleNum: is the rule number in hundreds format
|
|
3937
|
+
:param file_path: File path of checked location
|
|
3938
|
+
:param linenr: Line number of checked location
|
|
3939
|
+
|
|
3940
|
+
If the rule exists in the dict then check for a filename
|
|
3941
|
+
If the filename is None then rule is suppressed globally
|
|
3942
|
+
for all files.
|
|
3943
|
+
If the filename exists then look for list of
|
|
3944
|
+
line number, symbol name tuples. If the list is None then
|
|
3945
|
+
the rule is suppressed for the entire file
|
|
3946
|
+
If the list of tuples exists then search the list looking for
|
|
3947
|
+
matching line numbers. Symbol names are currently ignored
|
|
3948
|
+
because they can include regular expressions.
|
|
3949
|
+
TODO: Support symbol names and expression matching.
|
|
3950
|
+
|
|
3951
|
+
"""
|
|
3952
|
+
ruleIsSuppressed = False
|
|
3953
|
+
|
|
3954
|
+
# Remove any prefix listed in command arguments from the filename.
|
|
3955
|
+
filename = None
|
|
3956
|
+
if file_path is not None:
|
|
3957
|
+
if self.filePrefix is not None:
|
|
3958
|
+
filename = remove_file_prefix(file_path, self.filePrefix)
|
|
3959
|
+
else:
|
|
3960
|
+
filename = os.path.basename(file_path)
|
|
3961
|
+
|
|
3962
|
+
if ruleNum in self.suppressedRules:
|
|
3963
|
+
fileDict = self.suppressedRules[ruleNum]
|
|
3964
|
+
|
|
3965
|
+
# a file name entry of None means that the rule is suppressed
|
|
3966
|
+
# globally
|
|
3967
|
+
if None in fileDict:
|
|
3968
|
+
ruleIsSuppressed = True
|
|
3969
|
+
else:
|
|
3970
|
+
# Does the filename match one of the names in
|
|
3971
|
+
# the file list
|
|
3972
|
+
if filename in fileDict:
|
|
3973
|
+
# Get the list of ruleItems
|
|
3974
|
+
ruleItemList = fileDict[filename]
|
|
3975
|
+
|
|
3976
|
+
if None in ruleItemList:
|
|
3977
|
+
# Entry of None in the ruleItemList means the rule is
|
|
3978
|
+
# suppressed for all lines in the filename
|
|
3979
|
+
ruleIsSuppressed = True
|
|
3980
|
+
else:
|
|
3981
|
+
# Iterate though the the list of line numbers
|
|
3982
|
+
# and symbols looking for a match of the line
|
|
3983
|
+
# number. Matching the symbol is a TODO:
|
|
3984
|
+
for each in ruleItemList:
|
|
3985
|
+
if each is not None:
|
|
3986
|
+
if each[0] == linenr:
|
|
3987
|
+
ruleIsSuppressed = True
|
|
3988
|
+
|
|
3989
|
+
return ruleIsSuppressed
|
|
3990
|
+
|
|
3991
|
+
def isRuleGloballySuppressed(self, rule_num):
|
|
3992
|
+
"""
|
|
3993
|
+
Check to see if a rule is globally suppressed.
|
|
3994
|
+
:param rule_num: is the rule number in hundreds format
|
|
3995
|
+
"""
|
|
3996
|
+
if rule_num not in self.suppressedRules:
|
|
3997
|
+
return False
|
|
3998
|
+
return None in self.suppressedRules[rule_num]
|
|
3999
|
+
|
|
4000
|
+
def showSuppressedRules(self):
|
|
4001
|
+
"""
|
|
4002
|
+
Print out rules in suppression list sorted by Rule Number
|
|
4003
|
+
"""
|
|
4004
|
+
print("Suppressed Rules List:")
|
|
4005
|
+
outlist = list()
|
|
4006
|
+
|
|
4007
|
+
for ruleNum in self.suppressedRules:
|
|
4008
|
+
fileDict = self.suppressedRules[ruleNum]
|
|
4009
|
+
|
|
4010
|
+
for fname in fileDict:
|
|
4011
|
+
ruleItemList = fileDict[fname]
|
|
4012
|
+
|
|
4013
|
+
for item in ruleItemList:
|
|
4014
|
+
if item is None:
|
|
4015
|
+
item_str = "None"
|
|
4016
|
+
else:
|
|
4017
|
+
item_str = str(item[0])
|
|
4018
|
+
|
|
4019
|
+
outlist.append("%s: %s: %s (%d locations suppressed)" % (
|
|
4020
|
+
float(ruleNum) / 100, fname, item_str, self.suppressionStats.get(ruleNum, 0)))
|
|
4021
|
+
|
|
4022
|
+
for line in sorted(outlist, reverse=True):
|
|
4023
|
+
print(" %s" % line)
|
|
4024
|
+
|
|
4025
|
+
def setFilePrefix(self, prefix):
|
|
4026
|
+
"""
|
|
4027
|
+
Set the file prefix to ignore from files when matching
|
|
4028
|
+
suppression files
|
|
4029
|
+
"""
|
|
4030
|
+
self.filePrefix = prefix
|
|
4031
|
+
|
|
4032
|
+
def setSeverity(self, severity):
|
|
4033
|
+
"""
|
|
4034
|
+
Set the severity for all errors.
|
|
4035
|
+
"""
|
|
4036
|
+
self.severity = severity
|
|
4037
|
+
|
|
4038
|
+
def setSuppressionList(self, suppressionlist):
|
|
4039
|
+
num1 = 0
|
|
4040
|
+
num2 = 0
|
|
4041
|
+
rule_pattern = re.compile(r'([0-9]+).([0-9]+)')
|
|
4042
|
+
strlist = suppressionlist.split(",")
|
|
4043
|
+
|
|
4044
|
+
# build ignore list
|
|
4045
|
+
for item in strlist:
|
|
4046
|
+
res = rule_pattern.match(item)
|
|
4047
|
+
if res:
|
|
4048
|
+
num1 = int(res.group(1))
|
|
4049
|
+
num2 = int(res.group(2))
|
|
4050
|
+
ruleNum = (num1 * 100) + num2
|
|
4051
|
+
|
|
4052
|
+
self.addSuppressedRule(ruleNum)
|
|
4053
|
+
|
|
4054
|
+
def reportError(self, location, num1, num2):
|
|
4055
|
+
ruleNum = num1 * 100 + num2
|
|
4056
|
+
|
|
4057
|
+
if self.isRuleGloballySuppressed(ruleNum):
|
|
4058
|
+
return
|
|
4059
|
+
|
|
4060
|
+
if self.settings.verify:
|
|
4061
|
+
self.verify_actual.append('%s:%d %d.%d' % (location.file, location.linenr, num1, num2))
|
|
4062
|
+
elif self.isRuleSuppressed(location.file, location.linenr, ruleNum):
|
|
4063
|
+
# Error is suppressed. Ignore
|
|
4064
|
+
self.suppressionStats.setdefault(ruleNum, 0)
|
|
4065
|
+
self.suppressionStats[ruleNum] += 1
|
|
4066
|
+
return
|
|
4067
|
+
else:
|
|
4068
|
+
errorId = 'c2012-' + str(num1) + '.' + str(num2)
|
|
4069
|
+
misra_severity = 'Undefined'
|
|
4070
|
+
cppcheck_severity = 'style'
|
|
4071
|
+
if ruleNum in self.ruleTexts:
|
|
4072
|
+
errmsg = self.ruleTexts[ruleNum].text
|
|
4073
|
+
if self.ruleTexts[ruleNum].misra_severity:
|
|
4074
|
+
misra_severity = self.ruleTexts[ruleNum].misra_severity
|
|
4075
|
+
cppcheck_severity = self.ruleTexts[ruleNum].cppcheck_severity
|
|
4076
|
+
elif len(self.ruleTexts) == 0:
|
|
4077
|
+
errmsg = 'misra violation (use --rule-texts=<file> to get proper output)'
|
|
4078
|
+
if self.path_premium_addon:
|
|
4079
|
+
for line in cppcheckdata.cmd_output([self.path_premium_addon, '--cli', '--get-rule-text=' + errorId]).split('\n'):
|
|
4080
|
+
if len(line) > 1 and not line.startswith('{'):
|
|
4081
|
+
errmsg = line.strip()
|
|
4082
|
+
break
|
|
4083
|
+
else:
|
|
4084
|
+
errmsg = 'misra violation %s with no text in the supplied rule-texts-file' % (ruleNum)
|
|
4085
|
+
|
|
4086
|
+
if self.severity:
|
|
4087
|
+
cppcheck_severity = self.severity
|
|
4088
|
+
|
|
4089
|
+
this_violation = '{}-{}-{}-{}'.format(location.file, location.linenr, location.column, ruleNum)
|
|
4090
|
+
|
|
4091
|
+
# If this is new violation then record it and show it. If not then
|
|
4092
|
+
# skip it since it has already been displayed.
|
|
4093
|
+
if not this_violation in self.existing_violations:
|
|
4094
|
+
self.existing_violations.add(this_violation)
|
|
4095
|
+
cppcheckdata.reportError(location, cppcheck_severity, errmsg, 'misra', errorId, misra_severity)
|
|
4096
|
+
|
|
4097
|
+
if misra_severity not in self.violations:
|
|
4098
|
+
self.violations[misra_severity] = []
|
|
4099
|
+
self.violations[misra_severity].append('misra-' + errorId)
|
|
4100
|
+
|
|
4101
|
+
def loadRuleTexts(self, filename):
|
|
4102
|
+
num1 = 0
|
|
4103
|
+
num2 = 0
|
|
4104
|
+
appendixA = False
|
|
4105
|
+
ruleText = False
|
|
4106
|
+
expect_more = False
|
|
4107
|
+
|
|
4108
|
+
Rule_pattern = re.compile(r'^Rule ([0-9]+).([0-9]+)')
|
|
4109
|
+
severity_pattern = re.compile(r'.*[ ]*(Advisory|Required|Mandatory)$')
|
|
4110
|
+
xA_Z_pattern = re.compile(r'^[#A-Z].*')
|
|
4111
|
+
a_z_pattern = re.compile(r'^[a-z].*')
|
|
4112
|
+
# Try to detect the file encoding
|
|
4113
|
+
file_stream = None
|
|
4114
|
+
encodings = ['ascii', 'utf-8', 'windows-1250', 'windows-1252']
|
|
4115
|
+
for e in encodings:
|
|
4116
|
+
try:
|
|
4117
|
+
file_stream = codecs.open(filename, 'r', encoding=e)
|
|
4118
|
+
file_stream.readlines()
|
|
4119
|
+
file_stream.seek(0)
|
|
4120
|
+
except UnicodeDecodeError:
|
|
4121
|
+
file_stream = None
|
|
4122
|
+
else:
|
|
4123
|
+
break
|
|
4124
|
+
if not file_stream:
|
|
4125
|
+
print('Could not find a suitable codec for "' + filename + '".')
|
|
4126
|
+
print('If you know the codec please report it to the developers so the list can be enhanced.')
|
|
4127
|
+
print('Trying with default codec now and ignoring errors if possible ...')
|
|
4128
|
+
try:
|
|
4129
|
+
file_stream = open(filename, 'rt', errors='ignore')
|
|
4130
|
+
except TypeError:
|
|
4131
|
+
# Python 2 does not support the errors parameter
|
|
4132
|
+
file_stream = open(filename, 'rt')
|
|
4133
|
+
|
|
4134
|
+
rule = None
|
|
4135
|
+
have_severity = False
|
|
4136
|
+
severity_loc = 0
|
|
4137
|
+
|
|
4138
|
+
for line in file_stream:
|
|
4139
|
+
|
|
4140
|
+
line = line.replace('\r', '').replace('\n', '')
|
|
4141
|
+
|
|
4142
|
+
if not appendixA:
|
|
4143
|
+
if line.find('Appendix A') >= 0 and line.find('Summary of guidelines') >= 10:
|
|
4144
|
+
appendixA = True
|
|
4145
|
+
continue
|
|
4146
|
+
if line.find('Appendix B') >= 0:
|
|
4147
|
+
break
|
|
4148
|
+
if len(line) == 0:
|
|
4149
|
+
continue
|
|
4150
|
+
|
|
4151
|
+
# Parse rule declaration.
|
|
4152
|
+
res = Rule_pattern.match(line)
|
|
4153
|
+
|
|
4154
|
+
if res:
|
|
4155
|
+
have_severity = False
|
|
4156
|
+
expect_more = False
|
|
4157
|
+
severity_loc = 0
|
|
4158
|
+
num1 = int(res.group(1))
|
|
4159
|
+
num2 = int(res.group(2))
|
|
4160
|
+
rule = Rule(num1, num2)
|
|
4161
|
+
|
|
4162
|
+
if not have_severity and rule is not None:
|
|
4163
|
+
res = severity_pattern.match(line)
|
|
4164
|
+
|
|
4165
|
+
if res:
|
|
4166
|
+
rule.misra_severity = res.group(1)
|
|
4167
|
+
have_severity = True
|
|
4168
|
+
else:
|
|
4169
|
+
severity_loc += 1
|
|
4170
|
+
|
|
4171
|
+
# Only look for severity on the Rule line
|
|
4172
|
+
# or the next non-blank line after
|
|
4173
|
+
# If it's not in either of those locations then
|
|
4174
|
+
# assume a severity was not provided.
|
|
4175
|
+
|
|
4176
|
+
if severity_loc < 2:
|
|
4177
|
+
continue
|
|
4178
|
+
else:
|
|
4179
|
+
rule.misra_severity = ''
|
|
4180
|
+
have_severity = True
|
|
4181
|
+
|
|
4182
|
+
if rule is None:
|
|
4183
|
+
continue
|
|
4184
|
+
|
|
4185
|
+
# Parse continuing of rule text.
|
|
4186
|
+
if expect_more:
|
|
4187
|
+
if a_z_pattern.match(line):
|
|
4188
|
+
self.ruleTexts[rule.num].text += ' ' + line
|
|
4189
|
+
continue
|
|
4190
|
+
|
|
4191
|
+
expect_more = False
|
|
4192
|
+
continue
|
|
4193
|
+
|
|
4194
|
+
# Parse beginning of rule text.
|
|
4195
|
+
if xA_Z_pattern.match(line):
|
|
4196
|
+
rule.text = line
|
|
4197
|
+
self.ruleTexts[rule.num] = rule
|
|
4198
|
+
expect_more = True
|
|
4199
|
+
|
|
4200
|
+
def verifyRuleTexts(self):
|
|
4201
|
+
"""Prints rule numbers without rule text."""
|
|
4202
|
+
rule_texts_rules = []
|
|
4203
|
+
for rule_num in self.ruleTexts:
|
|
4204
|
+
rule = self.ruleTexts[rule_num]
|
|
4205
|
+
rule_texts_rules.append(str(rule.num1) + '.' + str(rule.num2))
|
|
4206
|
+
|
|
4207
|
+
all_rules = list(getAddonRules() + getCppcheckRules())
|
|
4208
|
+
|
|
4209
|
+
missing_rules = list(set(all_rules) - set(rule_texts_rules))
|
|
4210
|
+
if len(missing_rules) == 0:
|
|
4211
|
+
print("Rule texts are correct.")
|
|
4212
|
+
else:
|
|
4213
|
+
print("Missing rule texts: " + ', '.join(missing_rules))
|
|
4214
|
+
|
|
4215
|
+
def printStatus(self, *args, **kwargs):
|
|
4216
|
+
if not self.settings.quiet:
|
|
4217
|
+
print(*args, **kwargs)
|
|
4218
|
+
|
|
4219
|
+
def executeCheck(self, rule_num, check_function, *args):
|
|
4220
|
+
"""Execute check function for a single MISRA rule.
|
|
4221
|
+
|
|
4222
|
+
:param rule_num: Number of rule in hundreds format
|
|
4223
|
+
:param check_function: Check function to execute
|
|
4224
|
+
:param args: Check function arguments
|
|
4225
|
+
"""
|
|
4226
|
+
if not self.isRuleGloballySuppressed(rule_num):
|
|
4227
|
+
misra_cpp = (
|
|
4228
|
+
202, # misra-c2012-2.3 : misra c++2008 0-1-9
|
|
4229
|
+
203, # misra-c2012-2.3 : misra c++2008 0-1-5
|
|
4230
|
+
402, # misra-c2012-4.2 : misra c++2008 2-3-1
|
|
4231
|
+
701, # misra-c2012-7.1 : misra c++2008 2-3-1
|
|
4232
|
+
702, # misra-c2012-7.2 : misra c++2008 2-13-2
|
|
4233
|
+
1203, # misra-c2012-12.3 : misra c++2008 5-14-1
|
|
4234
|
+
1204, # misra-c2012-12.4 : misra c++2008 5-18-1
|
|
4235
|
+
1305, # misra-c2012-13.5 : misra c++2008 5-19-1
|
|
4236
|
+
1702, # misra-c2012-17.2 : misra c++2008 7-5-4
|
|
4237
|
+
1901) # misra-c2012-19.1 : misra c++2008 2-13-3
|
|
4238
|
+
|
|
4239
|
+
if (not self.is_cpp) or rule_num in misra_cpp:
|
|
4240
|
+
check_function(*args)
|
|
4241
|
+
|
|
4242
|
+
def parseDump(self, dumpfile):
|
|
4243
|
+
def fillVerifyExpected(verify_expected, tok):
|
|
4244
|
+
"""Add expected suppressions to verify_expected list."""
|
|
4245
|
+
rule_re = re.compile(r'[0-9]+\.[0-9]+')
|
|
4246
|
+
if tok.str.startswith('//') and 'TODO' not in tok.str:
|
|
4247
|
+
for word in tok.str[2:].split(' '):
|
|
4248
|
+
if rule_re.match(word):
|
|
4249
|
+
verify_expected.append('%s:%d %s' % (tok.file, tok.linenr, word))
|
|
4250
|
+
|
|
4251
|
+
data = cppcheckdata.parsedump(dumpfile)
|
|
4252
|
+
|
|
4253
|
+
typeBits['CHAR'] = data.platform.char_bit
|
|
4254
|
+
typeBits['SHORT'] = data.platform.short_bit
|
|
4255
|
+
typeBits['INT'] = data.platform.int_bit
|
|
4256
|
+
typeBits['LONG'] = data.platform.long_bit
|
|
4257
|
+
typeBits['LONG_LONG'] = data.platform.long_long_bit
|
|
4258
|
+
typeBits['POINTER'] = data.platform.pointer_bit
|
|
4259
|
+
|
|
4260
|
+
if self.settings.verify:
|
|
4261
|
+
# Add suppressions from the current file
|
|
4262
|
+
for tok in data.rawTokens:
|
|
4263
|
+
fillVerifyExpected(self.verify_expected, tok)
|
|
4264
|
+
# Add suppressions from the included headers
|
|
4265
|
+
include_re = re.compile(r'^#include [<"]([a-zA-Z0-9]+[a-zA-Z\-_./\\0-9]*)[">]$')
|
|
4266
|
+
dump_dir = os.path.dirname(data.filename)
|
|
4267
|
+
for conf in data.configurations:
|
|
4268
|
+
for directive in conf.directives:
|
|
4269
|
+
m = re.match(include_re, directive.str)
|
|
4270
|
+
if not m:
|
|
4271
|
+
continue
|
|
4272
|
+
header_dump_path = os.path.join(dump_dir, m.group(1) + '.dump')
|
|
4273
|
+
if not os.path.exists(header_dump_path):
|
|
4274
|
+
continue
|
|
4275
|
+
header_data = cppcheckdata.parsedump(header_dump_path)
|
|
4276
|
+
for tok in header_data.rawTokens:
|
|
4277
|
+
fillVerifyExpected(self.verify_expected, tok)
|
|
4278
|
+
else:
|
|
4279
|
+
self.printStatus('Checking ' + dumpfile + '...')
|
|
4280
|
+
|
|
4281
|
+
self.is_cpp = data.files and data.files[0].endswith('.cpp')
|
|
4282
|
+
|
|
4283
|
+
for cfgNumber, cfg in enumerate(data.iterconfigurations()):
|
|
4284
|
+
if not self.settings.quiet:
|
|
4285
|
+
self.printStatus('Checking %s, config %s...' % (dumpfile, cfg.name))
|
|
4286
|
+
|
|
4287
|
+
self.executeCheck(104, self.misra_1_4, cfg)
|
|
4288
|
+
self.executeCheck(202, self.misra_2_2, cfg)
|
|
4289
|
+
self.executeCheck(203, self.misra_2_3, dumpfile, cfg.typedefInfo)
|
|
4290
|
+
self.executeCheck(204, self.misra_2_4, dumpfile, cfg)
|
|
4291
|
+
self.executeCheck(205, self.misra_2_5, dumpfile, cfg)
|
|
4292
|
+
self.executeCheck(207, self.misra_2_7, cfg)
|
|
4293
|
+
# data.rawTokens is same for all configurations
|
|
4294
|
+
if cfgNumber == 0:
|
|
4295
|
+
self.executeCheck(301, self.misra_3_1, data.rawTokens)
|
|
4296
|
+
self.executeCheck(302, self.misra_3_2, data.rawTokens)
|
|
4297
|
+
self.executeCheck(401, self.misra_4_1, data.rawTokens)
|
|
4298
|
+
self.executeCheck(402, self.misra_4_2, data.rawTokens)
|
|
4299
|
+
self.executeCheck(501, self.misra_5_1, cfg)
|
|
4300
|
+
self.executeCheck(502, self.misra_5_2, cfg)
|
|
4301
|
+
self.executeCheck(504, self.misra_5_4, cfg)
|
|
4302
|
+
self.executeCheck(505, self.misra_5_5, cfg)
|
|
4303
|
+
self.executeCheck(506, self.misra_5_6, dumpfile, cfg.typedefInfo)
|
|
4304
|
+
self.executeCheck(507, self.misra_5_7, dumpfile, cfg)
|
|
4305
|
+
self.executeCheck(508, self.misra_5_8, dumpfile, cfg)
|
|
4306
|
+
self.executeCheck(509, self.misra_5_9, dumpfile, cfg)
|
|
4307
|
+
self.executeCheck(601, self.misra_6_1, cfg)
|
|
4308
|
+
self.executeCheck(602, self.misra_6_2, cfg)
|
|
4309
|
+
if cfgNumber == 0:
|
|
4310
|
+
self.executeCheck(701, self.misra_7_1, data.rawTokens)
|
|
4311
|
+
self.executeCheck(702, self.misra_7_2, cfg)
|
|
4312
|
+
if cfgNumber == 0:
|
|
4313
|
+
self.executeCheck(703, self.misra_7_3, data.rawTokens)
|
|
4314
|
+
self.executeCheck(704, self.misra_7_4, cfg)
|
|
4315
|
+
self.executeCheck(801, self.misra_8_1, cfg)
|
|
4316
|
+
if cfgNumber == 0:
|
|
4317
|
+
self.executeCheck(802, self.misra_8_2, cfg, data.rawTokens)
|
|
4318
|
+
self.executeCheck(804, self.misra_8_4, cfg)
|
|
4319
|
+
self.executeCheck(805, self.misra_8_5, dumpfile, cfg)
|
|
4320
|
+
self.executeCheck(806, self.misra_8_6, dumpfile, cfg)
|
|
4321
|
+
self.executeCheck(807, self.misra_8_7, dumpfile, cfg)
|
|
4322
|
+
self.executeCheck(808, self.misra_8_8, cfg)
|
|
4323
|
+
self.executeCheck(809, self.misra_8_9, cfg)
|
|
4324
|
+
self.executeCheck(810, self.misra_8_10, cfg)
|
|
4325
|
+
self.executeCheck(811, self.misra_8_11, cfg)
|
|
4326
|
+
self.executeCheck(812, self.misra_8_12, cfg)
|
|
4327
|
+
if cfgNumber == 0:
|
|
4328
|
+
self.executeCheck(814, self.misra_8_14, data.rawTokens)
|
|
4329
|
+
self.executeCheck(902, self.misra_9_2, cfg)
|
|
4330
|
+
self.executeCheck(903, self.misra_9_3, cfg)
|
|
4331
|
+
self.executeCheck(904, self.misra_9_4, cfg)
|
|
4332
|
+
if cfgNumber == 0:
|
|
4333
|
+
self.executeCheck(905, self.misra_9_5, cfg, data.rawTokens)
|
|
4334
|
+
self.executeCheck(1001, self.misra_10_1, cfg)
|
|
4335
|
+
self.executeCheck(1002, self.misra_10_2, cfg)
|
|
4336
|
+
self.executeCheck(1003, self.misra_10_3, cfg)
|
|
4337
|
+
self.executeCheck(1004, self.misra_10_4, cfg)
|
|
4338
|
+
self.executeCheck(1005, self.misra_10_5, cfg)
|
|
4339
|
+
self.executeCheck(1006, self.misra_10_6, cfg)
|
|
4340
|
+
self.executeCheck(1007, self.misra_10_7, cfg)
|
|
4341
|
+
self.executeCheck(1008, self.misra_10_8, cfg)
|
|
4342
|
+
self.executeCheck(1101, self.misra_11_1, cfg)
|
|
4343
|
+
self.executeCheck(1102, self.misra_11_2, cfg)
|
|
4344
|
+
self.executeCheck(1103, self.misra_11_3, cfg)
|
|
4345
|
+
self.executeCheck(1104, self.misra_11_4, cfg)
|
|
4346
|
+
self.executeCheck(1105, self.misra_11_5, cfg)
|
|
4347
|
+
self.executeCheck(1106, self.misra_11_6, cfg)
|
|
4348
|
+
self.executeCheck(1107, self.misra_11_7, cfg)
|
|
4349
|
+
self.executeCheck(1108, self.misra_11_8, cfg)
|
|
4350
|
+
self.executeCheck(1109, self.misra_11_9, cfg)
|
|
4351
|
+
if cfgNumber == 0:
|
|
4352
|
+
self.executeCheck(1201, self.misra_12_1_sizeof, data.rawTokens)
|
|
4353
|
+
self.executeCheck(1201, self.misra_12_1, cfg)
|
|
4354
|
+
self.executeCheck(1202, self.misra_12_2, cfg)
|
|
4355
|
+
self.executeCheck(1203, self.misra_12_3, cfg)
|
|
4356
|
+
self.executeCheck(1204, self.misra_12_4, cfg)
|
|
4357
|
+
self.executeCheck(1301, self.misra_13_1, cfg)
|
|
4358
|
+
self.executeCheck(1303, self.misra_13_3, cfg)
|
|
4359
|
+
self.executeCheck(1304, self.misra_13_4, cfg)
|
|
4360
|
+
self.executeCheck(1305, self.misra_13_5, cfg)
|
|
4361
|
+
self.executeCheck(1306, self.misra_13_6, cfg)
|
|
4362
|
+
self.executeCheck(1401, self.misra_14_1, cfg)
|
|
4363
|
+
self.executeCheck(1402, self.misra_14_2, cfg)
|
|
4364
|
+
self.executeCheck(1404, self.misra_14_4, cfg)
|
|
4365
|
+
self.executeCheck(1501, self.misra_15_1, cfg)
|
|
4366
|
+
self.executeCheck(1502, self.misra_15_2, cfg)
|
|
4367
|
+
self.executeCheck(1503, self.misra_15_3, cfg)
|
|
4368
|
+
self.executeCheck(1504, self.misra_15_4, cfg)
|
|
4369
|
+
self.executeCheck(1505, self.misra_15_5, cfg)
|
|
4370
|
+
if cfgNumber == 0:
|
|
4371
|
+
self.executeCheck(1506, self.misra_15_6, data.rawTokens)
|
|
4372
|
+
self.executeCheck(1507, self.misra_15_7, cfg)
|
|
4373
|
+
self.executeCheck(1601, self.misra_16_1, cfg)
|
|
4374
|
+
self.executeCheck(1602, self.misra_16_2, cfg)
|
|
4375
|
+
if cfgNumber == 0:
|
|
4376
|
+
self.executeCheck(1603, self.misra_16_3, data.rawTokens)
|
|
4377
|
+
self.executeCheck(1604, self.misra_16_4, cfg)
|
|
4378
|
+
self.executeCheck(1605, self.misra_16_5, cfg)
|
|
4379
|
+
self.executeCheck(1606, self.misra_16_6, cfg)
|
|
4380
|
+
self.executeCheck(1607, self.misra_16_7, cfg)
|
|
4381
|
+
self.executeCheck(1701, self.misra_17_1, cfg)
|
|
4382
|
+
self.executeCheck(1702, self.misra_17_2, cfg)
|
|
4383
|
+
self.executeCheck(1703, self.misra_17_3, cfg)
|
|
4384
|
+
if cfgNumber == 0:
|
|
4385
|
+
self.executeCheck(1706, self.misra_17_6, data.rawTokens)
|
|
4386
|
+
self.executeCheck(1707, self.misra_17_7, cfg)
|
|
4387
|
+
self.executeCheck(1708, self.misra_17_8, cfg)
|
|
4388
|
+
self.executeCheck(1804, self.misra_18_4, cfg)
|
|
4389
|
+
self.executeCheck(1805, self.misra_18_5, cfg)
|
|
4390
|
+
self.executeCheck(1807, self.misra_18_7, cfg)
|
|
4391
|
+
self.executeCheck(1808, self.misra_18_8, cfg)
|
|
4392
|
+
self.executeCheck(1902, self.misra_19_2, cfg)
|
|
4393
|
+
self.executeCheck(2001, self.misra_20_1, cfg)
|
|
4394
|
+
self.executeCheck(2002, self.misra_20_2, cfg)
|
|
4395
|
+
self.executeCheck(2003, self.misra_20_3, cfg)
|
|
4396
|
+
self.executeCheck(2004, self.misra_20_4, cfg)
|
|
4397
|
+
self.executeCheck(2005, self.misra_20_5, cfg)
|
|
4398
|
+
self.executeCheck(2007, self.misra_20_7, cfg)
|
|
4399
|
+
self.executeCheck(2008, self.misra_20_8, cfg)
|
|
4400
|
+
self.executeCheck(2009, self.misra_20_9, cfg)
|
|
4401
|
+
self.executeCheck(2010, self.misra_20_10, cfg)
|
|
4402
|
+
self.executeCheck(2011, self.misra_20_11, cfg)
|
|
4403
|
+
self.executeCheck(2012, self.misra_20_12, cfg)
|
|
4404
|
+
self.executeCheck(2013, self.misra_20_13, cfg)
|
|
4405
|
+
self.executeCheck(2014, self.misra_20_14, cfg)
|
|
4406
|
+
self.executeCheck(2101, self.misra_21_1, cfg)
|
|
4407
|
+
self.executeCheck(2102, self.misra_21_2, cfg)
|
|
4408
|
+
self.executeCheck(2103, self.misra_21_3, cfg)
|
|
4409
|
+
self.executeCheck(2104, self.misra_21_4, cfg)
|
|
4410
|
+
self.executeCheck(2105, self.misra_21_5, cfg)
|
|
4411
|
+
self.executeCheck(2106, self.misra_21_6, cfg)
|
|
4412
|
+
self.executeCheck(2107, self.misra_21_7, cfg)
|
|
4413
|
+
self.executeCheck(2108, self.misra_21_8, cfg)
|
|
4414
|
+
self.executeCheck(2109, self.misra_21_9, cfg)
|
|
4415
|
+
self.executeCheck(2110, self.misra_21_10, cfg)
|
|
4416
|
+
self.executeCheck(2111, self.misra_21_11, cfg)
|
|
4417
|
+
self.executeCheck(2112, self.misra_21_12, cfg)
|
|
4418
|
+
self.executeCheck(2114, self.misra_21_14, cfg)
|
|
4419
|
+
self.executeCheck(2115, self.misra_21_15, cfg)
|
|
4420
|
+
self.executeCheck(2116, self.misra_21_16, cfg)
|
|
4421
|
+
self.executeCheck(2119, self.misra_21_19, cfg)
|
|
4422
|
+
self.executeCheck(2120, self.misra_21_20, cfg)
|
|
4423
|
+
self.executeCheck(2121, self.misra_21_21, cfg)
|
|
4424
|
+
# 22.4 is already covered by Cppcheck writeReadOnlyFile
|
|
4425
|
+
self.executeCheck(2205, self.misra_22_5, cfg)
|
|
4426
|
+
self.executeCheck(2207, self.misra_22_7, cfg)
|
|
4427
|
+
self.executeCheck(2208, self.misra_22_8, cfg)
|
|
4428
|
+
self.executeCheck(2209, self.misra_22_9, cfg)
|
|
4429
|
+
self.executeCheck(2210, self.misra_22_10, cfg)
|
|
4430
|
+
|
|
4431
|
+
def analyse_ctu_info(self, ctu_info_files):
|
|
4432
|
+
all_typedef_info = []
|
|
4433
|
+
all_tagname_info = []
|
|
4434
|
+
all_macro_info = []
|
|
4435
|
+
all_external_identifiers_decl = {}
|
|
4436
|
+
all_external_identifiers_def = {}
|
|
4437
|
+
all_internal_identifiers = {}
|
|
4438
|
+
all_local_identifiers = {}
|
|
4439
|
+
all_usage_files = {}
|
|
4440
|
+
|
|
4441
|
+
from cppcheckdata import Location
|
|
4442
|
+
|
|
4443
|
+
def is_different_location(loc1, loc2):
|
|
4444
|
+
return loc1['file'] != loc2['file'] or loc1['line'] != loc2['line']
|
|
4445
|
+
|
|
4446
|
+
try:
|
|
4447
|
+
for filename in ctu_info_files:
|
|
4448
|
+
for line in open(filename, 'rt'):
|
|
4449
|
+
if not line.startswith('{'):
|
|
4450
|
+
continue
|
|
4451
|
+
|
|
4452
|
+
s = json.loads(line)
|
|
4453
|
+
summary_type = s['summary']
|
|
4454
|
+
summary_data = s['data']
|
|
4455
|
+
|
|
4456
|
+
if summary_type == 'MisraTypedefInfo':
|
|
4457
|
+
for new_typedef_info in summary_data:
|
|
4458
|
+
found = False
|
|
4459
|
+
for old_typedef_info in all_typedef_info:
|
|
4460
|
+
if old_typedef_info['name'] == new_typedef_info['name']:
|
|
4461
|
+
found = True
|
|
4462
|
+
if is_different_location(old_typedef_info, new_typedef_info):
|
|
4463
|
+
self.reportError(Location(old_typedef_info), 5, 6)
|
|
4464
|
+
self.reportError(Location(new_typedef_info), 5, 6)
|
|
4465
|
+
else:
|
|
4466
|
+
if new_typedef_info['used']:
|
|
4467
|
+
old_typedef_info['used'] = True
|
|
4468
|
+
break
|
|
4469
|
+
if not found:
|
|
4470
|
+
all_typedef_info.append(new_typedef_info)
|
|
4471
|
+
|
|
4472
|
+
if summary_type == 'MisraTagName':
|
|
4473
|
+
for new_tagname_info in summary_data:
|
|
4474
|
+
found = False
|
|
4475
|
+
for old_tagname_info in all_tagname_info:
|
|
4476
|
+
if old_tagname_info['name'] == new_tagname_info['name']:
|
|
4477
|
+
found = True
|
|
4478
|
+
if is_different_location(old_tagname_info, new_tagname_info):
|
|
4479
|
+
self.reportError(Location(old_tagname_info), 5, 7)
|
|
4480
|
+
self.reportError(Location(new_tagname_info), 5, 7)
|
|
4481
|
+
else:
|
|
4482
|
+
if new_tagname_info['used']:
|
|
4483
|
+
old_tagname_info['used'] = True
|
|
4484
|
+
break
|
|
4485
|
+
if not found:
|
|
4486
|
+
all_tagname_info.append(new_tagname_info)
|
|
4487
|
+
|
|
4488
|
+
if summary_type == 'MisraMacro':
|
|
4489
|
+
for new_macro in summary_data:
|
|
4490
|
+
found = False
|
|
4491
|
+
for old_macro in all_macro_info:
|
|
4492
|
+
if old_macro['name'] == new_macro['name']:
|
|
4493
|
+
found = True
|
|
4494
|
+
if new_macro['used']:
|
|
4495
|
+
old_macro['used'] = True
|
|
4496
|
+
break
|
|
4497
|
+
if not found:
|
|
4498
|
+
all_macro_info.append(new_macro)
|
|
4499
|
+
|
|
4500
|
+
if summary_type == 'MisraExternalIdentifiers':
|
|
4501
|
+
for s in summary_data:
|
|
4502
|
+
is_declaration = s['decl']
|
|
4503
|
+
if is_declaration:
|
|
4504
|
+
all_external_identifiers = all_external_identifiers_decl
|
|
4505
|
+
else:
|
|
4506
|
+
all_external_identifiers = all_external_identifiers_def
|
|
4507
|
+
|
|
4508
|
+
name = s['name']
|
|
4509
|
+
if name in all_external_identifiers and is_different_location(s, all_external_identifiers[name]):
|
|
4510
|
+
num = 5 if is_declaration else 6
|
|
4511
|
+
self.reportError(Location(s), 8, num)
|
|
4512
|
+
self.reportError(Location(all_external_identifiers[name]), 8, num)
|
|
4513
|
+
all_external_identifiers[name] = s
|
|
4514
|
+
|
|
4515
|
+
if summary_type == 'MisraInternalIdentifiers':
|
|
4516
|
+
for s in summary_data:
|
|
4517
|
+
if s['name'] in all_internal_identifiers:
|
|
4518
|
+
if not s['inlinefunc'] or s['file'] != all_internal_identifiers[s['name']]['file']:
|
|
4519
|
+
self.reportError(Location(s), 5, 9)
|
|
4520
|
+
self.reportError(Location(all_internal_identifiers[s['name']]), 5, 9)
|
|
4521
|
+
all_internal_identifiers[s['name']] = s
|
|
4522
|
+
|
|
4523
|
+
if summary_type == 'MisraLocalIdentifiers':
|
|
4524
|
+
for s in summary_data:
|
|
4525
|
+
all_local_identifiers[s['name']] = s
|
|
4526
|
+
|
|
4527
|
+
if summary_type == 'MisraUsage':
|
|
4528
|
+
for s in summary_data:
|
|
4529
|
+
if s['name'] in all_usage_files:
|
|
4530
|
+
all_usage_files[s['name']].append(s['file'])
|
|
4531
|
+
else:
|
|
4532
|
+
all_usage_files[s['name']] = [s['file']]
|
|
4533
|
+
|
|
4534
|
+
except FileNotFoundError:
|
|
4535
|
+
return
|
|
4536
|
+
|
|
4537
|
+
for ti in all_typedef_info:
|
|
4538
|
+
if not ti['used']:
|
|
4539
|
+
self.reportError(Location(ti), 2, 3)
|
|
4540
|
+
|
|
4541
|
+
for ti in all_tagname_info:
|
|
4542
|
+
if not ti['used']:
|
|
4543
|
+
self.reportError(Location(ti), 2, 4)
|
|
4544
|
+
|
|
4545
|
+
for m in all_macro_info:
|
|
4546
|
+
if not m['used']:
|
|
4547
|
+
self.reportError(Location(m), 2, 5)
|
|
4548
|
+
|
|
4549
|
+
all_external_identifiers = all_external_identifiers_decl
|
|
4550
|
+
all_external_identifiers.update(all_external_identifiers_def)
|
|
4551
|
+
for name, external_identifier in all_external_identifiers.items():
|
|
4552
|
+
internal_identifier = all_internal_identifiers.get(name)
|
|
4553
|
+
if internal_identifier:
|
|
4554
|
+
self.reportError(Location(internal_identifier), 5, 8)
|
|
4555
|
+
self.reportError(Location(external_identifier), 5, 8)
|
|
4556
|
+
|
|
4557
|
+
local_identifier = all_local_identifiers.get(name)
|
|
4558
|
+
if local_identifier:
|
|
4559
|
+
self.reportError(Location(local_identifier), 5, 8)
|
|
4560
|
+
self.reportError(Location(external_identifier), 5, 8)
|
|
4561
|
+
|
|
4562
|
+
for name, files in all_usage_files.items():
|
|
4563
|
+
#print('%s:%i' % (name, count))
|
|
4564
|
+
count = len(files)
|
|
4565
|
+
if count != 1 or name not in all_external_identifiers_def:
|
|
4566
|
+
continue
|
|
4567
|
+
if files[0] != Location(all_external_identifiers_def[name]).file:
|
|
4568
|
+
continue
|
|
4569
|
+
if name in all_external_identifiers:
|
|
4570
|
+
self.reportError(Location(all_external_identifiers[name]), 8, 7)
|
|
4571
|
+
|
|
4572
|
+
RULE_TEXTS_HELP = '''Path to text file of MISRA rules
|
|
4573
|
+
|
|
4574
|
+
If you have the tool 'pdftotext' you might be able
|
|
4575
|
+
to generate this textfile with such command:
|
|
4576
|
+
|
|
4577
|
+
pdftotext MISRA_C_2012.pdf MISRA_C_2012.txt
|
|
4578
|
+
|
|
4579
|
+
Otherwise you can more or less copy/paste the chapter
|
|
4580
|
+
Appendix A Summary of guidelines
|
|
4581
|
+
from the MISRA pdf. You can buy the MISRA pdf from
|
|
4582
|
+
http://www.misra.org.uk/
|
|
4583
|
+
|
|
4584
|
+
Format:
|
|
4585
|
+
|
|
4586
|
+
<..arbitrary text..>
|
|
4587
|
+
Appendix A Summary of guidelines
|
|
4588
|
+
Rule 1.1 Required
|
|
4589
|
+
Rule text for 1.1
|
|
4590
|
+
continuation of rule text for 1.1
|
|
4591
|
+
Rule 1.2 Mandatory
|
|
4592
|
+
Rule text for 1.2
|
|
4593
|
+
continuation of rule text for 1.2
|
|
4594
|
+
<...>
|
|
4595
|
+
|
|
4596
|
+
'''
|
|
4597
|
+
|
|
4598
|
+
SUPPRESS_RULES_HELP = '''MISRA rules to suppress (comma-separated)
|
|
4599
|
+
|
|
4600
|
+
For example, if you'd like to suppress rules 15.1, 11.3,
|
|
4601
|
+
and 20.13, run:
|
|
4602
|
+
|
|
4603
|
+
python misra.py --suppress-rules 15.1,11.3,20.13 ...
|
|
4604
|
+
|
|
4605
|
+
'''
|
|
4606
|
+
|
|
4607
|
+
|
|
4608
|
+
def get_args_parser():
|
|
4609
|
+
"""Generates list of command-line arguments acceptable by misra.py script."""
|
|
4610
|
+
parser = cppcheckdata.ArgumentParser()
|
|
4611
|
+
parser.add_argument("--rule-texts", type=str, help=RULE_TEXTS_HELP)
|
|
4612
|
+
parser.add_argument("--verify-rule-texts",
|
|
4613
|
+
help="Verify that all supported rules texts are present in given file and exit.",
|
|
4614
|
+
action="store_true")
|
|
4615
|
+
parser.add_argument("--suppress-rules", type=str, help=SUPPRESS_RULES_HELP)
|
|
4616
|
+
parser.add_argument("--no-summary", help="Hide summary of violations", action="store_true")
|
|
4617
|
+
parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true")
|
|
4618
|
+
parser.add_argument("-P", "--file-prefix", type=str, help="Prefix to strip when matching suppression file rules")
|
|
4619
|
+
parser.add_argument("-generate-table", help=argparse.SUPPRESS, action="store_true")
|
|
4620
|
+
parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true")
|
|
4621
|
+
parser.add_argument("--severity", type=str, help="Set a custom severity string, for example 'error' or 'warning'. ")
|
|
4622
|
+
return parser
|
|
4623
|
+
|
|
4624
|
+
|
|
4625
|
+
def main():
|
|
4626
|
+
parser = get_args_parser()
|
|
4627
|
+
args = parser.parse_args()
|
|
4628
|
+
settings = MisraSettings(args)
|
|
4629
|
+
checker = MisraChecker(settings)
|
|
4630
|
+
|
|
4631
|
+
checker.path_premium_addon = cppcheckdata.get_path_premium_addon()
|
|
4632
|
+
|
|
4633
|
+
if args.generate_table:
|
|
4634
|
+
generateTable()
|
|
4635
|
+
sys.exit(0)
|
|
4636
|
+
|
|
4637
|
+
if args.rule_texts:
|
|
4638
|
+
filename = os.path.expanduser(args.rule_texts)
|
|
4639
|
+
filename = os.path.normpath(filename)
|
|
4640
|
+
if not os.path.isfile(filename):
|
|
4641
|
+
print('Fatal error: file is not found: ' + filename)
|
|
4642
|
+
sys.exit(1)
|
|
4643
|
+
checker.loadRuleTexts(filename)
|
|
4644
|
+
if args.verify_rule_texts:
|
|
4645
|
+
checker.verifyRuleTexts()
|
|
4646
|
+
sys.exit(0)
|
|
4647
|
+
|
|
4648
|
+
if args.verify_rule_texts and not args.rule_texts:
|
|
4649
|
+
print("Error: Please specify rule texts file with --rule-texts=<file>")
|
|
4650
|
+
sys.exit(1)
|
|
4651
|
+
|
|
4652
|
+
if args.suppress_rules:
|
|
4653
|
+
checker.setSuppressionList(args.suppress_rules)
|
|
4654
|
+
|
|
4655
|
+
if args.file_prefix:
|
|
4656
|
+
checker.setFilePrefix(args.file_prefix)
|
|
4657
|
+
|
|
4658
|
+
dump_files, ctu_info_files = cppcheckdata.get_files(args)
|
|
4659
|
+
|
|
4660
|
+
if (not dump_files) and (not ctu_info_files):
|
|
4661
|
+
if not args.quiet:
|
|
4662
|
+
print("No input files.")
|
|
4663
|
+
sys.exit(0)
|
|
4664
|
+
|
|
4665
|
+
if args.severity:
|
|
4666
|
+
checker.setSeverity(args.severity)
|
|
4667
|
+
|
|
4668
|
+
for item in dump_files:
|
|
4669
|
+
checker.parseDump(item)
|
|
4670
|
+
|
|
4671
|
+
if settings.verify:
|
|
4672
|
+
verify_expected = checker.get_verify_expected()
|
|
4673
|
+
verify_actual = checker.get_verify_actual()
|
|
4674
|
+
|
|
4675
|
+
exitCode = 0
|
|
4676
|
+
for expected in verify_expected:
|
|
4677
|
+
if expected not in verify_actual:
|
|
4678
|
+
print('Expected but not seen: ' + expected)
|
|
4679
|
+
exitCode = 1
|
|
4680
|
+
for actual in verify_actual:
|
|
4681
|
+
if actual not in verify_expected:
|
|
4682
|
+
print('Not expected: ' + actual)
|
|
4683
|
+
exitCode = 1
|
|
4684
|
+
|
|
4685
|
+
# Existing behavior of verify mode is to exit
|
|
4686
|
+
# on the first un-expected output.
|
|
4687
|
+
# TODO: Is this required? or can it be moved to after
|
|
4688
|
+
# all input files have been processed
|
|
4689
|
+
if exitCode != 0:
|
|
4690
|
+
sys.exit(exitCode)
|
|
4691
|
+
|
|
4692
|
+
checker.analyse_ctu_info(ctu_info_files)
|
|
4693
|
+
|
|
4694
|
+
if settings.verify:
|
|
4695
|
+
sys.exit(exitCode)
|
|
4696
|
+
|
|
4697
|
+
number_of_violations = len(checker.get_violations())
|
|
4698
|
+
if number_of_violations > 0:
|
|
4699
|
+
if settings.show_summary:
|
|
4700
|
+
print("\nMISRA rules violations found:\n\t%s\n" % (
|
|
4701
|
+
"\n\t".join(["%s: %d" % (viol, len(checker.get_violations(viol))) for viol in
|
|
4702
|
+
checker.get_violation_types()])))
|
|
4703
|
+
|
|
4704
|
+
rules_violated = {}
|
|
4705
|
+
for severity, ids in checker.get_violations():
|
|
4706
|
+
for misra_id in ids:
|
|
4707
|
+
rules_violated[misra_id] = rules_violated.get(misra_id, 0) + 1
|
|
4708
|
+
print("MISRA rules violated:")
|
|
4709
|
+
convert = lambda text: int(text) if text.isdigit() else text
|
|
4710
|
+
misra_sort = lambda key: [convert(c) for c in re.split(r'[\.-]([0-9]*)', key)]
|
|
4711
|
+
for misra_id in sorted(rules_violated.keys(), key=misra_sort):
|
|
4712
|
+
res = re.match(r'misra-c2012-([0-9]+)\\.([0-9]+)', misra_id)
|
|
4713
|
+
if res is None:
|
|
4714
|
+
num = 0
|
|
4715
|
+
else:
|
|
4716
|
+
num = int(res.group(1)) * 100 + int(res.group(2))
|
|
4717
|
+
severity = '-'
|
|
4718
|
+
if num in checker.ruleTexts:
|
|
4719
|
+
severity = checker.ruleTexts[num].cppcheck_severity
|
|
4720
|
+
print("\t%15s (%s): %d" % (misra_id, severity, rules_violated[misra_id]))
|
|
4721
|
+
|
|
4722
|
+
if args.show_suppressed_rules:
|
|
4723
|
+
checker.showSuppressedRules()
|
|
4724
|
+
|
|
4725
|
+
|
|
4726
|
+
if __name__ == '__main__':
|
|
4727
|
+
main()
|
|
4728
|
+
sys.exit(cppcheckdata.EXIT_CODE)
|