migraphx.py 12.1 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
Format  
Paul committed
40

Paul's avatar
Paul committed
41
42
43
44
45
46
47
48
49
50
51
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
52

53
54
55
@cppcheck.checker
def AvoidBranchingStatementAsLastInLoop(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
56
57
        if not token.str in ['for', 'while']:
            continue
58
59
60
61
62
63
64
65
66
        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
67
68
69
70
71
            cppcheck.reportError(
                stmt, "style",
                "Branching statement as the last statement inside a loop is very confusing."
            )

72
73
74
75
76
77
78
79

# @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
80

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

91
92
93
94

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

Paul's avatar
Format  
Paul committed
101

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

Paul's avatar
Format  
Paul committed
111

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

122
123
124
125

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

Paul's avatar
Format  
Paul committed
132

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

Paul's avatar
Format  
Paul committed
142

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

Paul's avatar
Format  
Paul committed
152

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

Paul's avatar
Format  
Paul committed
162

163
164
165
@cppcheck.checker
def ForLoopShouldBeWhileLoop(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
166
167
        if token.str != 'for':
            continue
168
169
170
171
172
173
174
175
        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
176
177
178
        cppcheck.reportError(token, "style",
                             "For loop should be written as a while loop.")

179
180
181
182

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

Paul's avatar
Format  
Paul committed
187

188
189
190
191
192
193
194
195
196
197
198
199
# @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
200

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

212
213
214
215
216
217

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

227
228
229
230

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

238
239
240
241

@cppcheck.checker
def NestedBlocks(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
242
243
        if not token.str in ['if', 'while', 'for', 'switch']:
            continue
244
245
246
247
248
249
250
        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
251

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

Paul's avatar
Format  
Paul committed
270

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

281
282
283
284

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

Paul's avatar
Format  
Paul committed
293

294
295
296
@cppcheck.checker
def RedundantLocalVariable(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
297
298
        if not token.variable:
            continue
Paul's avatar
Format  
Paul committed
299
300
        m = match(token,
                  "%var%@decl ; %var%@assign = **; return %var%@returned ;")
301
302
303
304
305
306
        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
307
308
309
310
311
        cppcheck.reportError(
            m.returned, "style",
            "Variable is returned immediately after its declaration, can be simplified to just return expression."
        )

312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327

# @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
328

329
330
331
@cppcheck.checker
def UnnecessaryEmptyCondition(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
332
333
        if token.str != 'if':
            continue
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
        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
350
351
352
353
        cppcheck.reportError(
            container, "style",
            "Unnecessary check for empty before for range loop.")

354
355
356
357

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

Paul's avatar
Format  
Paul committed
362

363
364
@cppcheck.checker
def UseManagePointer(cfg, data):
Paul's avatar
Format  
Paul committed
365
366
367
368
369
370
    functions = {
        "fclose", "free", "hipFree", "hipHostFree", "hipFreeArray",
        "hipMemFree", "hipStreamDestroy", "hipEventDestroy", "hipArrayDestroy",
        "hipCtxDestroy", "hipDestroyTextureObject", "hipDestroySurfaceObject",
        "miirDestroyHandle"
    }
371
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
372
373
374
        if not isFunctionCall(token):
            continue
        if not token.str in functions:
375
            continue
Paul's avatar
Format  
Paul committed
376
377
378
        cppcheck.reportError(token, "style",
                             "Use manage pointer for resource management.")

379
380
381
382

@cppcheck.checker
def UseSmartPointer(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
383
384
        if token.str != 'new':
            continue
385
386
        if not match(token, "new %name%"):
            continue
Paul's avatar
Format  
Paul committed
387
388
389
        cppcheck.reportError(token, "style",
                             "Use manage pointer for resource management.")

390
391
392

@cppcheck.checker
def useStlAlgorithms(cfg, data):
Paul's avatar
Paul committed
393
    copy_functions = {"memcpy", "strcpy", "strncpy", "strcat", "strncat"}
394
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
395
396
397
        if not isFunctionCall(token):
            continue
        if token.str in copy_functions:
398
            cppcheck.reportError(token, "style", "Use std::copy instead.")
Paul's avatar
Paul committed
399
        elif token.str == 'memset':
400
            cppcheck.reportError(token, "style", "Use std::fill instead.")
Paul's avatar
Paul committed
401
        elif token.str == 'memcmp':
Paul's avatar
Format  
Paul committed
402
403
            cppcheck.reportError(token, "style",
                                 "Use std::equal_range instead.")
Paul's avatar
Paul committed
404
        elif token.str == 'memchr':
405
            cppcheck.reportError(token, "style", "Use std::find instead.")