diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index f2db1c8d0..aa0d06c93 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -76,8 +76,9 @@ Preprocessor::~Preprocessor() namespace { struct BadInlineSuppression { - BadInlineSuppression(const simplecpp::Location &l, std::string msg) : location(l), errmsg(std::move(msg)) {} - simplecpp::Location location; + BadInlineSuppression(std::string file, const int line, std::string msg) : file(std::move(file)), line(line), errmsg(std::move(msg)) {} + std::string file; + int line; std::string errmsg; }; } @@ -97,18 +98,50 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std: if (comment.substr(pos1, cppchecksuppress.size()) != cppchecksuppress) return false; - // skip spaces after "cppcheck-suppress" - const std::string::size_type pos2 = comment.find_first_not_of(' ', pos1+cppchecksuppress.size()); + // check if it has a prefix + const std::string::size_type posEndComment = comment.find_first_of(" [", pos1+cppchecksuppress.size()); + + // skip spaces after "cppcheck-suppress" and its possible prefix + const std::string::size_type pos2 = comment.find_first_not_of(' ', posEndComment); if (pos2 == std::string::npos) return false; + Suppressions::Type errorType = Suppressions::Type::unique; + + // determine prefix if specified + if (posEndComment >= (pos1 + cppchecksuppress.size() + 1)) { + if (comment.at(pos1 + cppchecksuppress.size()) != '-') + return false; + + const unsigned int argumentLength = + posEndComment - (pos1 + cppchecksuppress.size() + 1); + + const std::string suppressTypeString = + comment.substr(pos1 + cppchecksuppress.size() + 1, argumentLength); + + if ("file" == suppressTypeString) { + errorType = Suppressions::Type::file; + } else if ("begin" == suppressTypeString) { + errorType = Suppressions::Type::blockBegin; + } else if ("end" == suppressTypeString) { + errorType = Suppressions::Type::blockEnd; + } else { + return false; + } + } + if (comment[pos2] == '[') { // multi suppress format std::string errmsg; std::vector suppressions = Suppressions::parseMultiSuppressComment(comment, &errmsg); + for (Suppressions::Suppression &s : suppressions) { + s.type = errorType; + s.lineNumber = tok->location.line; + } + if (!errmsg.empty()) - bad.emplace_back(tok->location, std::move(errmsg)); + bad.emplace_back(tok->location.file(), tok->location.line, std::move(errmsg)); std::copy_if(suppressions.cbegin(), suppressions.cend(), std::back_inserter(inlineSuppressions), [](const Suppressions::Suppression& s) { return !s.errorId.empty(); @@ -120,21 +153,30 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std: if (!s.parseComment(comment, &errmsg)) return false; + s.type = errorType; + s.lineNumber = tok->location.line; + if (!s.errorId.empty()) inlineSuppressions.push_back(std::move(s)); if (!errmsg.empty()) - bad.emplace_back(tok->location, std::move(errmsg)); + bad.emplace_back(tok->location.file(), tok->location.line, std::move(errmsg)); } return true; } -static void addinlineSuppressions(const simplecpp::TokenList &tokens, const Settings &settings, Suppressions &suppressions, std::list &bad) +static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Settings &settings, Suppressions &suppressions, std::list &bad) { + std::list inlineSuppressionsBlockBegin; + + bool onlyComments = true; + for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { - if (!tok->comment) + if (!tok->comment) { + onlyComments = false; continue; + } std::list inlineSuppressions; if (!parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad)) @@ -142,18 +184,27 @@ static void addinlineSuppressions(const simplecpp::TokenList &tokens, const Sett if (!sameline(tok->previous, tok)) { // find code after comment.. - tok = tok->next; - while (tok && tok->comment) { - parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad); + if (tok->next) { tok = tok->next; + + while (tok->comment) { + parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad); + if (tok->next) { + tok = tok->next; + } else { + break; + } + } } - if (!tok) - break; } if (inlineSuppressions.empty()) continue; + // It should never happen + if (!tok) + continue; + // Relative filename std::string relativeFilename(tok->location.file()); if (settings.relativePaths) { @@ -166,23 +217,66 @@ static void addinlineSuppressions(const simplecpp::TokenList &tokens, const Sett } relativeFilename = Path::simplifyPath(relativeFilename); - // special handling when suppressing { warnings for backwards compatibility - const bool thisAndNextLine = tok->previous && - tok->previous->previous && - tok->next && - !sameline(tok->previous->previous, tok->previous) && - tok->location.line + 1 == tok->next->location.line && - tok->location.fileIndex == tok->next->location.fileIndex && - tok->previous->str() == "{"; - // Add the suppressions. for (Suppressions::Suppression &suppr : inlineSuppressions) { suppr.fileName = relativeFilename; - suppr.lineNumber = tok->location.line; - suppr.thisAndNextLine = thisAndNextLine; - suppressions.addSuppression(std::move(suppr)); + + if (Suppressions::Type::blockBegin == suppr.type) + { + inlineSuppressionsBlockBegin.push_back(std::move(suppr)); + } else if (Suppressions::Type::blockEnd == suppr.type) { + bool throwError = true; + + if (!inlineSuppressionsBlockBegin.empty()) { + const Suppressions::Suppression lastBeginSuppression = inlineSuppressionsBlockBegin.back(); + + for (const Suppressions::Suppression &supprBegin : inlineSuppressionsBlockBegin) + { + if (lastBeginSuppression.lineNumber != supprBegin.lineNumber) + continue; + + if (suppr.symbolName == supprBegin.symbolName && suppr.lineNumber > supprBegin.lineNumber) { + suppr.lineBegin = supprBegin.lineNumber; + suppr.lineEnd = suppr.lineNumber; + suppr.lineNumber = supprBegin.lineNumber; + suppr.type = Suppressions::Type::block; + inlineSuppressionsBlockBegin.remove(supprBegin); + suppressions.addSuppression(std::move(suppr)); + throwError = false; + break; + } + } + } + + if (throwError) { + // NOLINTNEXTLINE(bugprone-use-after-move) - moved only when thrownError is false + bad.emplace_back(suppr.fileName, suppr.lineNumber, "Suppress End: No matching begin"); + } + } else if (Suppressions::Type::unique == suppr.type) { + // special handling when suppressing { warnings for backwards compatibility + const bool thisAndNextLine = tok->previous && + tok->previous->previous && + tok->next && + !sameline(tok->previous->previous, tok->previous) && + tok->location.line + 1 == tok->next->location.line && + tok->location.fileIndex == tok->next->location.fileIndex && + tok->previous->str() == "{"; + + suppr.thisAndNextLine = thisAndNextLine; + suppr.lineNumber = tok->location.line; + suppressions.addSuppression(std::move(suppr)); + } else if (Suppressions::Type::file == suppr.type) { + if (onlyComments) + suppressions.addSuppression(std::move(suppr)); + else + bad.emplace_back(suppr.fileName, suppr.lineNumber, "File suppression should be at the top of the file"); + } } } + + std::for_each(inlineSuppressionsBlockBegin.begin(), inlineSuppressionsBlockBegin.end(), [&](const Suppressions::Suppression & suppr) { + bad.emplace_back(suppr.fileName, suppr.lineNumber, "Suppress Begin: No matching end"); + }); } void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens, Suppressions &suppressions) @@ -190,13 +284,13 @@ void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens, Suppre if (!mSettings.inlineSuppressions) return; std::list err; - ::addinlineSuppressions(tokens, mSettings, suppressions, err); + ::addInlineSuppressions(tokens, mSettings, suppressions, err); for (std::map::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) { if (it->second) - ::addinlineSuppressions(*it->second, mSettings, suppressions, err); + ::addInlineSuppressions(*it->second, mSettings, suppressions, err); } for (const BadInlineSuppression &bad : err) { - error(bad.location.file(), bad.location.line, bad.errmsg); + error(bad.file, bad.line, bad.errmsg); } } diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index e86c4ee74..288b9ea36 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -165,6 +165,8 @@ std::vector Suppressions::parseMultiSuppressComment(c return suppressions; } + const std::string SymbolNameString = "symbolName="; + while (iss) { std::string word; iss >> word; @@ -172,8 +174,8 @@ std::vector Suppressions::parseMultiSuppressComment(c break; if (word.find_first_not_of("+-*/%#;") == std::string::npos) break; - if (startsWith(word, "symbolName=")) { - s.symbolName = word.substr(11); + if (startsWith(word, SymbolNameString)) { + s.symbolName = word.substr(SymbolNameString.size()); } else { if (errorMessage && errorMessage->empty()) *errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]"; @@ -302,22 +304,28 @@ bool Suppressions::Suppression::parseComment(std::string comment, std::string *e if (comment.compare(comment.size() - 2, 2, "*/") == 0) comment.erase(comment.size() - 2, 2); + const std::string cppchecksuppress = "cppcheck-suppress"; + std::istringstream iss(comment.substr(2)); std::string word; iss >> word; - if (word != "cppcheck-suppress") + if (word.substr(0, cppchecksuppress.size()) != cppchecksuppress) return false; + iss >> errorId; if (!iss) return false; + + const std::string SymbolNameString = "symbolName="; + while (iss) { iss >> word; if (!iss) break; if (word.find_first_not_of("+-*/%#;") == std::string::npos) break; - if (startsWith(word,"symbolName=")) - symbolName = word.substr(11); + if (startsWith(word, SymbolNameString)) + symbolName = word.substr(SymbolNameString.size()); else if (errorMessage && errorMessage->empty()) *errorMessage = "Bad suppression attribute '" + word + "'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym"; } @@ -332,10 +340,12 @@ bool Suppressions::Suppression::isSuppressed(const Suppressions::ErrorMessage &e return false; if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName())) return false; - if (lineNumber != NO_LINE && lineNumber != errmsg.lineNumber) { + if ((Suppressions::Type::unique == type) && (lineNumber != NO_LINE) && (lineNumber != errmsg.lineNumber)) { if (!thisAndNextLine || lineNumber + 1 != errmsg.lineNumber) return false; } + if ((Suppressions::Type::block == type) && ((errmsg.lineNumber < lineBegin) || (errmsg.lineNumber > lineEnd))) + return false; if (!symbolName.empty()) { for (std::string::size_type pos = 0; pos < errmsg.symbolNames.size();) { const std::string::size_type pos2 = errmsg.symbolNames.find('\n',pos); @@ -385,15 +395,16 @@ std::string Suppressions::Suppression::getText() const bool Suppressions::isSuppressed(const Suppressions::ErrorMessage &errmsg, bool global) { const bool unmatchedSuppression(errmsg.errorId == "unmatchedSuppression"); + bool return_value = false; for (Suppression &s : mSuppressions) { if (!global && !s.isLocal()) continue; if (unmatchedSuppression && s.errorId != errmsg.errorId) continue; if (s.isMatch(errmsg)) - return true; + return_value = true; } - return false; + return return_value; } bool Suppressions::isSuppressed(const ::ErrorMessage &errmsg) @@ -470,7 +481,15 @@ void Suppressions::markUnmatchedInlineSuppressionsAsChecked(const Tokenizer &tok currLineNr = tok->linenr(); currFileIdx = tok->fileIndex(); for (auto &suppression : mSuppressions) { - if (!suppression.checked && (suppression.lineNumber == currLineNr) && (suppression.fileName == tokenizer.list.file(tok))) { + if (suppression.type == Suppressions::Type::unique) { + if (!suppression.checked && (suppression.lineNumber == currLineNr) && (suppression.fileName == tokenizer.list.file(tok))) { + suppression.checked = true; + } + } else if (suppression.type == Suppressions::Type::block) { + if ((!suppression.checked && (suppression.lineBegin <= currLineNr) && (suppression.lineEnd >= currLineNr) && (suppression.fileName == tokenizer.list.file(tok)))) { + suppression.checked = true; + } + } else if (!suppression.checked && suppression.fileName == tokenizer.list.file(tok)) { suppression.checked = true; } } diff --git a/lib/suppressions.h b/lib/suppressions.h index 6e1fd9c9e..53f3b6e7f 100644 --- a/lib/suppressions.h +++ b/lib/suppressions.h @@ -41,6 +41,10 @@ enum class Certainty; class CPPCHECKLIB Suppressions { public: + enum class Type { + unique, file, block, blockBegin, blockEnd + }; + struct CPPCHECKLIB ErrorMessage { std::size_t hash; std::string errorId; @@ -77,6 +81,26 @@ public: return false; } + bool operator==(const Suppression &other) const { + if (errorId != other.errorId) + return false; + if (lineNumber < other.lineNumber) + return false; + if (fileName != other.fileName) + return false; + if (symbolName != other.symbolName) + return false; + if (hash != other.hash) + return false; + if (type != other.type) + return false; + if (lineBegin != other.lineBegin) + return false; + if (lineEnd != other.lineEnd) + return false; + return true; + } + /** * Parse inline suppression in comment * @param comment the full comment text @@ -107,6 +131,9 @@ public: std::string errorId; std::string fileName; int lineNumber = NO_LINE; + int lineBegin = NO_LINE; + int lineEnd = NO_LINE; + Type type = Type::unique; std::string symbolName; std::size_t hash{}; bool thisAndNextLine{}; // Special case for backwards compatibility: { // cppcheck-suppress something diff --git a/lib/utils.h b/lib/utils.h index 2ac0df275..73aec4aa1 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -91,6 +91,11 @@ bool startsWith(const std::string& str, const char (&start)[N]) return startsWith(str, start, N - 1); } +inline bool startsWith(const std::string& str, const std::string& start) +{ + return startsWith(str, start.c_str(), start.length()); +} + inline bool endsWith(const std::string &str, char c) { return !str.empty() && str.back() == c; diff --git a/man/manual.md b/man/manual.md index 4829c6986..7f4b1210c 100644 --- a/man/manual.md +++ b/man/manual.md @@ -531,6 +531,26 @@ Suppressing multiple ids in one comment by using []: // cppcheck-suppress [aaaa, bbbb] +Suppressing warnings `aaaa` on a block of code: + + // cppcheck-suppress-begin aaaa + ... + // cppcheck-suppress-end aaaa + +Suppressing multiple ids on a block of code: + + // cppcheck-suppress-begin [aaaa, bbbb] + ... + // cppcheck-suppress-end [aaaa, bbbb] + +Suppressing warnings `aaaa` for a whole file: + + // cppcheck-suppress-file aaaa + +Suppressing multiple ids for a whole file: + + // cppcheck-suppress-file [aaaa, bbbb] + ### Comment before code or on same line The comment can be put before the code or at the same line as the code. diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index ddb4c6860..e1921d5e3 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -277,7 +277,7 @@ private: #endif void runChecks(unsigned int (TestSuppressions::*check)(const char[], const std::string &)) { - // check to make sure the appropriate error is present + // check to make sure the appropriate errors are present ASSERT_EQUALS(1, (this->*check)("void f() {\n" " int a;\n" " a++;\n" @@ -285,6 +285,16 @@ private: "")); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); + ASSERT_EQUALS(1, (this->*check)("void f() {\n" + " int a;\n" + " a++;\n" + " int b;\n" + " b++;\n" + "}\n", + "")); + ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n" + "[test.cpp:5]: (error) Uninitialized variable: b\n", errout.str()); + // suppress uninitvar globally ASSERT_EQUALS(0, (this->*check)("void f() {\n" " int a;\n" @@ -293,6 +303,59 @@ private: "uninitvar")); ASSERT_EQUALS("", errout.str()); + (this->*check)("void f() {\n" + " // cppcheck-suppress-file uninitvar\n" + " int a;\n" + " a++;\n" + "}\n", + ""); + ASSERT_EQUALS("[test.cpp:2]: (error) File suppression should be at the top of the file\n" + "[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); + + (this->*check)("void f() {\n" + " int a;\n" + " a++;\n" + "}\n" + "// cppcheck-suppress-file uninitvar\n", + ""); + ASSERT_EQUALS("[test.cpp:5]: (error) File suppression should be at the top of the file\n" + "[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); + + ASSERT_EQUALS(0, (this->*check)("// cppcheck-suppress-file uninitvar\n" + "void f() {\n" + " int a;\n" + " a++;\n" + " int b;\n" + " b++;\n" + "}\n", + "")); + ASSERT_EQUALS("", errout.str()); + + ASSERT_EQUALS(0, (this->*check)("/* Fake file description\n" + " * End\n" + " */\n" + "\n" + "// cppcheck-suppress-file uninitvar\n" + "\n" + "void f() {\n" + " int a;\n" + " a++;\n" + " int b;\n" + " b++;\n" + "}\n", + "")); + ASSERT_EQUALS("", errout.str()); + + (this->*check)("// cppcheck-suppress-file uninitvar\n" + "void f() {\n" + " int a;\n" + " a++;\n" + " int b;\n" + " b++;\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + // suppress uninitvar globally, without error present ASSERT_EQUALS(0, (this->*check)("void f() {\n" " int a;\n" @@ -301,6 +364,14 @@ private: "uninitvar")); ASSERT_EQUALS("(information) Unmatched suppression: uninitvar\n", errout.str()); + (this->*check)("// cppcheck-suppress-file uninitvar\n" + "void f() {\n" + " int a;\n" + " b++;\n" + "}\n", + ""); + ASSERT_EQUALS("[test.cpp:1]: (information) Unmatched suppression: uninitvar\n", errout.str()); + // suppress uninitvar for this file only ASSERT_EQUALS(0, (this->*check)("void f() {\n" " int a;\n" @@ -479,6 +550,222 @@ private: ""); ASSERT_EQUALS("[test.cpp:4]: (information) Unmatched suppression: uninitvar\n", errout.str()); + // suppress block inline checks + ASSERT_EQUALS(0, (this->*check)("void f() {\n" + " // cppcheck-suppress-begin uninitvar\n" + " int a;\n" + " a++;\n" + " int b;\n" + " b++;\n" + " // cppcheck-suppress-end uninitvar\n" + "}\n", + "")); + ASSERT_EQUALS("", errout.str()); + + ASSERT_EQUALS(1, (this->*check)("void f() {\n" + " // cppcheck-suppress-begin uninitvar\n" + " int a;\n" + " a++;\n" + " int b;\n" + " b++;\n" + "}\n", + "")); + ASSERT_EQUALS("[test.cpp:2]: (error) Suppress Begin: No matching end\n" + "[test.cpp:4]: (error) Uninitialized variable: a\n" + "[test.cpp:6]: (error) Uninitialized variable: b\n", errout.str()); + + ASSERT_EQUALS(1, (this->*check)("void f() {\n" + " int a;\n" + " a++;\n" + " int b;\n" + " b++;\n" + " // cppcheck-suppress-end uninitvar\n" + "}\n", + "")); + ASSERT_EQUALS("[test.cpp:6]: (error) Suppress End: No matching begin\n" + "[test.cpp:3]: (error) Uninitialized variable: a\n" + "[test.cpp:5]: (error) Uninitialized variable: b\n", errout.str()); + + ASSERT_EQUALS(1, (this->*check)("void f() {\n" + " int a;\n" + " // cppcheck-suppress-begin uninitvar\n" + " a++;\n" + " // cppcheck-suppress-end uninitvar\n" + " int b;\n" + " b++;\n" + "}\n", + "")); + ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: b\n", errout.str()); + + ASSERT_EQUALS(1, (this->*check)("void f() {\n" + " int a;\n" + " // cppcheck-suppress-begin uninitvar\n" + " a++;\n" + " // cppcheck-suppress-end uninitvar\n" + " int b;\n" + " b++;\n" + "}\n", + "")); + ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: b\n", errout.str()); + + ASSERT_EQUALS(1, (this->*check)("void f() {\n" + " int a;\n" + " // cppcheck-suppress-begin[uninitvar]\n" + " a++;\n" + " // cppcheck-suppress-end[uninitvar]\n" + " int b;\n" + " b++;\n" + "}\n", + "")); + ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: b\n", errout.str()); + + ASSERT_EQUALS(1, (this->*check)("void f() {\n" + " int a;\n" + " // cppcheck-suppress-begin [uninitvar]\n" + " a++;\n" + " // cppcheck-suppress-end [uninitvar]\n" + " int b;\n" + " b++;\n" + "}\n", + "")); + ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: b\n", errout.str()); + + (this->*check)("void f() {\n" + " int a;\n" + " // cppcheck-suppress-begin uninitvar\n" + " a++;\n" + " // cppcheck-suppress-end uninitvar\n" + " int b;\n" + " // cppcheck-suppress-begin uninitvar\n" + " b++;\n" + " // cppcheck-suppress-end uninitvar\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + (this->*check)("void f() {\n" + " int a;\n" + " // cppcheck-suppress-begin uninitvar\n" + " a++;\n" + " // cppcheck-suppress-end uninitvar\n" + " // cppcheck-suppress-begin uninitvar\n" + " int b;\n" + " b++;\n" + " // cppcheck-suppress-end uninitvar\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + (this->*check)("void f() {\n" + " // cppcheck-suppress-begin [uninitvar]\n" + " int a;\n" + " // cppcheck-suppress-begin uninitvar\n" + " a++;\n" + " // cppcheck-suppress-end uninitvar\n" + " int b;\n" + " // cppcheck-suppress-begin uninitvar\n" + " b++;\n" + " // cppcheck-suppress-end uninitvar\n" + " // cppcheck-suppress-end [uninitvar]\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + (this->*check)("void f() {\n" + " // cppcheck-suppress-begin [uninitvar, syntaxError]\n" + " int a;\n" + " // cppcheck-suppress-begin uninitvar\n" + " a++;\n" + " // cppcheck-suppress-end uninitvar\n" + " int b;\n" + " // cppcheck-suppress-begin uninitvar\n" + " b++;\n" + " // cppcheck-suppress-end uninitvar\n" + " // cppcheck-suppress-end [uninitvar, syntaxError]\n" + "}\n", + ""); + ASSERT_EQUALS("[test.cpp:2]: (information) Unmatched suppression: syntaxError\n", errout.str()); + + (this->*check)("// cppcheck-suppress-begin [uninitvar, syntaxError]\n" + "void f() {\n" + " int a;\n" + " // cppcheck-suppress-begin uninitvar\n" + " a++;\n" + " // cppcheck-suppress-end uninitvar\n" + " int b;\n" + " // cppcheck-suppress-begin uninitvar\n" + " b++;\n" + " // cppcheck-suppress-end uninitvar\n" + "}\n" + "// cppcheck-suppress-end [uninitvar, syntaxError]\n", + ""); + ASSERT_EQUALS("[test.cpp:1]: (information) Unmatched suppression: syntaxError\n", errout.str()); + + (this->*check)("// cppcheck-suppress-begin [uninitvar, syntaxError]\n" + "void f() {\n" + " int a;\n" + " // cppcheck-suppress-begin uninitvar\n" + " a++;\n" + " // cppcheck-suppress-end uninitvar\n" + " int b;\n" + " // cppcheck-suppress-begin uninitvar\n" + " b++;\n" + " // cppcheck-suppress-end uninitvar\n" + "}\n" + "// cppcheck-suppress-end [uninitvar, syntaxError]", + ""); + ASSERT_EQUALS("[test.cpp:1]: (information) Unmatched suppression: syntaxError\n", errout.str()); + + // test of multiple suppression types + (this->*check)("// cppcheck-suppress-file uninitvar\n" + "void f() {\n" + " int a;\n" + " // cppcheck-suppress-begin uninitvar\n" + " // cppcheck-suppress uninitvar\n" + " a++;\n" + " // cppcheck-suppress-end uninitvar\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + (this->*check)("void f() {\n" + " int a;\n" + " // cppcheck-suppress-begin uninitvar\n" + " // cppcheck-suppress uninitvar\n" + " a++;\n" + " // cppcheck-suppress-end uninitvar\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + (this->*check)("// cppcheck-suppress-file uninitvar\n" + "void f() {\n" + " int a;\n" + " // cppcheck-suppress uninitvar\n" + " a++;\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + (this->*check)("// cppcheck-suppress-file uninitvar\n" + "void f() {\n" + " int a;\n" + " // cppcheck-suppress-begin uninitvar\n" + " a++;\n" + " // cppcheck-suppress-end uninitvar\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + (this->*check)("// cppcheck-suppress-file uninitvar\n" + "void f() {\n" + " // cppcheck-suppress uninitvar\n" + " int a;\n" + " a++;\n" + "}\n", + ""); + ASSERT_EQUALS("[test.cpp:4]: (information) Unmatched suppression: uninitvar\n", errout.str()); + // #5746 - exitcode ASSERT_EQUALS(1U, (this->*check)("int f() {\n" @@ -571,15 +858,42 @@ private: void inlinesuppress() const { Suppressions::Suppression s; std::string msg; + + // Suppress without attribute ASSERT_EQUALS(false, s.parseComment("/* some text */", &msg)); ASSERT_EQUALS(false, s.parseComment("/* cppcheck-suppress */", &msg)); + ASSERT_EQUALS(false, s.parseComment("/* cppcheck-suppress-file */", &msg)); + ASSERT_EQUALS(false, s.parseComment("/* cppcheck-suppress-begin */", &msg)); + ASSERT_EQUALS(false, s.parseComment("/* cppcheck-suppress-end */", &msg)); + // Correct suppress msg.clear(); ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id */", &msg)); ASSERT_EQUALS("", msg); + msg.clear(); + ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-file id */", &msg)); + ASSERT_EQUALS("", msg); + + msg.clear(); + ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-begin id */", &msg)); + ASSERT_EQUALS("", msg); + + msg.clear(); + ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-end id */", &msg)); + ASSERT_EQUALS("", msg); + + // Bad attribute construction + const std::string badSuppressionAttribute = "Bad suppression attribute 'some'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym"; + ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id some text */", &msg)); - ASSERT_EQUALS("Bad suppression attribute 'some'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym", msg); + ASSERT_EQUALS(badSuppressionAttribute, msg); + ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-file id some text */", &msg)); + ASSERT_EQUALS(badSuppressionAttribute, msg); + ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-begin id some text */", &msg)); + ASSERT_EQUALS(badSuppressionAttribute, msg); + ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-end id some text */", &msg)); + ASSERT_EQUALS(badSuppressionAttribute, msg); } void inlinesuppress_symbolname() { @@ -622,6 +936,48 @@ private: ASSERT_EQUALS("", suppressions[0].symbolName); ASSERT_EQUALS("", errMsg); + errMsg = ""; + suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-begin[errorId]", &errMsg); + ASSERT_EQUALS(1, suppressions.size()); + ASSERT_EQUALS("errorId", suppressions[0].errorId); + ASSERT_EQUALS("", suppressions[0].symbolName); + ASSERT_EQUALS("", errMsg); + + errMsg = ""; + suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-begin [errorId]", &errMsg); + ASSERT_EQUALS(1, suppressions.size()); + ASSERT_EQUALS("errorId", suppressions[0].errorId); + ASSERT_EQUALS("", suppressions[0].symbolName); + ASSERT_EQUALS("", errMsg); + + errMsg = ""; + suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-end[errorId]", &errMsg); + ASSERT_EQUALS(1, suppressions.size()); + ASSERT_EQUALS("errorId", suppressions[0].errorId); + ASSERT_EQUALS("", suppressions[0].symbolName); + ASSERT_EQUALS("", errMsg); + + errMsg = ""; + suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-end [errorId]", &errMsg); + ASSERT_EQUALS(1, suppressions.size()); + ASSERT_EQUALS("errorId", suppressions[0].errorId); + ASSERT_EQUALS("", suppressions[0].symbolName); + ASSERT_EQUALS("", errMsg); + + errMsg = ""; + suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-file[errorId]", &errMsg); + ASSERT_EQUALS(1, suppressions.size()); + ASSERT_EQUALS("errorId", suppressions[0].errorId); + ASSERT_EQUALS("", suppressions[0].symbolName); + ASSERT_EQUALS("", errMsg); + + errMsg = ""; + suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-file [errorId]", &errMsg); + ASSERT_EQUALS(1, suppressions.size()); + ASSERT_EQUALS("errorId", suppressions[0].errorId); + ASSERT_EQUALS("", suppressions[0].symbolName); + ASSERT_EQUALS("", errMsg); + errMsg = ""; suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress[errorId symbolName=arr]", &errMsg); ASSERT_EQUALS(1, suppressions.size());