File: misc.py

package info (click to toggle)
cppcheck 2.17.1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 25,384 kB
  • sloc: cpp: 263,341; python: 19,737; ansic: 7,953; sh: 1,018; makefile: 996; xml: 994; cs: 291
file content (163 lines) | stat: -rw-r--r-- 6,065 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/usr/bin/env python3
#
# Misc: Uncategorized checks that might be moved to some better addon later
#
# Example usage of this addon (scan a sourcefile main.cpp)
# cppcheck --dump main.cpp
# python misc.py main.cpp.dump

import cppcheckdata
import sys
import re

DEBUG = ('-debug' in sys.argv)
VERIFY = ('-verify' in sys.argv)
VERIFY_EXPECTED = []
VERIFY_ACTUAL = []

def reportError(token, severity, msg, id):
    if id == 'debug' and not DEBUG:
        return
    if VERIFY:
        VERIFY_ACTUAL.append(str(token.linenr) + ':' + id)
    else:
        cppcheckdata.reportError(token, severity, msg, 'misc', id)

def simpleMatch(token, pattern):
    return cppcheckdata.simpleMatch(token, pattern)

# Get function arguments
def getArgumentsRecursive(tok, arguments):
    if tok is None:
        return
    if tok.str == ',':
        getArgumentsRecursive(tok.astOperand1, arguments)
        getArgumentsRecursive(tok.astOperand2, arguments)
    else:
        arguments.append(tok)

def getArguments(ftok):
    arguments = []
    getArgumentsRecursive(ftok.astOperand2, arguments)
    return arguments

def isStringLiteral(tokenString):
    return tokenString.startswith('"')

# check data
def stringConcatInArrayInit(data):
    # Get all string macros
    stringMacros = []
    for cfg in data.iterconfigurations():
        for directive in cfg.directives:
            res = re.match(r'#define[ ]+([A-Za-z0-9_]+)[ ]+".*', directive.str)
            if res:
                macroName = res.group(1)
                if macroName not in stringMacros:
                    stringMacros.append(macroName)

    # Check code
    arrayInit = False
    for i in range(len(data.rawTokens)):
        if i < 2:
            continue
        tok1 = data.rawTokens[i-2].str
        tok2 = data.rawTokens[i-1].str
        tok3 = data.rawTokens[i-0].str
        if tok3 == '}':
            arrayInit = False
        elif tok1 == ']' and tok2 == '=' and tok3 == '{':
            arrayInit = True
        elif arrayInit and (tok1 in [',', '{']):
            isString2 = (isStringLiteral(tok2) or (tok2 in stringMacros))
            isString3 = (isStringLiteral(tok3) or (tok3 in stringMacros))
            if isString2 and isString3:
                reportError(data.rawTokens[i], 'style', 'String concatenation in array initialization, missing comma?', 'stringConcatInArrayInit')


def implicitlyVirtual(data):
    for cfg in data.iterconfigurations():
        for function in cfg.functions:
            if function.isImplicitlyVirtual is None:
                continue
            if not function.isImplicitlyVirtual:
                continue
            reportError(function.tokenDef, 'style', 'Function \'' + function.name + '\' overrides base class function but is not marked with \'virtual\' keyword.', 'implicitlyVirtual')

def ellipsisStructArg(data):
    for cfg in data.iterconfigurations():
        for tok in cfg.tokenlist:
            if tok.str != '(':
                continue
            if tok.astOperand1 is None or tok.astOperand2 is None:
                continue
            if tok.astOperand2.str != ',':
                continue
            if tok.scope.type in ['Global', 'Class']:
                continue
            if tok.astOperand1.function is None:
                continue
            for argnr, argvar in tok.astOperand1.function.argument.items():
                if argnr < 1:
                    continue
                if not simpleMatch(argvar.typeStartToken, '...'):
                    continue
                callArgs = getArguments(tok)
                for i in range(argnr-1, len(callArgs)):
                    valueType = callArgs[i].valueType
                    if valueType is None:
                        argStart = callArgs[i].previous
                        while argStart.str != ',':
                            if argStart.str == ')':
                                argStart = argStart.link
                            argStart = argStart.previous
                        argEnd = callArgs[i]
                        while argEnd.str != ',' and argEnd.str != ')':
                            if argEnd.str == '(':
                                argEnd = argEnd.link
                            argEnd = argEnd.next
                        expression = ''
                        argStart = argStart.next
                        while argStart != argEnd:
                            expression = expression + argStart.str
                            argStart = argStart.next
                        reportError(tok, 'debug', 'Bailout, unknown argument type for argument \'' + expression + '\'.', 'debug')
                        continue
                    if valueType.pointer > 0:
                        continue
                    if valueType.type != 'record' and valueType.type != 'container':
                        continue
                    reportError(tok, 'style', 'Passing record to ellipsis function \'' + tok.astOperand1.function.name + '\'.', 'ellipsisStructArg')
                break

for arg in sys.argv[1:]:
    if arg in ['-debug', '-verify', '--cli']:
        continue

    print("Checking %s..." % arg)
    data = cppcheckdata.CppcheckData(arg)

    if VERIFY:
        VERIFY_ACTUAL = []
        VERIFY_EXPECTED = []
        for tok in data.rawTokens:
            if tok.str.startswith('//'):
                for word in tok.str[2:].split(' '):
                    if word in ['stringConcatInArrayInit', 'implicitlyVirtual', 'ellipsisStructArg']:
                        VERIFY_EXPECTED.append(str(tok.linenr) + ':' + word)

    stringConcatInArrayInit(data)
    implicitlyVirtual(data)
    ellipsisStructArg(data)

    if VERIFY:
        for expected in VERIFY_EXPECTED:
            if expected not in VERIFY_ACTUAL:
                print('Expected but not seen: ' + expected)
                sys.exit(1)
        for actual in VERIFY_ACTUAL:
            if actual not in VERIFY_EXPECTED:
                print('Not expected: ' + actual)
                sys.exit(1)

sys.exit(cppcheckdata.EXIT_CODE)