46 if (!msg.callStack.empty()) {
47 ret.
setFileName(msg.callStack.back().getfile(
false));
67 return c > 0 && std::isalnum(c);
76 while (std::getline(istr, line))
77 filedata += line +
"\n";
78 std::replace(filedata.begin(), filedata.end(),
'\r',
'\n');
81 std::istringstream istr2(filedata);
82 while (std::getline(istr2, line)) {
88 if (line.length() > 1 && line[0] ==
'#')
90 if (line.length() >= 2 && line[0] ==
'/' && line[1] ==
'/')
104 tinyxml2::XMLDocument doc;
105 const tinyxml2::XMLError
error = doc.LoadFile(filename);
106 if (
error != tinyxml2::XML_SUCCESS)
107 return std::string(
"failed to load suppressions XML '") + filename +
"' (" + tinyxml2::XMLDocument::ErrorIDToName(
error) +
").";
109 const tinyxml2::XMLElement *
const rootnode = doc.FirstChildElement();
111 return std::string(
"failed to load suppressions XML '") + filename +
"' (no root node found).";
113 for (
const tinyxml2::XMLElement * e = rootnode->FirstChildElement(); e; e = e->NextSiblingElement()) {
114 if (std::strcmp(e->Name(),
"suppress") != 0)
115 return std::string(
"invalid suppression xml file '") + filename +
"', expected 'suppress' element but got a '" + e->Name() +
"'.";
118 for (
const tinyxml2::XMLElement * e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) {
119 const char *text = e2->GetText() ? e2->GetText() :
"";
120 if (std::strcmp(e2->Name(),
"id") == 0)
122 else if (std::strcmp(e2->Name(),
"fileName") == 0)
124 else if (std::strcmp(e2->Name(),
"lineNumber") == 0)
126 else if (std::strcmp(e2->Name(),
"symbolName") == 0)
128 else if (*text && std::strcmp(e2->Name(),
"hash") == 0)
129 s.
hash = strToInt<std::size_t>(text);
131 return std::string(
"unknown element '") + e2->Name() +
"' in suppressions XML '" + filename +
"', expected id/fileName/lineNumber/symbolName/hash.";
144 std::vector<Suppression> suppressions;
147 const std::string::size_type start_position = comment.find(
'[');
148 const std::string::size_type end_position = comment.find(
']', start_position);
149 if (end_position == std::string::npos) {
150 if (errorMessage && errorMessage->empty())
151 *errorMessage =
"Bad multi suppression '" + comment +
"'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]";
156 for (std::string::size_type pos = start_position; pos < end_position;) {
157 const std::string::size_type pos1 = pos + 1;
158 pos = comment.find(
',', pos1);
159 const std::string::size_type pos2 = (pos < end_position) ? pos : end_position;
164 std::istringstream iss(comment.substr(pos1, pos2-pos1));
168 if (errorMessage && errorMessage->empty())
169 *errorMessage =
"Bad multi suppression '" + comment +
"'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]";
170 suppressions.clear();
174 const std::string symbolNameString =
"symbolName=";
181 if (word.find_first_not_of(
"+-*/%#;") == std::string::npos)
184 s.
symbolName = word.substr(symbolNameString.size());
186 if (errorMessage && errorMessage->empty())
187 *errorMessage =
"Bad multi suppression '" + comment +
"'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]";
188 suppressions.clear();
193 suppressions.push_back(std::move(s));
201 std::istringstream lineStream;
205 std::string::size_type endpos = std::min(line.find(
'#'), line.find(
"//"));
206 if (endpos != std::string::npos) {
207 while (endpos > 0 && std::isspace(line[endpos-1])) {
210 lineStream.str(line.substr(0, endpos));
212 lineStream.str(line);
215 if (std::getline(lineStream, suppression.
errorId,
':')) {
216 if (std::getline(lineStream, suppression.
fileName)) {
222 const std::string::size_type pos = suppression.
fileName.rfind(
':');
225 if (pos != std::string::npos &&
226 suppression.
fileName.find(
'.', pos) == std::string::npos) {
229 std::istringstream istr1(suppression.
fileName.substr(pos+1));
255 foundSuppression->matched = suppression.
matched;
260 if (suppression.
errorId.empty() && suppression.
hash == 0)
261 return "Failed to add suppression. No id.";
263 for (std::string::size_type pos = 0; pos < suppression.
errorId.length(); ++pos) {
265 return "Failed to add suppression. Invalid id \"" + suppression.
errorId +
"\"";
267 if (pos == 0 && std::isdigit(suppression.
errorId[pos])) {
268 return "Failed to add suppression. Invalid id \"" + suppression.
errorId +
"\"";
273 return "Failed to add suppression. Invalid glob pattern '" + suppression.
errorId +
"'.";
275 return "Failed to add suppression. Invalid glob pattern '" + suppression.
fileName +
"'.";
284 for (
auto &newSuppression : suppressions) {
299 if (comment.size() < 2)
302 if (comment.find(
';') != std::string::npos)
303 comment.erase(comment.find(
';'));
305 if (comment.find(
"//", 2) != std::string::npos)
306 comment.erase(comment.find(
"//",2));
308 if (comment.compare(comment.size() - 2, 2,
"*/") == 0)
309 comment.erase(comment.size() - 2, 2);
311 const std::set<std::string> cppchecksuppress{
313 "cppcheck-suppress-begin",
314 "cppcheck-suppress-end",
315 "cppcheck-suppress-file",
316 "cppcheck-suppress-macro"
319 std::istringstream iss(comment.substr(2));
322 if (!cppchecksuppress.count(word))
329 const std::string symbolNameString =
"symbolName=";
335 if (word.find_first_not_of(
"+-*/%#;") == std::string::npos)
338 symbolName = word.substr(symbolNameString.size());
339 else if (errorMessage && errorMessage->empty())
340 *errorMessage =
"Bad suppression attribute '" + word +
"'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym";
347 if (hash > 0 && hash != errmsg.
hash)
358 if (!thisAndNextLine || lineNumber + 1 != errmsg.
lineNumber)
364 if (!symbolName.empty()) {
365 for (std::string::size_type pos = 0; pos < errmsg.
symbolNames.size();) {
366 const std::string::size_type pos2 = errmsg.
symbolNames.find(
'\n',pos);
368 if (pos2 == std::string::npos) {
395 if (!errorId.empty())
397 if (!fileName.empty())
398 ret +=
" fileName=" + fileName;
399 if (lineNumber != NO_LINE)
400 ret +=
" lineNumber=" + std::to_string(lineNumber);
401 if (!symbolName.empty())
402 ret +=
" symbolName=" + symbolName;
404 ret +=
" hash=" + std::to_string(hash);
406 return ret.substr(1);
412 const bool unmatchedSuppression(errmsg.
errorId ==
"unmatchedSuppression");
413 bool returnValue =
false;
415 if (!global && !s.isLocal())
417 if (unmatchedSuppression && s.errorId != errmsg.
errorId)
419 if (s.isMatch(errmsg))
428 if (!global && !s.isLocal())
430 if (s.errorId != errmsg.
errorId)
432 if (s.isMatch(errmsg))
447 out <<
" <suppressions>" << std::endl;
449 out <<
" <suppression";
451 if (!suppression.fileName.empty())
454 out <<
" lineNumber=\"" << suppression.lineNumber <<
'"';
455 if (!suppression.symbolName.empty())
457 if (suppression.hash > 0)
458 out <<
" hash=\"" << suppression.hash <<
'\"';
460 out <<
" lineBegin=\"" << suppression.lineBegin <<
'"';
462 out <<
" lineEnd=\"" << suppression.lineEnd <<
'"';
464 out <<
" type=\"file\"";
466 out <<
" type=\"block\"";
468 out <<
" type=\"blockBegin\"";
470 out <<
" type=\"blockEnd\"";
472 out <<
" type=\"macro\"";
473 out <<
" />" << std::endl;
475 out <<
" </suppressions>" << std::endl;
481 std::list<Suppression> result;
493 if (tmpFile.empty() || !s.isLocal() || s.fileName != tmpFile)
502 std::list<Suppression> result;
526 int currFileIdx = -1;
528 if (currFileIdx != tok->fileIndex() || currLineNr != tok->linenr()) {
529 currLineNr = tok->linenr();
530 currFileIdx = tok->fileIndex();
533 if (!suppression.checked && (suppression.lineNumber == currLineNr) && (suppression.fileName == tokenizer.
list.
file(tok))) {
534 suppression.checked =
true;
537 if ((!suppression.checked && (suppression.lineBegin <= currLineNr) && (suppression.lineEnd >= currLineNr) && (suppression.fileName == tokenizer.
list.
file(tok)))) {
538 suppression.checked =
true;
540 }
else if (!suppression.checked && suppression.fileName == tokenizer.
list.
file(tok)) {
541 suppression.checked =
true;
554 if (s.errorId ==
"unmatchedSuppression")
558 bool suppressed =
false;
560 if (s2.errorId ==
"unmatchedSuppression") {
561 if ((s2.fileName.empty() || s2.fileName ==
"*" || s2.fileName == s.fileName) &&
572 std::list<::ErrorMessage::FileLocation> callStack;
573 if (!s.fileName.empty())
574 callStack.emplace_back(s.fileName, s.lineNumber, 0);
This is an interface, which the class responsible of error logging should implement.
virtual void reportErr(const ErrorMessage &msg)=0
Information about found errors and warnings is directed here.
static std::string toxml(const std::string &str)
Convert XML-sensitive characters into XML entities.
static std::string simplifyPath(std::string originalPath)
Simplify path "foo/bar/.." => "foo".
static std::vector< Suppression > parseMultiSuppressComment(const std::string &comment, std::string *errorMessage)
Parse multi inline suppression in comment.
void dump(std::ostream &out) const
Create an xml dump of suppressions.
std::string addSuppressionLine(const std::string &line)
Don't show the given error.
void markUnmatchedInlineSuppressionsAsChecked(const Tokenizer &tokenizer)
Marks Inline Suppressions as checked if source line is in the token stream.
std::list< Suppression > getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const
Returns list of unmatched global (glob pattern) suppressions.
const std::list< Suppression > & getSuppressions() const
Returns list of all suppressions.
std::string addSuppression(Suppression suppression)
Don't show this error.
std::list< Suppression > mSuppressions
List of error which the user doesn't want to see.
std::string parseXmlFile(const char *filename)
Don't show errors listed in the file.
std::string addSuppressions(std::list< Suppression > suppressions)
Combine list of suppressions into the current suppressions.
bool isSuppressed(const ErrorMessage &errmsg, bool global=true)
Returns true if this message should not be shown to the user.
bool isSuppressedExplicitly(const ErrorMessage &errmsg, bool global=true)
Returns true if this message is "explicitly" suppressed.
static bool reportUnmatchedSuppressions(const std::list< SuppressionList::Suppression > &unmatched, ErrorLogger &errorLogger)
Report unmatched suppressions.
std::string parseFile(std::istream &istr)
Don't show errors listed in the file.
std::list< Suppression > getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const
Returns list of unmatched local (per-file) suppressions.
const std::string & file(const Token *tok) const
get filename for given token
The token list that the TokenList generates is a linked-list of this class.
The main purpose is to tokenize the source code.
const Token * tokens() const
TokenList list
Token list: stores all tokens.
static const std::string emptyString
static void replace(std::string &source, const std::unordered_map< std::string, std::string > &substitutionMap)
@ information
Checking information.
@ error
Programming error.
void setFileName(std::string s)
const std::string & getFileName() const
static SuppressionList::ErrorMessage fromErrorMessage(const ::ErrorMessage &msg, const std::set< std::string > ¯oNames)
std::set< std::string > macroNames
bool isSuppressed(const ErrorMessage &errmsg) const
bool isSameParameters(const Suppression &other) const
bool parseComment(std::string comment, std::string *errorMessage)
Parse inline suppression in comment.
std::string getText() const
bool isMatch(const ErrorMessage &errmsg)
static const char ID_CHECKERSREPORT[]
static const char ID_UNUSEDFUNCTION[]
static bool isAcceptedErrorIdChar(char c)
bool matchglob(const std::string &pattern, const std::string &name)
bool isValidGlobPattern(const std::string &pattern)
bool startsWith(const std::string &str, const char start[], std::size_t startlen)