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.
Files changed (84) hide show
  1. cppcheck/__init__.py +55 -0
  2. cppcheck/cppcheck-2.9.tar.gz +0 -0
  3. cppcheck/data/addons/__init__.py +0 -0
  4. cppcheck/data/addons/cppcheck.py +39 -0
  5. cppcheck/data/addons/cppcheckdata.py +1529 -0
  6. cppcheck/data/addons/findcasts.py +33 -0
  7. cppcheck/data/addons/misc.py +163 -0
  8. cppcheck/data/addons/misra.py +4728 -0
  9. cppcheck/data/addons/misra_9.py +513 -0
  10. cppcheck/data/addons/naming.py +91 -0
  11. cppcheck/data/addons/namingng.py +253 -0
  12. cppcheck/data/addons/runaddon.py +12 -0
  13. cppcheck/data/addons/threadsafety.py +39 -0
  14. cppcheck/data/addons/y2038.py +252 -0
  15. cppcheck/data/cfg/avr.cfg +431 -0
  16. cppcheck/data/cfg/bento4.cfg +142 -0
  17. cppcheck/data/cfg/boost.cfg +1268 -0
  18. cppcheck/data/cfg/bsd.cfg +546 -0
  19. cppcheck/data/cfg/cairo.cfg +88 -0
  20. cppcheck/data/cfg/cppcheck-lib.cfg +72 -0
  21. cppcheck/data/cfg/cppunit.cfg +48 -0
  22. cppcheck/data/cfg/daca.cfg +31 -0
  23. cppcheck/data/cfg/dpdk.cfg +11 -0
  24. cppcheck/data/cfg/embedded_sql.cfg +4 -0
  25. cppcheck/data/cfg/emscripten.cfg +7 -0
  26. cppcheck/data/cfg/ginac.cfg +12848 -0
  27. cppcheck/data/cfg/gnu.cfg +1626 -0
  28. cppcheck/data/cfg/googletest.cfg +60 -0
  29. cppcheck/data/cfg/gtk.cfg +20584 -0
  30. cppcheck/data/cfg/icu.cfg +6 -0
  31. cppcheck/data/cfg/kde.cfg +51 -0
  32. cppcheck/data/cfg/libcerror.cfg +128 -0
  33. cppcheck/data/cfg/libcurl.cfg +487 -0
  34. cppcheck/data/cfg/libsigc++.cfg +36 -0
  35. cppcheck/data/cfg/lua.cfg +316 -0
  36. cppcheck/data/cfg/mfc.cfg +256 -0
  37. cppcheck/data/cfg/microsoft_atl.cfg +40 -0
  38. cppcheck/data/cfg/microsoft_sal.cfg +491 -0
  39. cppcheck/data/cfg/microsoft_unittest.cfg +20 -0
  40. cppcheck/data/cfg/motif.cfg +318 -0
  41. cppcheck/data/cfg/nspr.cfg +38 -0
  42. cppcheck/data/cfg/ntl.cfg +6907 -0
  43. cppcheck/data/cfg/opencv2.cfg +132 -0
  44. cppcheck/data/cfg/opengl.cfg +580 -0
  45. cppcheck/data/cfg/openmp.cfg +157 -0
  46. cppcheck/data/cfg/openssl.cfg +174 -0
  47. cppcheck/data/cfg/pcre.cfg +165 -0
  48. cppcheck/data/cfg/posix.cfg +6252 -0
  49. cppcheck/data/cfg/python.cfg +674 -0
  50. cppcheck/data/cfg/qt.cfg +5255 -0
  51. cppcheck/data/cfg/ruby.cfg +142 -0
  52. cppcheck/data/cfg/sdl.cfg +354 -0
  53. cppcheck/data/cfg/sfml.cfg +301 -0
  54. cppcheck/data/cfg/sqlite3.cfg +1527 -0
  55. cppcheck/data/cfg/std.cfg +8716 -0
  56. cppcheck/data/cfg/tinyxml2.cfg +56 -0
  57. cppcheck/data/cfg/vcl.cfg +4 -0
  58. cppcheck/data/cfg/windows.cfg +17941 -0
  59. cppcheck/data/cfg/wxsqlite3.cfg +1955 -0
  60. cppcheck/data/cfg/wxsvg.cfg +16905 -0
  61. cppcheck/data/cfg/wxwidgets.cfg +16464 -0
  62. cppcheck/data/cfg/zlib.cfg +1450 -0
  63. cppcheck/data/cppcheck-htmlreport +986 -0
  64. cppcheck/data/cppcheck.exe +0 -0
  65. cppcheck/data/platforms/aix_ppc64.xml +17 -0
  66. cppcheck/data/platforms/arm32-wchar_t2.xml +17 -0
  67. cppcheck/data/platforms/arm32-wchar_t4.xml +17 -0
  68. cppcheck/data/platforms/arm64-wchar_t2.xml +17 -0
  69. cppcheck/data/platforms/arm64-wchar_t4.xml +17 -0
  70. cppcheck/data/platforms/avr8.xml +18 -0
  71. cppcheck/data/platforms/cray_sv1.xml +17 -0
  72. cppcheck/data/platforms/elbrus-e1cp.xml +18 -0
  73. cppcheck/data/platforms/mips32.xml +18 -0
  74. cppcheck/data/platforms/msp430_eabi_large_datamodel.xml +17 -0
  75. cppcheck/data/platforms/pic16.xml +18 -0
  76. cppcheck/data/platforms/pic8-enhanced.xml +18 -0
  77. cppcheck/data/platforms/pic8.xml +18 -0
  78. cppcheck/data/platforms/unix32-unsigned.xml +17 -0
  79. cppcheck/data/platforms/unix64-unsigned.xml +17 -0
  80. cppcheck_py-2.9.dist-info/METADATA +76 -0
  81. cppcheck_py-2.9.dist-info/RECORD +84 -0
  82. cppcheck_py-2.9.dist-info/WHEEL +5 -0
  83. cppcheck_py-2.9.dist-info/entry_points.txt +4 -0
  84. 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)