cppcheck/addons/cppcheckdata.py

629 lines
19 KiB
Python
Raw Normal View History

2015-08-26 18:10:33 +02:00
## @mainpage cppcheckdata
#
# @brief This is a Python module that helps you access Cppcheck dump data.<br><br>
#
# License: No restrictions, use this as you need.<br><br>
#
import xml.etree.ElementTree as ET
import argparse
## Directive class. Contains information about each preprocessor directive in the source code.
#
# file and linenr denote the location where the directive is defined.
#
# To iterate through all directives use such code:
# @code
# data = cppcheckdata.parsedump(...)
# for cfg in data.configurations:
# for directive in cfg.directives:
# print(directive.str)
# @endcode
#
class Directive:
## The directive line, with all C or C++ comments removed
str = None
## name of (possibly included) file where directive is defined
file = None
## line number in (possibly included) file where directive is defined
linenr = None
def __init__(self, element):
self.str = element.get('str')
self.file = element.get('file')
self.linenr = element.get('linenr')
2015-08-26 18:10:33 +02:00
## Token class. Contains information about each token in the source code.
#
# The CppcheckData.tokenlist is a list of Token items
#
# C++ class: http://cppcheck.net/devinfo/doxyoutput/classToken.html
2015-08-26 18:10:33 +02:00
#
# To iterate through all tokens use such code:
# @code
# data = cppcheckdata.parsedump(...)
# for cfg in data.configurations:
# code = ''
# for token in cfg.tokenlist:
# code = code + token.str + ' '
# print(code)
2015-08-26 18:10:33 +02:00
# @endcode
#
class Token:
2014-07-21 16:10:04 +02:00
Id = None
2015-08-26 18:10:33 +02:00
## Token string
2014-07-21 16:10:04 +02:00
str = None
2015-08-26 18:10:33 +02:00
## Next token in tokenlist. For last token, next is None.
2014-07-21 16:10:04 +02:00
next = None
2015-08-26 18:10:33 +02:00
## Previous token in tokenlist. For first token, previous is None,
2014-07-21 16:10:04 +02:00
previous = None
2015-06-21 13:26:32 +02:00
linkId = None
2015-08-26 18:10:33 +02:00
## Linked token in tokenlist. Each '(', '[' and '{' are linked to the
2015-06-21 13:26:32 +02:00
# corresponding '}', ']' and ')'. For templates, the '<' is linked to
# the corresponding '>'.
link = None
2014-07-21 16:10:04 +02:00
scopeId = None
2015-08-26 18:10:33 +02:00
## Scope information for this token. See the Scope class.
2014-07-21 16:10:04 +02:00
scope = None
2015-08-26 18:10:33 +02:00
## Is this token a symbol name
2015-06-21 13:26:32 +02:00
isName = False
2015-08-26 18:10:33 +02:00
## Is this token a number, for example 123, 12.34
2015-06-21 13:26:32 +02:00
isNumber = False
2015-08-26 18:10:33 +02:00
## Is this token a int value such as 1234
2015-06-21 13:26:32 +02:00
isInt = False
2015-08-26 18:10:33 +02:00
## Is this token a int value such as 12.34
2015-06-21 13:26:32 +02:00
isFloat = False
2015-08-26 18:10:33 +02:00
## Is this token a string literal such as "hello"
2015-06-21 13:26:32 +02:00
isString = False
2015-08-26 18:10:33 +02:00
## string length for string literal
2014-07-21 16:10:04 +02:00
strlen = None
2015-08-26 18:10:33 +02:00
## Is this token a char literal such as 'x'
2015-06-21 13:26:32 +02:00
isChar = False
2015-08-26 18:10:33 +02:00
## Is this token a operator
2015-06-21 13:26:32 +02:00
isOp = False
2015-08-26 18:10:33 +02:00
## Is this token a arithmetic operator
2015-06-21 13:26:32 +02:00
isArithmeticalOp = False
2015-08-26 18:10:33 +02:00
## Is this token a assignment operator
2015-06-21 13:26:32 +02:00
isAssignmentOp = False
2015-08-26 18:10:33 +02:00
## Is this token a comparison operator
2015-06-21 13:26:32 +02:00
isComparisonOp = False
2015-08-26 18:10:33 +02:00
## Is this token a logical operator: && ||
2015-06-21 13:26:32 +02:00
isLogicalOp = False
2015-08-26 18:10:33 +02:00
## varId for token, each variable has a unique non-zero id
2014-07-21 16:10:04 +02:00
varId = None
variableId = None
2015-08-26 18:10:33 +02:00
## Variable information for this token. See the Variable class.
#
# Example code:
# @code
# data = cppcheckdata.parsedump(...)
2015-08-26 18:10:33 +02:00
# code = ''
# for token in data.tokenlist:
# code = code + token.str
# if token.variable:
# if token.variable.isLocal:
# code = code + ':localvar'
# if token.variable.isArgument:
# code = code + ':arg'
# code = code + ' '
# print(code)
# @endcode
2014-07-21 16:10:04 +02:00
variable = None
functionId = None
2015-08-26 18:10:33 +02:00
## If this token points at a function call, this attribute has the Function
2015-08-21 10:55:19 +02:00
# information. See the Function class.
2014-07-21 16:10:04 +02:00
function = None
valuesId = None
2015-08-26 18:10:33 +02:00
## Possible values of token
#
# Example code:
# @code
# data = cppcheckdata.parsedump(...)
2015-08-26 18:10:33 +02:00
# code = ''
# for token in data.tokenlist:
# code = code + token.str
# if token.values:
# # print values..
# code = code + '{'
# for value in token.values:
# if value.intvalue:
# code = code + str(value.intvalue) + ' '
# code = code + '}'
# code = code + ' '
# print(code)
# @endcode
2014-07-21 16:10:04 +02:00
values = None
2015-08-21 10:55:19 +02:00
typeScopeId = None
## type scope (token->type()->classScope)
typeScope = None
2014-07-21 16:10:04 +02:00
astParentId = None
2015-08-26 18:10:33 +02:00
## syntax tree parent
2014-07-21 16:10:04 +02:00
astParent = None
astOperand1Id = None
2015-08-26 18:10:33 +02:00
## syntax tree operand1
#
# Example code:
# @code
# data = cppcheckdata.parsedump(...)
2015-08-26 18:10:33 +02:00
# for token in data.tokenlist:
#
# # is this a addition?
# if token.str == '+':
#
# # print LHS operand
# print(token.astOperand1.str)
#
2015-08-26 18:10:33 +02:00
# @endcode
2014-07-21 16:10:04 +02:00
astOperand1 = None
astOperand2Id = None
2015-08-26 18:10:33 +02:00
## syntax tree operand2
#
# Example code:
# @code
# data = cppcheckdata.parsedump(...)
2015-08-26 18:10:33 +02:00
# for token in data.tokenlist:
#
# # is this a division?
# if token.str == '/':
#
# # print RHS operand
# print(token.astOperand2.str)
#
2015-08-26 18:10:33 +02:00
# @endcode
2014-07-21 16:10:04 +02:00
astOperand2 = None
2015-06-21 13:26:32 +02:00
2015-08-26 18:10:33 +02:00
## file name
2014-07-21 16:10:04 +02:00
file = None
2015-08-26 18:10:33 +02:00
## line number
2014-07-21 16:10:04 +02:00
linenr = None
def __init__(self, element):
2014-07-21 16:10:04 +02:00
self.Id = element.get('id')
self.str = element.get('str')
self.next = None
self.previous = None
self.scopeId = element.get('scope')
self.scope = None
type = element.get('type')
2014-07-21 12:44:53 +02:00
if type == 'name':
self.isName = True
elif type == 'number':
self.isNumber = True
if element.get('isInt'):
self.isInt = True
elif element.get('isFloat'):
self.isFloat = True
elif type == 'string':
self.isString = True
self.strlen = int(element.get('strlen'))
elif type == 'char':
self.isChar = True
elif type == 'op':
self.isOp = True
if element.get('isArithmeticalOp'):
self.isArithmeticalOp = True
elif element.get('isAssignmentOp'):
self.isAssignmentOp = True
elif element.get('isComparisonOp'):
self.isComparisonOp = True
elif element.get('isLogicalOp'):
self.isLogicalOp = True
2014-07-21 16:10:04 +02:00
self.linkId = element.get('link')
self.link = None
self.varId = element.get('varId')
self.variableId = element.get('variable')
self.variable = None
self.functionId = element.get('function')
self.function = None
self.valuesId = element.get('values')
self.values = None
self.typeScopeId = element.get('type-scope')
self.typeScope = None
2014-07-21 16:10:04 +02:00
self.astParentId = element.get('astParent')
self.astParent = None
self.astOperand1Id = element.get('astOperand1')
2014-07-21 16:10:04 +02:00
self.astOperand1 = None
self.astOperand2Id = element.get('astOperand2')
2014-07-21 16:10:04 +02:00
self.astOperand2 = None
self.file = element.get('file')
self.linenr = element.get('linenr')
def setId(self, IdMap):
2014-07-21 16:10:04 +02:00
self.scope = IdMap[self.scopeId]
self.link = IdMap[self.linkId]
self.variable = IdMap[self.variableId]
self.function = IdMap[self.functionId]
self.values = IdMap[self.valuesId]
self.typeScope = IdMap[self.typeScopeId]
2014-07-21 16:10:04 +02:00
self.astParent = IdMap[self.astParentId]
self.astOperand1 = IdMap[self.astOperand1Id]
self.astOperand2 = IdMap[self.astOperand2Id]
2015-08-26 18:10:33 +02:00
## Get value if it exists
# Returns None if it doesn't exist
2014-07-21 16:10:04 +02:00
def getValue(self, v):
if not self.values:
return None
for value in self.values:
if value.intvalue == v:
return value
return None
2015-08-26 18:10:33 +02:00
## Scope. Information about global scope, function scopes, class scopes, inner scopes, etc.
# C++ class: http://cppcheck.net/devinfo/doxyoutput/classScope.html
class Scope:
2014-07-21 16:10:04 +02:00
Id = None
classStartId = None
2015-08-26 18:10:33 +02:00
## The { Token for this scope
2014-07-21 16:10:04 +02:00
classStart = None
classEndId = None
2015-08-26 18:10:33 +02:00
## The } Token for this scope
2014-07-21 16:10:04 +02:00
classEnd = None
## Name of this scope.
# For a function scope, this is the function name;
# for a class scope, this is the class name.
2014-07-21 16:10:04 +02:00
className = None
2015-08-26 18:10:33 +02:00
## Type of scope: Global, Function, Class, If, While
2014-07-21 16:10:04 +02:00
type = None
def __init__(self, element):
self.Id = element.get('id')
self.className = element.get('className')
self.classStartId = element.get('classStart')
2014-07-21 16:10:04 +02:00
self.classStart = None
self.classEndId = element.get('classEnd')
self.classEnd = None
self.nestedInId = element.get('nestedId')
self.nestedIn = None
self.type = element.get('type')
def setId(self, IdMap):
self.classStart = IdMap[self.classStartId]
self.classEnd = IdMap[self.classEndId]
self.nestedIn = IdMap[self.nestedInId]
2015-08-26 18:10:33 +02:00
## Information about a function
# C++ class:
# http://cppcheck.net/devinfo/doxyoutput/classFunction.html
class Function:
2014-07-21 16:10:04 +02:00
Id = None
argument = None
argumentId = None
tokenDef = None
tokenDefId = None
2015-07-28 14:18:58 +02:00
name = None
2014-07-21 16:10:04 +02:00
def __init__(self, element):
self.Id = element.get('id')
self.tokenDefId = element.get('tokenDef')
2015-07-28 14:18:58 +02:00
self.name = element.get('name')
2014-07-21 16:10:04 +02:00
self.argument = {}
self.argumentId = {}
for arg in element:
self.argumentId[arg.get('nr')] = arg.get('id')
2014-07-21 16:10:04 +02:00
def setId(self, IdMap):
2014-12-22 16:03:43 +01:00
for argnr, argid in self.argumentId.items():
self.argument[argnr] = IdMap[argid]
self.tokenDef = IdMap[self.tokenDefId]
2014-07-21 16:10:04 +02:00
2015-08-26 18:10:33 +02:00
## Information about a variable
# C++ class:
# http://cppcheck.net/devinfo/doxyoutput/classVariable.html
class Variable:
2014-07-21 16:10:04 +02:00
Id = None
nameTokenId = None
## name token in variable declaration
2014-07-21 16:10:04 +02:00
nameToken = None
typeStartTokenId = None
2015-08-26 18:10:33 +02:00
## start token of variable declaration
2014-07-21 16:10:04 +02:00
typeStartToken = None
typeEndTokenId = None
2015-08-26 18:10:33 +02:00
## end token of variable declaration
2014-07-21 16:10:04 +02:00
typeEndToken = None
2015-08-26 18:10:33 +02:00
## Is this variable a function argument?
isArgument = False
2015-08-26 18:10:33 +02:00
## Is this variable an array?
isArray = False
2015-08-26 18:10:33 +02:00
## Is this variable a class or struct?
isClass = False
2015-08-26 18:10:33 +02:00
## Is this variable a local variable?
isLocal = False
2015-08-26 18:10:33 +02:00
## Is this variable a pointer
isPointer = False
2015-08-26 18:10:33 +02:00
## Is this variable a reference
isReference = False
2015-08-26 18:10:33 +02:00
## Is this variable static?
isStatic = False
def __init__(self, element):
2014-07-21 16:10:04 +02:00
self.Id = element.get('id')
self.nameTokenId = element.get('nameToken')
self.nameToken = None
self.typeStartTokenId = element.get('typeStartToken')
2014-07-21 16:10:04 +02:00
self.typeStartToken = None
self.typeEndTokenId = element.get('typeEndToken')
self.typeEndToken = None
self.isArgument = element.get('isArgument') == 'true'
self.isArray = element.get('isArray') == 'true'
self.isClass = element.get('isClass') == 'true'
self.isLocal = element.get('isLocal') == 'true'
self.isPointer = element.get('isPointer') == 'true'
self.isReference = element.get('isReference') == 'true'
self.isStatic = element.get('isStatic') == 'true'
def setId(self, IdMap):
self.nameToken = IdMap[self.nameTokenId]
self.typeStartToken = IdMap[self.typeStartTokenId]
self.typeEndToken = IdMap[self.typeEndTokenId]
2015-08-26 18:10:33 +02:00
## ValueFlow class
class ValueFlow:
2015-08-26 18:10:33 +02:00
## ValueFlow::Value class
# Each possible value has a ValueFlow::Value item.
# Each ValueFlow::Value either has a intvalue or tokvalue
# C++ class:
# http://cppcheck.net/devinfo/doxyoutput/classValueFlow_1_1Value.html
class Value:
2015-12-14 20:30:13 +01:00
## integer value
intvalue = None
## token value
tokvalue = None
## condition where this Value comes from
condition = None
2014-07-21 16:10:04 +02:00
def __init__(self, element):
self.intvalue = element.get('intvalue')
if self.intvalue:
self.intvalue = int(self.intvalue)
self.tokvalue = element.get('tokvalue')
self.condition = element.get('condition-line')
if self.condition:
self.condition = int(self.condition)
2014-07-21 16:10:04 +02:00
Id = None
2015-08-26 18:10:33 +02:00
## Possible values
values = None
2014-07-21 16:10:04 +02:00
def __init__(self, element):
2014-07-21 16:10:04 +02:00
self.Id = element.get('id')
self.values = []
for value in element:
self.values.append(ValueFlow.Value(value))
## Configuration class
# This class contains the directives, tokens, scopes, functions,
# variables and value flows for one configuration.
class Configuration:
## Name of the configuration, "" for default
name = ''
## List of Directive items
directives = []
2015-08-26 18:10:33 +02:00
## List of Token items
tokenlist = []
2015-08-26 18:10:33 +02:00
## List of Scope items
2014-07-21 16:10:04 +02:00
scopes = []
2015-08-26 18:10:33 +02:00
## List of Function items
functions = []
2015-08-26 18:10:33 +02:00
## List of Variable items
variables = []
2015-08-26 18:10:33 +02:00
## List of ValueFlow values
valueflow = []
def __init__(self, confignode):
self.name = confignode.get('cfg')
self.directives = []
self.tokenlist = []
2014-07-21 16:10:04 +02:00
self.scopes = []
self.functions = []
self.variables = []
self.valueflow = []
for element in confignode:
if element.tag == 'directivelist':
for directive in element:
self.directives.append(Directive(directive))
if element.tag == 'tokenlist':
for token in element:
self.tokenlist.append(Token(token))
# set next/previous..
prev = None
for token in self.tokenlist:
token.previous = prev
if prev:
prev.next = token
prev = token
if element.tag == 'scopes':
for scope in element:
self.scopes.append(Scope(scope))
for functionList in scope:
if functionList.tag == 'functionList':
for function in functionList:
self.functions.append(Function(function))
if element.tag == 'variables':
for variable in element:
self.variables.append(Variable(variable))
if element.tag == 'valueflow':
for values in element:
self.valueflow.append(ValueFlow(values))
IdMap = {}
IdMap[None] = None
2014-07-21 16:10:04 +02:00
IdMap['0'] = None
for token in self.tokenlist:
IdMap[token.Id] = token
for scope in self.scopes:
IdMap[scope.Id] = scope
for function in self.functions:
IdMap[function.Id] = function
for variable in self.variables:
IdMap[variable.Id] = variable
for values in self.valueflow:
IdMap[values.Id] = values.values
for token in self.tokenlist:
token.setId(IdMap)
for scope in self.scopes:
scope.setId(IdMap)
for function in self.functions:
function.setId(IdMap)
for variable in self.variables:
variable.setId(IdMap)
## Class that makes cppcheck dump data available
# Contains a list of Configuration instances
#
# To iterate through all configurations use such code:
# @code
# data = cppcheckdata.parsedump(...)
# for cfg in data.configurations:
# print('cfg: ' + cfg.name)
# @endcode
#
# To iterate through all tokens in each configuration use such code:
# @code
# data = cppcheckdata.parsedump(...)
# for cfg in data.configurations:
# print('cfg: ' + cfg.name)
# code = ''
# for token in cfg.tokenlist:
# code = code + token.str + ' '
# print(' ' + code)
# @endcode
#
# To iterate through all scopes (functions, types, etc) use such code:
# @code
# data = cppcheckdata.parsedump(...)
# for cfg in data.configurations:
# print('cfg: ' + cfg.name)
# for scope in cfg.scopes:
# print(' type:' + scope.type + ' name:' + scope.className)
# @endcode
#
#
class CppcheckData:
## List of Configurations
configurations = []
def __init__(self, filename):
self.configurations = []
data = ET.parse(filename)
# root is 'dumps' node, each config has its own 'dump' subnode.
for cfgnode in data.getroot():
self.configurations.append(Configuration(cfgnode))
2015-08-26 18:10:33 +02:00
## parse a cppcheck dump file
def parsedump(filename):
return CppcheckData(filename)
2015-08-26 18:10:33 +02:00
## Check if type of ast node is float/double
def astIsFloat(token):
if not token:
return False
if token.str == '.':
return astIsFloat(token.astOperand2)
if '+-*/%'.find(token.str) == 0:
if True == astIsFloat(token.astOperand1):
return True
return astIsFloat(token.astOperand2)
if not token.variable:
# float literal?
if token.str[0].isdigit():
for c in token.str:
2014-07-21 16:10:04 +02:00
if c == 'f' or c == '.' or c == 'E':
return True
return False
typeToken = token.variable.typeStartToken
endToken = token.variable.typeEndToken
while typeToken != endToken:
if typeToken.str == 'float' or typeToken.str == 'double':
return True
typeToken = typeToken.next
if typeToken.str == 'float' or typeToken.str == 'double':
return True
return False
# Create a cppcheck parser
class CppCheckFormatter(argparse.HelpFormatter):
'''
Properly formats multiline argument helps
'''
2016-07-23 13:44:05 +02:00
def _split_lines(self, text, width):
# this is the RawTextHelpFormatter._split_lines
if text.startswith('R|'):
2016-07-23 13:44:05 +02:00
return text[2:].splitlines()
return argparse.HelpFormatter._split_lines(self, text, width)
def ArgumentParser():
'''
Returns an argparse argument parser with an already-added
argument definition for -t/--template
'''
parser = argparse.ArgumentParser(formatter_class=CppCheckFormatter)
parser.add_argument('-t', '--template', metavar='<text>',
2016-07-23 13:44:05 +02:00
default='{callstack}: ({severity}) {message}',
help="R|Format the error messages. E.g.\n"
"'{file}:{line},{severity},{id},{message}' or\n"
"'{file}({line}):({severity}) {message}' or\n"
"'{callstack} {message}'\n"
"Pre-defined templates: gcc, vs, edit")
return parser
# Format an error message.
2016-07-23 13:44:05 +02:00
def reportError(template, callstack=[], severity='', message='', id=''):
'''
Format an error message according to the template.
2016-07-23 13:44:05 +02:00
:param template: format string, or 'gcc', 'vs' or 'edit'.
:param callstack: e.g. [['file1.cpp',10],['file2.h','20'], ... ]
:param severity: e.g. 'error', 'warning' ...
:param id: message ID.
:param message: message text.
'''
# expand predefined templates
if template == 'gcc':
template = '{file}:{line}: {severity}: {message}'
elif template == 'vs':
template = '{file}({line}): {severity}: {message}'
elif template == 'edit':
template = '{file} +{line}: {severity}: {message}'
# compute 'callstack}, {file} and {line} replacements
2016-07-23 13:44:05 +02:00
stack = ' -> '.join(['[' + f + ':' + str(l) + ']' for (f, l) in callstack])
file = callstack[-1][0]
line = str(callstack[-1][1])
# format message
return template.format(callstack=stack, file=file, line=line,
2016-07-23 13:44:05 +02:00
severity=severity, message=message, id=id)