From 995b496ddf474e1397034d6331967fe31fb16989 Mon Sep 17 00:00:00 2001 From: rebnridgway Date: Tue, 24 Apr 2018 21:19:24 +0100 Subject: [PATCH] Add suppressions to the XML dump (#1166) * Added parsing suppressions from dump xml. * Added code to dump suppressions to an xml file * Added declaration for dump function * Suppressions will now be written to the xml file when a dump is requested * Fixed syntax error * Removed excess whitespace * Fixed indentation to be consistent * Fixed indentation to be consistent * Fixed indentation to be consistent * Added missing include for ErrorLogger::toXml * Fixed suggestions from pull request #1166 Switched to using ranged for loop to iterate through suppressions. Made the line number attribute optional, rather than 0 if not specified. This means when Python deserialises it it will be None, which is more pythonic. * Implemented checking suppressions in reportError This modification expects suppressions and a function to be called to write a line of output to be passed in. The function checks if any of the suppressions match the warning (with the new Suppression.isMatch function) and if so returns None. This change maintains the old behaviour of returning the warning text, but adds the possibility of returning None if the warning was suppressed. * Fixed code quality warnings * Removed more extraneous whitespace --- addons/cppcheckdata.py | 53 ++++++++++++++++++++++++++++++++++++++---- lib/cppcheck.cpp | 3 +++ lib/suppressions.cpp | 20 ++++++++++++++++ lib/suppressions.h | 6 +++++ 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/addons/cppcheckdata.py b/addons/cppcheckdata.py index 9843f20b6..bd46b80cd 100755 --- a/addons/cppcheckdata.py +++ b/addons/cppcheckdata.py @@ -8,6 +8,7 @@ License: No restrictions, use this as you need. import xml.etree.ElementTree as ET import argparse +from fnmatch import fnmatch class Directive: @@ -450,12 +451,43 @@ class ValueFlow: for value in element: self.values.append(ValueFlow.Value(value)) +class Suppression: + """ + Suppression class + This class contains a suppression entry to suppress a warning. + + Attributes + errorId The id string of the error to suppress, can be a wildcard + fileName The name of the file to suppress warnings for, can include wildcards + lineNumber The number of the line to suppress warnings from, can be 0 to represent any line + symbolName The name of the symbol to match warnings for, can include wildcards + """ + + errorId = None + fileName = None + lineNumber = None + symbolName = None + + def __init__(self, element): + self.errorId = element.get('errorId') + self.fileName = element.get('fileName') + self.lineNumber = element.get('lineNumber') + self.symbolName = element.get('symbolName') + + def isMatch(file, line, message, errorId): + if (fnmatch(file, self.fileName) + and (self.lineNumber is None or line == self.lineNumber) + and fnmatch(message, '*'+self.symbolName+'*') + and fnmatch(errorId, self.errorId)): + return true + else: + return false class Configuration: """ Configuration class This class contains the directives, tokens, scopes, functions, - variables and value flows for one configuration. + variables, value flows, and suppressions for one configuration. Attributes: name Name of the configuration, "" for default @@ -465,6 +497,7 @@ class Configuration: functions List of Function items variables List of Variable items valueflow List of ValueFlow values + suppressions List of warning suppressions """ name = '' @@ -474,6 +507,7 @@ class Configuration: functions = [] variables = [] valueflow = [] + suppressions = [] def __init__(self, confignode): self.name = confignode.get('cfg') @@ -483,6 +517,7 @@ class Configuration: self.functions = [] self.variables = [] self.valueflow = [] + self.suppressions = [] arguments = [] for element in confignode: @@ -518,6 +553,9 @@ class Configuration: if element.tag == 'valueflow': for values in element: self.valueflow.append(ValueFlow(values)) + if element.tag == "suppressions": + for suppression in element: + self.suppressions.append(Suppression(suppression)) IdMap = {None: None, '0': None, '00000000': None, '0000000000000000': None} for token in self.tokenlist: @@ -711,7 +749,7 @@ def ArgumentParser(): return parser -def reportError(template, callstack=(), severity='', message='', id=''): +def reportError(template, callstack=(), severity='', message='', errorId='', suppressions=None, outputFunc=None): """ Format an error message according to the template. @@ -732,6 +770,13 @@ def reportError(template, callstack=(), severity='', message='', id=''): stack = ' -> '.join('[' + f + ':' + str(l) + ']' for (f, l) in callstack) file = callstack[-1][0] line = str(callstack[-1][1]) + + if suppressions is not None and any(suppression.isMatch(file, line, message, errorId) for suppression in suppressions): + return None + + outputLine = template.format(callstack=stack, file=file, line=line, + severity=severity, message=message, id=errorId) + if outputFunc is not None: + outputFunc(outputLine) # format message - return template.format(callstack=stack, file=file, line=line, - severity=severity, message=message, id=id) + return outputLine diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 33aa78883..50f72df61 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -225,6 +225,9 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string // Parse comments and then remove them preprocessor.inlineSuppressions(tokens1); + if (_settings.dump && fdump.is_open()) { + _settings.nomsg.dump(fdump); + } tokens1.removeComments(); preprocessor.removeComments(); diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index b452615eb..b16ca21af 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -18,6 +18,7 @@ #include "suppressions.h" +#include "errorlogger.h" #include "mathlib.h" #include "path.h" @@ -29,6 +30,8 @@ #include #include +class ErrorLogger; + static bool isValidGlobPattern(const std::string &pattern) { for (std::string::const_iterator i = pattern.begin(); i != pattern.end(); ++i) { @@ -274,6 +277,23 @@ bool Suppressions::isSuppressed(const Suppressions::ErrorMessage &errmsg) return false; } +void Suppressions::dump(std::ostream & out) +{ + out << " " << std::endl; + for (const Suppression &suppression : _suppressions) { + out << " 0) + out << " lineNumber=\"" << suppression.lineNumber << '"'; + if (!suppression.symbolName.empty()) + out << " symbolName=\"" << ErrorLogger::toxml(suppression.symbolName) << '\"'; + out << " />" << std::endl; + } + out << " " << std::endl; +} + std::list Suppressions::getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const { std::list result; diff --git a/lib/suppressions.h b/lib/suppressions.h index fc042c919..ac784fe99 100644 --- a/lib/suppressions.h +++ b/lib/suppressions.h @@ -135,6 +135,12 @@ public: */ bool isSuppressed(const ErrorMessage &errmsg); + /** + * @brief Create an xml dump of suppressions + * @param out stream to write XML to + */ + void dump(std::ostream &out); + /** * @brief Returns list of unmatched local (per-file) suppressions. * @return list of unmatched suppressions