Record C/CPP/Posix Standard used for cppcheck in dump file / use this for misra checking (#1782)
* Add cmd parameter for choosing between C90 and C99 Misra specifies different requirements to the uniqueness of macros/enums/variables depending on what C standard that's being used. * Add standards configuration to each dump file Read standards config from misra addon to decide what rules to use. * Posix as standard setting should be deprecated. Don't include this in the xml * Rewritten to use a switch
This commit is contained in:
parent
5dd7dacfd7
commit
44670005ea
|
@ -569,6 +569,9 @@ class Configuration:
|
||||||
arguments = []
|
arguments = []
|
||||||
|
|
||||||
for element in confignode:
|
for element in confignode:
|
||||||
|
if element.tag == "standards":
|
||||||
|
self.standards = Standards(element)
|
||||||
|
|
||||||
if element.tag == 'directivelist':
|
if element.tag == 'directivelist':
|
||||||
for directive in element:
|
for directive in element:
|
||||||
self.directives.append(Directive(directive))
|
self.directives.append(Directive(directive))
|
||||||
|
@ -660,6 +663,21 @@ class Platform:
|
||||||
self.long_long_bit = int(platformnode.get('long_long_bit'))
|
self.long_long_bit = int(platformnode.get('long_long_bit'))
|
||||||
self.pointer_bit = int(platformnode.get('pointer_bit'))
|
self.pointer_bit = int(platformnode.get('pointer_bit'))
|
||||||
|
|
||||||
|
class Standards:
|
||||||
|
"""
|
||||||
|
Standards class
|
||||||
|
This class contains versions of standards that were used for the cppcheck
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
c C Standard used
|
||||||
|
cpp C++ Standard used
|
||||||
|
posix If Posix was used
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, standardsnode):
|
||||||
|
self.c = standardsnode.find("c").get("version")
|
||||||
|
self.cpp = standardsnode.find("cpp").get("version")
|
||||||
|
self.posix = standardsnode.find("posix") != None
|
||||||
|
|
||||||
class CppcheckData:
|
class CppcheckData:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -591,7 +591,7 @@ class Rule:
|
||||||
|
|
||||||
class MisraChecker:
|
class MisraChecker:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, stdversion = "c90"):
|
||||||
|
|
||||||
# Test validation rules lists
|
# Test validation rules lists
|
||||||
self.verify_expected = list()
|
self.verify_expected = list()
|
||||||
|
@ -625,6 +625,14 @@ class MisraChecker:
|
||||||
# Statistics of all violations suppressed per rule
|
# Statistics of all violations suppressed per rule
|
||||||
self.suppressionStats = dict()
|
self.suppressionStats = dict()
|
||||||
|
|
||||||
|
self.stdversion = stdversion
|
||||||
|
|
||||||
|
def get_num_significant_naming_chars(self, cfg):
|
||||||
|
if cfg.standards and cfg.standards.c == "c99":
|
||||||
|
return 63
|
||||||
|
else:
|
||||||
|
return 31
|
||||||
|
|
||||||
def misra_3_1(self, rawTokens):
|
def misra_3_1(self, rawTokens):
|
||||||
for token in rawTokens:
|
for token in rawTokens:
|
||||||
if token.str.startswith('/*') or token.str.startswith('//'):
|
if token.str.startswith('/*') or token.str.startswith('//'):
|
||||||
|
@ -733,6 +741,7 @@ class MisraChecker:
|
||||||
|
|
||||||
|
|
||||||
def misra_5_3(self, data):
|
def misra_5_3(self, data):
|
||||||
|
num_sign_chars = self.get_num_significant_naming_chars(data)
|
||||||
enum = []
|
enum = []
|
||||||
scopeVars = {}
|
scopeVars = {}
|
||||||
for var in data.variables:
|
for var in data.variables:
|
||||||
|
@ -759,7 +768,7 @@ class MisraChecker:
|
||||||
outerScope = outerScope.nestedIn
|
outerScope = outerScope.nestedIn
|
||||||
continue
|
continue
|
||||||
for outerVar in scopeVars[outerScope]:
|
for outerVar in scopeVars[outerScope]:
|
||||||
if innerVar.nameToken.str[:31] == outerVar.nameToken.str[:31]:
|
if innerVar.nameToken.str[:num_sign_chars] == outerVar.nameToken.str[:num_sign_chars]:
|
||||||
if outerVar.isArgument and outerScope.type == "Global" and not innerVar.isArgument:
|
if outerVar.isArgument and outerScope.type == "Global" and not innerVar.isArgument:
|
||||||
continue
|
continue
|
||||||
if int(innerVar.nameToken.linenr) > int(outerVar.nameToken.linenr):
|
if int(innerVar.nameToken.linenr) > int(outerVar.nameToken.linenr):
|
||||||
|
@ -768,7 +777,7 @@ class MisraChecker:
|
||||||
self.reportError(outerVar.nameToken, 5, 3)
|
self.reportError(outerVar.nameToken, 5, 3)
|
||||||
outerScope = outerScope.nestedIn
|
outerScope = outerScope.nestedIn
|
||||||
for scope in data.scopes:
|
for scope in data.scopes:
|
||||||
if scope.className and innerVar.nameToken.str[:31] == scope.className[:31]:
|
if scope.className and innerVar.nameToken.str[:num_sign_chars] == scope.className[:num_sign_chars]:
|
||||||
if int(innerVar.nameToken.linenr) > int(scope.bodyStart.linenr):
|
if int(innerVar.nameToken.linenr) > int(scope.bodyStart.linenr):
|
||||||
self.reportError(innerVar.nameToken, 5, 3)
|
self.reportError(innerVar.nameToken, 5, 3)
|
||||||
else:
|
else:
|
||||||
|
@ -776,18 +785,19 @@ class MisraChecker:
|
||||||
|
|
||||||
for e in enum:
|
for e in enum:
|
||||||
for scope in data.scopes:
|
for scope in data.scopes:
|
||||||
if scope.className and innerVar.nameToken.str[:31] == e[:31]:
|
if scope.className and innerVar.nameToken.str[:num_sign_chars] == e[:num_sign_chars]:
|
||||||
if int(innerVar.nameToken.linenr) > int(innerScope.bodyStart.linenr):
|
if int(innerVar.nameToken.linenr) > int(innerScope.bodyStart.linenr):
|
||||||
self.reportError(innerVar.nameToken, 5, 3)
|
self.reportError(innerVar.nameToken, 5, 3)
|
||||||
else:
|
else:
|
||||||
self.reportError(innerScope.bodyStart, 5, 3)
|
self.reportError(innerScope.bodyStart, 5, 3)
|
||||||
for e in enum:
|
for e in enum:
|
||||||
for scope in data.scopes:
|
for scope in data.scopes:
|
||||||
if scope.className and scope.className[:31] == e[:31]:
|
if scope.className and scope.className[:num_sign_chars] == e[:num_sign_chars]:
|
||||||
self.reportError(scope.bodyStart, 5, 3)
|
self.reportError(scope.bodyStart, 5, 3)
|
||||||
|
|
||||||
|
|
||||||
def misra_5_4(self, data):
|
def misra_5_4(self, data):
|
||||||
|
num_sign_chars = self.get_num_significant_naming_chars(data)
|
||||||
macro = {}
|
macro = {}
|
||||||
compile_name = re.compile(r'#define ([a-zA-Z0-9_]+)')
|
compile_name = re.compile(r'#define ([a-zA-Z0-9_]+)')
|
||||||
compile_param = re.compile(r'#define ([a-zA-Z0-9_]+)[(]([a-zA-Z0-9_, ]+)[)]')
|
compile_param = re.compile(r'#define ([a-zA-Z0-9_]+)[(]([a-zA-Z0-9_, ]+)[)]')
|
||||||
|
@ -807,18 +817,18 @@ class MisraChecker:
|
||||||
if len(macro[mvar]["params"]) > 0:
|
if len(macro[mvar]["params"]) > 0:
|
||||||
for i, macroparam1 in enumerate(macro[mvar]["params"]):
|
for i, macroparam1 in enumerate(macro[mvar]["params"]):
|
||||||
for j, macroparam2 in enumerate(macro[mvar]["params"]):
|
for j, macroparam2 in enumerate(macro[mvar]["params"]):
|
||||||
if j > i and macroparam1[:31] == macroparam2[:31]:
|
if j > i and macroparam1[:num_sign_chars] == macroparam2[:num_sign_chars]:
|
||||||
self.reportError(mvar, 5, 4)
|
self.reportError(mvar, 5, 4)
|
||||||
|
|
||||||
for x, m_var1 in enumerate(macro):
|
for x, m_var1 in enumerate(macro):
|
||||||
for y, m_var2 in enumerate(macro):
|
for y, m_var2 in enumerate(macro):
|
||||||
if x < y and macro[m_var1]["name"][:31] == macro[m_var2]["name"][:31]:
|
if x < y and macro[m_var1]["name"][:num_sign_chars] == macro[m_var2]["name"][:num_sign_chars]:
|
||||||
if m_var1.linenr > m_var2.linenr:
|
if m_var1.linenr > m_var2.linenr:
|
||||||
self.reportError(m_var1, 5, 4)
|
self.reportError(m_var1, 5, 4)
|
||||||
else:
|
else:
|
||||||
self.reportError(m_var2, 5, 4)
|
self.reportError(m_var2, 5, 4)
|
||||||
for param in macro[m_var2]["params"]:
|
for param in macro[m_var2]["params"]:
|
||||||
if macro[m_var1]["name"][:31] == param[:31]:
|
if macro[m_var1]["name"][:num_sign_chars] == param[:num_sign_chars]:
|
||||||
if m_var1.linenr > m_var2.linenr:
|
if m_var1.linenr > m_var2.linenr:
|
||||||
self.reportError(m_var1, 5, 4)
|
self.reportError(m_var1, 5, 4)
|
||||||
else:
|
else:
|
||||||
|
@ -826,6 +836,7 @@ class MisraChecker:
|
||||||
|
|
||||||
|
|
||||||
def misra_5_5(self, data):
|
def misra_5_5(self, data):
|
||||||
|
num_sign_chars = self.get_num_significant_naming_chars(data)
|
||||||
macroNames = []
|
macroNames = []
|
||||||
compiled = re.compile(r'#define ([A-Za-z0-9_]+)')
|
compiled = re.compile(r'#define ([A-Za-z0-9_]+)')
|
||||||
for dir in data.directives:
|
for dir in data.directives:
|
||||||
|
@ -835,11 +846,11 @@ class MisraChecker:
|
||||||
for var in data.variables:
|
for var in data.variables:
|
||||||
for macro in macroNames:
|
for macro in macroNames:
|
||||||
if var.nameToken is not None:
|
if var.nameToken is not None:
|
||||||
if var.nameToken.str[:31] == macro[:31]:
|
if var.nameToken.str[:num_sign_chars] == macro[:num_sign_chars]:
|
||||||
self.reportError(var.nameToken, 5, 5)
|
self.reportError(var.nameToken, 5, 5)
|
||||||
for scope in data.scopes:
|
for scope in data.scopes:
|
||||||
for macro in macroNames:
|
for macro in macroNames:
|
||||||
if scope.className and scope.className[:31] == macro[:31]:
|
if scope.className and scope.className[:num_sign_chars] == macro[:num_sign_chars]:
|
||||||
self.reportError(scope.bodyStart, 5, 5)
|
self.reportError(scope.bodyStart, 5, 5)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -473,6 +473,10 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
|
||||||
// dump xml if --dump
|
// dump xml if --dump
|
||||||
if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) {
|
if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) {
|
||||||
fdump << "<dump cfg=\"" << ErrorLogger::toxml(mCurrentConfig) << "\">" << std::endl;
|
fdump << "<dump cfg=\"" << ErrorLogger::toxml(mCurrentConfig) << "\">" << std::endl;
|
||||||
|
fdump << " <standards>" << std::endl;
|
||||||
|
fdump << " <c version=\"" << mSettings.standards.getC() << "\"/>" << std::endl;
|
||||||
|
fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>" << std::endl;
|
||||||
|
fdump << " </standards>" << std::endl;
|
||||||
preprocessor.dump(fdump);
|
preprocessor.dump(fdump);
|
||||||
mTokenizer.dump(fdump);
|
mTokenizer.dump(fdump);
|
||||||
fdump << "</dump>" << std::endl;
|
fdump << "</dump>" << std::endl;
|
||||||
|
|
|
@ -56,6 +56,17 @@ struct Standards {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const std::string getC(void) const {
|
||||||
|
switch (c) {
|
||||||
|
case C89:
|
||||||
|
return "c89";
|
||||||
|
case C99:
|
||||||
|
return "c99";
|
||||||
|
case C11:
|
||||||
|
return "c11";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
bool setCPP(const std::string& str) {
|
bool setCPP(const std::string& str) {
|
||||||
if (str == "c++03" || str == "C++03") {
|
if (str == "c++03" || str == "C++03") {
|
||||||
cpp = CPP03;
|
cpp = CPP03;
|
||||||
|
@ -79,6 +90,21 @@ struct Standards {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const std::string getCPP(void) const {
|
||||||
|
switch (cpp) {
|
||||||
|
case CPP03:
|
||||||
|
return "c++03";
|
||||||
|
case CPP11:
|
||||||
|
return "c++11";
|
||||||
|
case CPP14:
|
||||||
|
return "c++14";
|
||||||
|
case CPP17:
|
||||||
|
return "c++17";
|
||||||
|
case CPP20:
|
||||||
|
return "c++20";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
Loading…
Reference in New Issue