From 44670005ea591a266009f608e66d1c480e2ee9e8 Mon Sep 17 00:00:00 2001 From: eivindt Date: Fri, 14 Jun 2019 12:06:57 +0200 Subject: [PATCH] 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 --- addons/cppcheckdata.py | 18 ++++++++++++++++++ addons/misra.py | 31 +++++++++++++++++++++---------- lib/cppcheck.cpp | 4 ++++ lib/standards.h | 26 ++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 10 deletions(-) diff --git a/addons/cppcheckdata.py b/addons/cppcheckdata.py index c8307797b..d5097937f 100755 --- a/addons/cppcheckdata.py +++ b/addons/cppcheckdata.py @@ -569,6 +569,9 @@ class Configuration: arguments = [] for element in confignode: + if element.tag == "standards": + self.standards = Standards(element) + if element.tag == 'directivelist': for directive in element: self.directives.append(Directive(directive)) @@ -660,6 +663,21 @@ class Platform: self.long_long_bit = int(platformnode.get('long_long_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: """ diff --git a/addons/misra.py b/addons/misra.py index 2ad3f64bc..df833bef7 100755 --- a/addons/misra.py +++ b/addons/misra.py @@ -591,7 +591,7 @@ class Rule: class MisraChecker: - def __init__(self): + def __init__(self, stdversion = "c90"): # Test validation rules lists self.verify_expected = list() @@ -625,6 +625,14 @@ class MisraChecker: # Statistics of all violations suppressed per rule 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): for token in rawTokens: if token.str.startswith('/*') or token.str.startswith('//'): @@ -733,6 +741,7 @@ class MisraChecker: def misra_5_3(self, data): + num_sign_chars = self.get_num_significant_naming_chars(data) enum = [] scopeVars = {} for var in data.variables: @@ -759,7 +768,7 @@ class MisraChecker: outerScope = outerScope.nestedIn continue 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: continue if int(innerVar.nameToken.linenr) > int(outerVar.nameToken.linenr): @@ -768,7 +777,7 @@ class MisraChecker: self.reportError(outerVar.nameToken, 5, 3) outerScope = outerScope.nestedIn 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): self.reportError(innerVar.nameToken, 5, 3) else: @@ -776,18 +785,19 @@ class MisraChecker: for e in enum: 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): self.reportError(innerVar.nameToken, 5, 3) else: self.reportError(innerScope.bodyStart, 5, 3) for e in enum: 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) def misra_5_4(self, data): + num_sign_chars = self.get_num_significant_naming_chars(data) macro = {} compile_name = re.compile(r'#define ([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: for i, macroparam1 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) for x, m_var1 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: self.reportError(m_var1, 5, 4) else: self.reportError(m_var2, 5, 4) 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: self.reportError(m_var1, 5, 4) else: @@ -826,6 +836,7 @@ class MisraChecker: def misra_5_5(self, data): + num_sign_chars = self.get_num_significant_naming_chars(data) macroNames = [] compiled = re.compile(r'#define ([A-Za-z0-9_]+)') for dir in data.directives: @@ -835,11 +846,11 @@ class MisraChecker: for var in data.variables: for macro in macroNames: 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) for scope in data.scopes: 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) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 3098baf80..9cce5c4de 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -473,6 +473,10 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string // dump xml if --dump if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) { fdump << "" << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; preprocessor.dump(fdump); mTokenizer.dump(fdump); fdump << "" << std::endl; diff --git a/lib/standards.h b/lib/standards.h index c90bc0032..ba238411f 100644 --- a/lib/standards.h +++ b/lib/standards.h @@ -56,6 +56,17 @@ struct Standards { } 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) { if (str == "c++03" || str == "C++03") { cpp = CPP03; @@ -79,6 +90,21 @@ struct Standards { } 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 ""; + } }; /// @}