migraphx.py 14.2 KB
Newer Older
1
import cppcheck, itertools
Paul's avatar
Paul committed
2
from cppcheckdata import simpleMatch, match #patterns, bind_split
3

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
Paul committed
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
# def isplit(source, sep=' '):
#     sepsize = len(sep)
#     start = 0
#     while True:
#         idx = source.find(sep, start)
#         if idx == -1:
#             yield source[start:]
#             return
#         yield source[start:idx]
#         start = idx + sepsize

# def match_atom(token, p):
#     if not token:
#         return None
#     if not p:
#         return None
#     if token.str == p:
#         return token
#     if p in ['!', '|', '||', '%', '!=', '*']:
#         return None
#     if p in patterns:
#         return patterns[p](token)
#     if '|' in p:
#         for x in isplit(p, '|'):
#             t = match_atom(token, x)
#             if t:
#                 return t
#     elif p.startswith('!!'):
#         t = match_atom(token, p[2:])
#         if not t:
#             return token
#     elif p.startswith('**'):
#         a = p[2:]
#         t = token
#         while t:
#             if match_atom(t, a):
#                 return t
#             if t.link and t.str in ['(', '[', '<', '{']:
#                 t = t.link
#             t = t.next
#     return None

# class MatchResult:
#     def __init__(self, matches, bindings=None, keys=None):
#         self.__dict__.update(bindings or {})
#         self._matches = matches
#         self._keys = keys or []

#     def __bool__(self):
#         return self._matches

#     def __nonzero__(self):
#         return self._matches

#     def __getattr__(self, k):
#         if not self._matches:
#             return None
#         if k in self._keys:
#             return None
#         else:
#             raise AttributeError

# def match(token, pattern):
#     if not pattern:
#         return MatchResult(False)
#     end = None
#     bindings = {}
#     words = (bind_split(word) for word in isplit(pattern))
#     for p, b in words:
#         t = match_atom(token, p)
#         if b:
#             bindings[b] = token
#         if not t:
#             return MatchResult(False)
#         end = t
#         token = t.next
#     bindings['end'] = end
#     return MatchResult(True, bindings=bindings)
Paul's avatar
Format  
Paul committed
129

130
131
132
@cppcheck.checker
def AvoidBranchingStatementAsLastInLoop(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
133
134
        if not token.str in ['for', 'while']:
            continue
135
136
137
138
139
140
141
142
143
        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
144
145
146
147
148
            cppcheck.reportError(
                stmt, "style",
                "Branching statement as the last statement inside a loop is very confusing."
            )

149
150
151
152
153
154
155
156

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

158
159
160
@cppcheck.checker
def ConditionalAssert(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
161
162
        # if token.str != 'if':
            # continue
163
164
        if not match(token, "if (*) { assert (*) ; }"):
            continue
Paul's avatar
Format  
Paul committed
165
166
167
        cppcheck.reportError(token, "style",
                             "The if condition should be included in assert.")

168
169
170
171

@cppcheck.checker
def EmptyCatchStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
172
173
        # if token.str != 'catch':
            # continue
174
175
176
177
        if not match(token, "catch (*) { }"):
            continue
        cppcheck.reportError(token, "style", "An empty catch statement.")

Paul's avatar
Format  
Paul committed
178

179
180
181
@cppcheck.checker
def EmptyDoWhileStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
182
183
        # if token.str != 'do':
            # continue
Paul's avatar
Paul committed
184
        if not simpleMatch(token, "do { } while ("):
185
186
187
            continue
        cppcheck.reportError(token, "style", "Empty do-while.")

Paul's avatar
Format  
Paul committed
188

189
190
191
@cppcheck.checker
def EmptyElseBlock(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
192
193
        # if token.str != 'else':
            # continue
Paul's avatar
Paul committed
194
        if not simpleMatch(token, "else { }"):
195
            continue
Paul's avatar
Format  
Paul committed
196
197
198
        cppcheck.reportError(token, "style",
                             "Empty else statement can be safely removed.")

199
200
201
202

@cppcheck.checker
def EmptyForStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
203
204
        # if token.str != 'for':
            # continue
205
206
207
208
        if not match(token, "for (*) { }"):
            continue
        cppcheck.reportError(token, "style", "Empty for statement.")

Paul's avatar
Format  
Paul committed
209

210
211
212
@cppcheck.checker
def EmptyIfStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
213
214
        # if token.str != 'if':
            # continue
215
216
217
218
        if not match(token, "if (*) { }"):
            continue
        cppcheck.reportError(token, "style", "Empty if statement.")

Paul's avatar
Format  
Paul committed
219

220
221
222
@cppcheck.checker
def EmptySwitchStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
223
224
        # if token.str != 'switch':
            # continue
225
226
227
228
        if not match(token, "switch (*) { }"):
            continue
        cppcheck.reportError(token, "style", "Empty switch statement.")

Paul's avatar
Format  
Paul committed
229

230
231
232
@cppcheck.checker
def EmptyWhileStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
233
234
        # if token.str != 'while':
            # continue
235
236
237
238
        if not match(token, "while (*) { }"):
            continue
        cppcheck.reportError(token, "style", "Empty while statement.")

Paul's avatar
Format  
Paul committed
239

240
241
242
@cppcheck.checker
def ForLoopShouldBeWhileLoop(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
243
244
        # if token.str != 'for':
            # continue
245
246
247
248
249
250
251
252
        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
253
254
255
        cppcheck.reportError(token, "style",
                             "For loop should be written as a while loop.")

256
257
258
259

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

Paul's avatar
Format  
Paul committed
264

265
266
267
268
269
270
271
272
273
274
275
276
# @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
277

278
279
280
@cppcheck.checker
def LambdaAttribute(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
281
282
        # if token.str != ']':
            # continue
Paul's avatar
Paul committed
283
        if not match(token, "] __device__|__host__ {|("):
284
            continue
Paul's avatar
Format  
Paul committed
285
286
287
288
        cppcheck.reportError(
            token, "style",
            "Attributes to lambdas must come after parameter list.")

289
290
291
292
293
294

@cppcheck.checker
def MultipleUnaryOperator(cfg, data):
    for token in cfg.tokenlist:
        if not token.isUnaryOp(token.str):
            continue
Paul's avatar
Paul committed
295
296
        if not token.str in ['+', '-', '~', '!']:
            continue
297
298
299
300
        if not match(token.astOperand1, "+|-|~|!"):
            continue
        if not token.astOperand1.isUnaryOp(token.astOperand1.str):
            continue
Paul's avatar
Format  
Paul committed
301
302
303
        cppcheck.reportError(token, "style",
                             "Muliple unary operators used together.")

304
305
306
307

@cppcheck.checker
def MutableVariable(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
308
309
        # if token.str != 'mutable':
            # continue
310
311
        if not match(token, "mutable %var%"):
            continue
Paul's avatar
Format  
Paul committed
312
313
314
        cppcheck.reportError(token, "style",
                             "Do not create mutable variables.")

315
316
317
318

@cppcheck.checker
def NestedBlocks(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
319
320
        if not token.str in ['if', 'while', 'for', 'switch']:
            continue
321
322
323
324
325
326
327
        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
328

329
330
331
@cppcheck.checker
def RedundantCast(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
332
333
        if not token.variable:
            continue
Paul's avatar
Format  
Paul committed
334
335
        m = match(token,
                  "%var%@decl ; %var%@assign = static_cast <*>@cast (*) ;")
336
337
338
339
        if not m:
            continue
        if m.decl.varId != m.assign.varId:
            continue
Paul's avatar
Paul committed
340
        if not simpleMatch(token.previous, "auto"):
Paul's avatar
Format  
Paul committed
341
342
343
            if not isTokensEqual(getVariableDecl(m.decl.variable),
                                 getInnerLink(m.cast),
                                 skip='const|volatile|&|&&|*'):
344
345
346
                continue
        cppcheck.reportError(token, "style", "Static cast is redundant.")

Paul's avatar
Format  
Paul committed
347

348
349
350
@cppcheck.checker
def RedundantConditionalOperator(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
351
352
        # if token.str != '?':
            # continue
353
354
        if not match(token, "? true|false : true|false"):
            continue
Paul's avatar
Format  
Paul committed
355
356
357
        cppcheck.reportError(token, "style",
                             "Conditional operator is redundant.")

358
359
360
361

@cppcheck.checker
def RedundantIfStatement(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
362
363
        # if token.str != 'if':
            # continue
Paul's avatar
Format  
Paul committed
364
365
366
        if not match(
                token,
                "if (*) { return true|false ; } else { return true|false ; }"):
367
368
369
            continue
        cppcheck.reportError(token, "style", "The if statement is redundant.")

Paul's avatar
Format  
Paul committed
370

371
372
373
@cppcheck.checker
def RedundantLocalVariable(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
374
375
        if not token.variable:
            continue
Paul's avatar
Format  
Paul committed
376
377
        m = match(token,
                  "%var%@decl ; %var%@assign = **; return %var%@returned ;")
378
379
380
381
382
383
        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
384
385
386
387
388
        cppcheck.reportError(
            m.returned, "style",
            "Variable is returned immediately after its declaration, can be simplified to just return expression."
        )

389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404

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

406
407
408
@cppcheck.checker
def UnnecessaryEmptyCondition(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
409
410
        # if token.str != 'if':
            # continue
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
        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
427
428
429
430
        cppcheck.reportError(
            container, "style",
            "Unnecessary check for empty before for range loop.")

431
432
433
434

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

Paul's avatar
Format  
Paul committed
439

440
441
@cppcheck.checker
def UseManagePointer(cfg, data):
Paul's avatar
Paul committed
442
    functions = {"fclose", "free", "hipFree", "hipHostFree", "hipFreeArray", "hipMemFree", "hipStreamDestroy", "hipEventDestroy", "hipArrayDestroy", "hipCtxDestroy", "hipDestroyTextureObject", "hipDestroySurfaceObject", "miirDestroyHandle"}
443
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
444
445
446
        if not isFunctionCall(token):
            continue
        if not token.str in functions:
447
            continue
Paul's avatar
Format  
Paul committed
448
449
450
        cppcheck.reportError(token, "style",
                             "Use manage pointer for resource management.")

451
452
453
454

@cppcheck.checker
def UseSmartPointer(cfg, data):
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
455
456
        # if token.str != 'new':
            # continue
457
458
        if not match(token, "new %name%"):
            continue
Paul's avatar
Format  
Paul committed
459
460
461
        cppcheck.reportError(token, "style",
                             "Use manage pointer for resource management.")

462
463
464

@cppcheck.checker
def useStlAlgorithms(cfg, data):
Paul's avatar
Paul committed
465
    copy_functions = {"memcpy", "strcpy", "strncpy", "strcat", "strncat"}
466
    for token in cfg.tokenlist:
Paul's avatar
Paul committed
467
468
469
        if not isFunctionCall(token):
            continue
        if token.str in copy_functions:
470
            cppcheck.reportError(token, "style", "Use std::copy instead.")
Paul's avatar
Paul committed
471
        elif token.str == 'memset':
472
            cppcheck.reportError(token, "style", "Use std::fill instead.")
Paul's avatar
Paul committed
473
        elif token.str == 'memcmp':
Paul's avatar
Format  
Paul committed
474
475
            cppcheck.reportError(token, "style",
                                 "Use std::equal_range instead.")
Paul's avatar
Paul committed
476
        elif token.str == 'memchr':
477
            cppcheck.reportError(token, "style", "Use std::find instead.")