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:
eivindt 2019-06-14 12:06:57 +02:00 committed by Daniel Marjamäki
parent 5dd7dacfd7
commit 44670005ea
4 changed files with 69 additions and 10 deletions

View File

@ -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:
""" """

View File

@ -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)

View File

@ -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;

View File

@ -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 "";
}
}; };
/// @} /// @}