cppcheck/addons/misra.py

171 lines
4.5 KiB
Python
Raw Normal View History

2017-04-08 16:08:54 +02:00
#/usr/bin/python
#
2017-04-11 14:45:38 +02:00
# MISRA C 2012 checkers
2017-04-08 16:08:54 +02:00
#
# Example usage of this addon (scan a sourcefile main.cpp)
# cppcheck --dump main.cpp
# python misra.py main.cpp.dump
#
# Limitations: This addon is released as open source. Rule texts can't be freely
# distributed. https://www.misra.org.uk/forum/viewtopic.php?f=56&t=1189
#
#
2017-04-08 16:08:54 +02:00
import cppcheckdata
import sys
import re
2017-04-11 22:21:54 +02:00
def reportError(token, num1, num2):
2017-04-08 16:08:54 +02:00
sys.stderr.write(
2017-04-11 22:21:54 +02:00
'[' + token.file + ':' + str(token.linenr) + '] misra ' + str(num1) + '.' + str(num2) + ' violation\n')
2017-04-08 16:08:54 +02:00
def hasSideEffects(expr):
if not expr:
return False
2017-04-08 19:00:50 +02:00
if expr.str in ['++', '--', '=']:
2017-04-08 16:08:54 +02:00
return True
# Todo: Check function calls
return hasSideEffects(expr.astOperand1) or hasSideEffects(expr.astOperand2)
2017-04-11 14:45:38 +02:00
def isBoolExpression(expr):
return expr and expr.str in ['!', '==', '!=', '<', '<=', '>', '>=', '&&', '||']
2017-04-11 22:21:54 +02:00
def getPrecedence(expr):
if not expr:
return 16
if not expr.astOperand1 or not expr.astOperand2:
return 16
if expr.str in ['*', '/', '%']:
return 12
if expr.str in ['+', '-']:
return 11
if expr.str in ['<<', '>>']:
return 10
if expr.str in ['<', '>', '<=', '>=']:
return 9
if expr.str in ['==', '!=']:
return 8
if expr.str == '&':
return 7
if expr.str == '^':
return 6
if expr.str == '|':
return 5
if expr.str == '&&':
return 4
if expr.str == '||':
return 3
if expr.str in ['?',':']:
return 2
if expr.isAssignmentOp:
return 1
if expr.str == ',':
return 0
return -1
def noParentheses(tok1, tok2):
while tok1 and tok1 != tok2:
if tok1.str == '(' or tok1.str == ')':
return False
tok1 = tok1.next
return tok1 == tok2
2017-04-11 14:45:38 +02:00
def misra_5_1(data):
2017-04-08 16:08:54 +02:00
for token in data.tokenlist:
if token.isName and len(token.str) > 31:
2017-04-11 22:21:54 +02:00
reportError(token, 5, 1)
2017-04-08 16:08:54 +02:00
2017-04-11 14:45:38 +02:00
def misra_7_1(rawTokens):
2017-04-09 10:11:54 +02:00
for tok in rawTokens:
2017-04-11 14:45:38 +02:00
if re.match(r'^0[0-7]+$', tok.str):
2017-04-11 22:21:54 +02:00
reportError(tok, 7, 1)
2017-04-08 16:08:54 +02:00
def misra_7_3(rawTokens):
for tok in rawTokens:
2017-04-12 19:07:10 +02:00
if re.match(r'^[0-9]+l', tok.str):
2017-04-11 22:21:54 +02:00
reportError(tok, 7, 3)
2017-04-12 16:19:13 +02:00
def misra_12_1_sizeof(rawTokens):
state = 0
for tok in rawTokens:
if tok.str.startswith('//') or tok.str.startswith('/*'):
continue
if tok.str == 'sizeof':
state = 1
elif state == 1:
if re.match(r'^[a-zA-Z_]',tok.str):
state = 2
else:
state = 0
elif state == 2:
if tok.str in ['+','-','*','/','%']:
reportError(tok, 12, 1)
else:
state = 0
2017-04-11 22:21:54 +02:00
def misra_12_1(data):
for token in data.tokenlist:
p = getPrecedence(token)
if p < 2 or p > 12:
continue
p1 = getPrecedence(token.astOperand1)
if p1 <= 12 and p1 > p and noParentheses(token.astOperand1,token):
reportError(token, 12, 1)
continue
p2 = getPrecedence(token.astOperand2)
if p2 <= 12 and p2 > p and noParentheses(token, token.astOperand2):
reportError(token, 12, 1)
continue
2017-04-12 20:18:54 +02:00
def misra_12_3(data):
for token in data.tokenlist:
if token.str != ',':
continue
if token.astParent and (token.astParent.str in ['(', ',', '{']):
2017-04-12 21:45:39 +02:00
continue
2017-04-12 20:18:54 +02:00
reportError(token, 12, 3)
2017-04-11 14:45:38 +02:00
def misra_13_5(data):
2017-04-08 16:08:54 +02:00
for token in data.tokenlist:
if token.isLogicalOp and hasSideEffects(token.astOperand2):
2017-04-11 22:21:54 +02:00
reportError(token, 13, 5)
2017-04-08 16:08:54 +02:00
2017-04-11 14:45:38 +02:00
def misra_14_4(data):
2017-04-08 16:08:54 +02:00
for token in data.tokenlist:
2017-04-11 14:45:38 +02:00
if token.str != '(':
2017-04-08 19:00:50 +02:00
continue
2017-04-11 14:45:38 +02:00
if not token.astOperand1 or not (token.astOperand1.str in ['if', 'while']):
continue
if not isBoolExpression(token.astOperand2):
2017-04-11 22:21:54 +02:00
reportError(token, 14, 4)
2017-04-08 16:08:54 +02:00
2017-04-11 14:45:38 +02:00
def misra_15_1(data):
2017-04-08 16:08:54 +02:00
for token in data.tokenlist:
2017-04-08 19:00:50 +02:00
if token.str == "goto":
2017-04-11 22:21:54 +02:00
reportError(token, 15, 1)
2017-04-08 16:08:54 +02:00
for arg in sys.argv[1:]:
print('Checking ' + arg + '...')
data = cppcheckdata.parsedump(arg)
2017-04-09 10:11:54 +02:00
cfgNumber = 0
2017-04-08 16:08:54 +02:00
for cfg in data.configurations:
2017-04-09 10:11:54 +02:00
cfgNumber = cfgNumber + 1
2017-04-08 16:08:54 +02:00
if len(data.configurations) > 1:
print('Checking ' + arg + ', config "' + cfg.name + '"...')
2017-04-11 14:45:38 +02:00
misra_5_1(cfg)
if cfgNumber == 1:
misra_7_1(data.rawTokens)
misra_7_3(data.rawTokens)
2017-04-12 16:19:13 +02:00
misra_12_1_sizeof(data.rawTokens)
2017-04-11 22:21:54 +02:00
misra_12_1(cfg)
2017-04-12 20:18:54 +02:00
misra_12_3(cfg)
2017-04-11 14:45:38 +02:00
misra_13_5(cfg)
misra_14_4(cfg)
misra_15_1(cfg)