migraphx.py 11.9 KB
Newer Older
1
2
3
import cppcheck, itertools
from cppcheckdata import simpleMatch, match

Paul's avatar
Format  
Paul committed
4

5
6
7
8
9
10
def skipTokenMatches(tokens, skip=None):
    for tok in tokens:
        if match(tok, skip):
            continue
        yield tok

Paul's avatar
Format  
Paul committed
11

12
def isTokensEqual(xtokens, ytokens, skip=None):
Paul's avatar
Format  
Paul committed
13
14
    for x, y in itertools.zip_longest(skipTokenMatches(xtokens, skip),
                                      skipTokenMatches(ytokens, skip)):
15
16
17
18
19
20
21
22
        if not x:
            return False
        if not y:
            return False
        if x.str != y.str:
            return False
    return True

Paul's avatar
Format  
Paul committed
23

24
25
26
27
28
29
30
def getInnerLink(token):
    if not token:
        return []
    if not token.link:
        return []
    return token.next.forward(token.link)

Paul's avatar
Format  
Paul committed
31

32
33
34
35
36
37
38
39
def getVariableDecl(var):
    if not var:
        return []
    end = var.typeEndToken
    if end:
        end = end.next
    return var.typeStartToken.forward(end)

Paul's avatar
Paul committed
40
41
42
43
44
45
46
47
48
49
50
def isFunctionCall(token):
    if not token:
        return False
    if not token.isName:
        return False
    if not token.next:
        return False
    if not token.next.str == '(':
        return False
    return True

Paul's avatar
Format  
Paul committed
51

52
53
54
@cppcheck.checker
def AvoidBranchingStatementAsLastInLoop(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
55
56
        if not token.str in ['for', 'while']:
            continue
57
58
59
60
61
62
63
64
65
        end = match(token, "for|while (*) {*}").end
        if not end:
            continue
        stmt = end.tokAt(-2)
        if not match(stmt, "%any% ; }"):
            continue
        if not match(stmt, "break|continue"):
            stmt = stmt.astTop()
        if match(stmt, "break|continue|return"):
Paul's avatar
Format  
Paul committed
66
67
68
69
70
            cppcheck.reportError(
                stmt, "style",
                "Branching statement as the last statement inside a loop is very confusing."
            )

71
72
73
74
75
76
77
78

# @cppcheck.checker
# def CollapsibleIfStatements(cfg, data):
#     for token in cfg.tokenlist:
#         if not match(token, "if (*) { if (*) {*} }"):
#             continue
#         cppcheck.reportError(token, "style", "These two if statements can be collapsed into one.")

Paul's avatar
Format  
Paul committed
79

80
81
82
@cppcheck.checker
def ConditionalAssert(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
83
84
        if token.str != 'if':
            continue
85
86
        if not match(token, "if (*) { assert (*) ; }"):
            continue
Paul's avatar
Format  
Paul committed
87
88
89
        cppcheck.reportError(token, "style",
                             "The if condition should be included in assert.")

90
91
92
93

@cppcheck.checker
def EmptyCatchStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
94
95
        if token.str != 'catch':
            continue
96
97
98
99
        if not match(token, "catch (*) { }"):
            continue
        cppcheck.reportError(token, "style", "An empty catch statement.")

Paul's avatar
Format  
Paul committed
100

101
102
103
@cppcheck.checker
def EmptyDoWhileStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
104
105
106
        if token.str != 'do':
            continue
        if not simpleMatch(token, "do { } while ("):
107
108
109
            continue
        cppcheck.reportError(token, "style", "Empty do-while.")

Paul's avatar
Format  
Paul committed
110

111
112
113
@cppcheck.checker
def EmptyElseBlock(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
114
115
116
        if token.str != 'else':
            continue
        if not simpleMatch(token, "else { }"):
117
            continue
Paul's avatar
Format  
Paul committed
118
119
120
        cppcheck.reportError(token, "style",
                             "Empty else statement can be safely removed.")

121
122
123
124

@cppcheck.checker
def EmptyForStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
125
126
        if token.str != 'for':
            continue
127
128
129
130
        if not match(token, "for (*) { }"):
            continue
        cppcheck.reportError(token, "style", "Empty for statement.")

Paul's avatar
Format  
Paul committed
131

132
133
134
@cppcheck.checker
def EmptyIfStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
135
136
        if token.str != 'if':
            continue
137
138
139
140
        if not match(token, "if (*) { }"):
            continue
        cppcheck.reportError(token, "style", "Empty if statement.")

Paul's avatar
Format  
Paul committed
141

142
143
144
@cppcheck.checker
def EmptySwitchStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
145
146
        if token.str != 'switch':
            continue
147
148
149
150
        if not match(token, "switch (*) { }"):
            continue
        cppcheck.reportError(token, "style", "Empty switch statement.")

Paul's avatar
Format  
Paul committed
151

152
153
154
@cppcheck.checker
def EmptyWhileStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
155
156
        if token.str != 'while':
            continue
157
158
159
160
        if not match(token, "while (*) { }"):
            continue
        cppcheck.reportError(token, "style", "Empty while statement.")

Paul's avatar
Format  
Paul committed
161

162
163
164
@cppcheck.checker
def ForLoopShouldBeWhileLoop(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
165
166
        if token.str != 'for':
            continue
167
168
169
170
171
172
173
174
        if not match(token, "for ( ; !!;"):
            continue
        # Skip empty for loops
        if match(token, "for (*) { }"):
            continue
        end = token.next.link
        if not match(end.tokAt(-1), "; )"):
            continue
Paul's avatar
Format  
Paul committed
175
176
177
        cppcheck.reportError(token, "style",
                             "For loop should be written as a while loop.")

178
179
180
181

@cppcheck.checker
def GotoStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
182
        if token.str != 'goto':
183
184
185
            continue
        cppcheck.reportError(token, "style", "Goto considered harmful.")

Paul's avatar
Format  
Paul committed
186

187
188
189
190
191
192
193
194
195
196
197
198
# @cppcheck.checker
# def InvertedLogic(cfg, data):
#     for token in cfg.tokenlist:
#         cond = None
#         if match(token, "if (*) {*} else { !!if"):
#             cond = token.next.astOperand2
#         elif match(token, "?"):
#             cond = token.astOperand1
#         if not match(cond, "!|!="):
#             continue
#         cppcheck.reportError(cond, "style", "It is cleaner to invert the logic.")

Paul's avatar
Format  
Paul committed
199

200
201
202
@cppcheck.checker
def LambdaAttribute(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
203
204
        if token.str != ']':
            continue
205
206
        if not match(token, "] __device__|__host__ {|{}}"):
            continue
Paul's avatar
Format  
Paul committed
207
208
209
210
        cppcheck.reportError(
            token, "style",
            "Attributes to lambdas must come after parameter list.")

211
212
213
214
215
216

@cppcheck.checker
def MultipleUnaryOperator(cfg, data):
    for token in cfg.tokenlist:
        if not token.isUnaryOp(token.str):
            continue
Paul's avatar
Paul committed
217
218
        if not token.str in ['+', '-', '~', '!']:
            continue
219
220
221
222
        if not match(token.astOperand1, "+|-|~|!"):
            continue
        if not token.astOperand1.isUnaryOp(token.astOperand1.str):
            continue
Paul's avatar
Format  
Paul committed
223
224
225
        cppcheck.reportError(token, "style",
                             "Muliple unary operators used together.")

226
227
228
229

@cppcheck.checker
def MutableVariable(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
230
231
        if token.str != 'mutable':
            continue
232
233
        if not match(token, "mutable %var%"):
            continue
Paul's avatar
Format  
Paul committed
234
235
236
        cppcheck.reportError(token, "style",
                             "Do not create mutable variables.")

237
238
239
240
241
242
243
244
245
246
247

@cppcheck.checker
def NestedBlocks(cfg, data):
    for token in cfg.tokenlist:
        block = match(token, "if|while|for|switch (*) { {*}@block }").block
        if not block:
            block = match(token, "; { {*}@block break ; }").block
        if not block:
            continue
        cppcheck.reportError(block, "style", "Block directly inside block.")

Paul's avatar
Format  
Paul committed
248

249
250
251
@cppcheck.checker
def RedundantCast(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Format  
Paul committed
252
253
        m = match(token,
                  "%var%@decl ; %var%@assign = static_cast <*>@cast (*) ;")
254
255
256
257
        if not m:
            continue
        if m.decl.varId != m.assign.varId:
            continue
Paul's avatar
Paul committed
258
        if not simpleMatch(token.previous, "auto"):
Paul's avatar
Format  
Paul committed
259
260
261
            if not isTokensEqual(getVariableDecl(m.decl.variable),
                                 getInnerLink(m.cast),
                                 skip='const|volatile|&|&&|*'):
262
263
264
                continue
        cppcheck.reportError(token, "style", "Static cast is redundant.")

Paul's avatar
Format  
Paul committed
265

266
267
268
@cppcheck.checker
def RedundantConditionalOperator(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
269
270
        if token.str != '?':
            continue
271
272
        if not match(token, "? true|false : true|false"):
            continue
Paul's avatar
Format  
Paul committed
273
274
275
        cppcheck.reportError(token, "style",
                             "Conditional operator is redundant.")

276
277
278
279

@cppcheck.checker
def RedundantIfStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
280
281
        if token.str != 'if':
            continue
Paul's avatar
Format  
Paul committed
282
283
284
        if not match(
                token,
                "if (*) { return true|false ; } else { return true|false ; }"):
285
286
287
            continue
        cppcheck.reportError(token, "style", "The if statement is redundant.")

Paul's avatar
Format  
Paul committed
288

289
290
291
@cppcheck.checker
def RedundantLocalVariable(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Format  
Paul committed
292
293
        m = match(token,
                  "%var%@decl ; %var%@assign = **; return %var%@returned ;")
294
295
296
297
298
299
        if not m:
            continue
        if m.decl.varId != m.assign.varId:
            continue
        if m.decl.varId != m.returned.varId:
            continue
Paul's avatar
Format  
Paul committed
300
301
302
303
304
        cppcheck.reportError(
            m.returned, "style",
            "Variable is returned immediately after its declaration, can be simplified to just return expression."
        )

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320

# @cppcheck.checker
# def UnnecessaryElseStatement(cfg, data):
#     for token in cfg.tokenlist:
#         m = match(token, "if (*) {*}@block else@else_statement {")
#         if not m:
#             continue
#         stmt = m.block.link.tokAt(-2)
#         if not match(stmt, "%any% ; }"):
#             continue
#         if not match(stmt, "break|continue"):
#             stmt = stmt.astTop()
#         if not match(stmt, "break|continue|return|throw"):
#             continue
#         cppcheck.reportError(m.else_statement, "style", "Else statement is not necessary.")

Paul's avatar
Format  
Paul committed
321

322
323
324
@cppcheck.checker
def UnnecessaryEmptyCondition(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
325
326
        if token.str != 'if':
            continue
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
        m = match(token, "if (*)@if_cond { for (*)@for_cond {*} }")
        if not m:
            continue
        cond = m.if_cond.astOperand2
        if match(cond, "!"):
            cond = cond.astOperand1
        if not match(cond.tokAt(-2), ". empty ("):
            continue
        container = cond.tokAt(-2).astOperand1
        if not container.varId:
            continue
        if not match(m.for_cond.astOperand2, ":"):
            continue
        container_iter = m.for_cond.astOperand2.astOperand2
        if container_iter.varId != container.varId:
            continue
Paul's avatar
Format  
Paul committed
343
344
345
346
        cppcheck.reportError(
            container, "style",
            "Unnecessary check for empty before for range loop.")

347
348
349
350

@cppcheck.checker
def UseDeviceLaunch(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
351
        if not simpleMatch(token, "hipLaunchKernelGGL ("):
352
353
354
            continue
        cppcheck.reportError(token, "style", "Use device::launch instead.")

Paul's avatar
Format  
Paul committed
355

356
357
@cppcheck.checker
def UseManagePointer(cfg, data):
Paul's avatar
Paul committed
358
    functions = {"fclose", "free", "hipFree", "hipHostFree", "hipFreeArray", "hipMemFree", "hipStreamDestroy", "hipEventDestroy", "hipArrayDestroy", "hipCtxDestroy", "hipDestroyTextureObject", "hipDestroySurfaceObject", "miirDestroyHandle"}
359
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
360
361
362
        if not isFunctionCall(token):
            continue
        if not token.str in functions:
363
            continue
Paul's avatar
Format  
Paul committed
364
365
366
        cppcheck.reportError(token, "style",
                             "Use manage pointer for resource management.")

367
368
369
370

@cppcheck.checker
def UseSmartPointer(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
371
372
        if token.str != 'new':
            continue
373
374
        if not match(token, "new %name%"):
            continue
Paul's avatar
Format  
Paul committed
375
376
377
        cppcheck.reportError(token, "style",
                             "Use manage pointer for resource management.")

378
379
380

@cppcheck.checker
def useStlAlgorithms(cfg, data):
Paul's avatar
Paul committed
381
    copy_functions = {"memcpy", "strcpy", "strncpy", "strcat", "strncat"}
382
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
383
384
385
        if not isFunctionCall(token):
            continue
        if token.str in copy_functions:
386
            cppcheck.reportError(token, "style", "Use std::copy instead.")
Paul's avatar
Paul committed
387
        elif token.str == 'memset':
388
            cppcheck.reportError(token, "style", "Use std::fill instead.")
Paul's avatar
Paul committed
389
        elif token.str == 'memcmp':
Paul's avatar
Format  
Paul committed
390
391
            cppcheck.reportError(token, "style",
                                 "Use std::equal_range instead.")
Paul's avatar
Paul committed
392
        elif token.str == 'memchr':
393
            cppcheck.reportError(token, "style", "Use std::find instead.")