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