diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 749560517..e8a0e9217 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -76,27 +76,69 @@ namespace { }; } -static void inlineSuppressions(const simplecpp::TokenList &tokens, Settings &mSettings, std::list *bad) +static void parseCommentToken(const simplecpp::Token *tok, std::list &inlineSuppressions, std::list *bad) { - std::list inlineSuppressions; - for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { - if (tok->comment) { - Suppressions::Suppression s; + const std::size_t position=tok->str().find("cppcheck-suppress"); + if (position != std::string::npos) { + if ((tok->str().length() > position+17) && (tok->str()[position+17] == '[')) { //multi suppress format std::string errmsg; - if (!s.parseComment(tok->str(), &errmsg)) - continue; + std::vector ss; + ss = Suppressions::parseMultiSuppressComment(tok->str(), &errmsg); + if (!errmsg.empty()) bad->push_back(BadInlineSuppression(tok->location, errmsg)); + + for (std::vector::iterator iter = ss.begin(); iter!=ss.end(); ++iter) { + if (!(*iter).errorId.empty()) + inlineSuppressions.push_back(*iter); + } + } + else { //single suppress format + std::string errmsg; + Suppressions::Suppression s; + if (!s.parseComment(tok->str(), &errmsg)) + return; + if (!s.errorId.empty()) inlineSuppressions.push_back(s); + + if (!errmsg.empty()) + bad->push_back(BadInlineSuppression(tok->location, errmsg)); + } + } + + return; +} + +static void inlineSuppressions(const simplecpp::TokenList &tokens, Settings &mSettings, std::list *bad) +{ + const simplecpp::Token *current_non_comment_tok; + std::list inlineSuppressions; + + for (const simplecpp::Token *tok = tokens.cfront(); tok; /*none*/) { + //parse all comment before current non-comment tok + if (tok->comment) { + parseCommentToken(tok, inlineSuppressions, bad); + tok = tok->next; continue; } - if (inlineSuppressions.empty()) + current_non_comment_tok = tok; + + //parse all comment tok after current non-comment tok in the same line + for (tok = tok->next; tok; tok = tok->next) { + if ((tok->location.line != current_non_comment_tok->location.line) || !(tok->comment)) + break; + parseCommentToken(tok, inlineSuppressions, bad); + } + + //if there is no suppress, jump it! + if (inlineSuppressions.empty()) { continue; + } // Relative filename - std::string relativeFilename(tok->location.file()); + std::string relativeFilename(current_non_comment_tok->location.file()); if (mSettings.relativePaths) { for (const std::string & basePath : mSettings.basePaths) { const std::string bp = basePath + "/"; @@ -110,7 +152,7 @@ static void inlineSuppressions(const simplecpp::TokenList &tokens, Settings &mSe // Add the suppressions. for (Suppressions::Suppression &suppr : inlineSuppressions) { suppr.fileName = relativeFilename; - suppr.lineNumber = tok->location.line; + suppr.lineNumber = current_non_comment_tok->location.line; mSettings.nomsg.addSuppression(suppr); } inlineSuppressions.clear(); diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index 97ad92b53..2dcd38829 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -112,6 +112,99 @@ std::string Suppressions::parseXmlFile(const char *filename) return ""; } +std::vector Suppressions::parseMultiSuppressComment(std::string comment, std::string *errorMessage) +{ + std::vector suppressions; + if (comment.size() < 2) + return suppressions; + + const std::size_t first_non_blank_position = comment.find_first_not_of(" \t\n", 2); + const std::size_t suppress_position = comment.find("cppcheck-suppress", 2); + if (suppress_position == std::string::npos) + return suppressions; + if (suppress_position != first_non_blank_position) { + if (errorMessage && errorMessage->empty()) + *errorMessage = "Bad multi suppression '" + comment + "'. there shouldn't have non-blank character before cppcheck-suppress"; + return suppressions; + } + + const std::size_t start_position = comment.find("[", suppress_position); + const std::size_t end_position = comment.find("]", suppress_position); + if ( start_position == std::string::npos + || end_position == std::string::npos + || start_position != suppress_position+17 //there must be no space before "[" + || start_position >= end_position) + { + if (errorMessage && errorMessage->empty()) + *errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]"; + return suppressions; + } + + //extract supressions + std::size_t current_comma_position = 0; + //multi_suppressions_word maybe "[errorId1, errorId2 symbolName=arr", who just has left bracket + std::string multi_suppressions_word = comment.substr(start_position, end_position-start_position); + do + { + std::string suppression_word; + + //single suppression word + const std::size_t previous_comma_position=current_comma_position; + current_comma_position=multi_suppressions_word.find(",", previous_comma_position+1); //find "," after previous comma + if (current_comma_position == std::string::npos) + { + suppression_word = multi_suppressions_word.substr(previous_comma_position+1); + } + else + { + suppression_word = multi_suppressions_word.substr(previous_comma_position+1, current_comma_position-previous_comma_position-1); + } + + //parse single suppression word + Suppression s; + std::string word; + std::string errorId; + std::string symbolName; + std::istringstream iss(suppression_word); + + iss >> errorId; + if (!iss) + { + if (errorMessage && errorMessage->empty()) + *errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]"; + suppressions.clear(); + return suppressions; + } + + while (iss) + { + iss >> word; + if (!iss) + break; + if (word.find_first_not_of("+-*/%#;") == std::string::npos) + break; + if (word.compare(0, 11, "symbolName=") == 0) + { + symbolName = word.substr(11); + } + else + { + if (errorMessage && errorMessage->empty()) + *errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]"; + suppressions.clear(); + return suppressions; + } + } + + s.errorId = errorId; + s.symbolName = symbolName; + suppressions.push_back(s); + + } while(current_comma_position!=std::string::npos); + + return suppressions; +} + std::string Suppressions::addSuppressionLine(const std::string &line) { std::istringstream lineStream(line); diff --git a/lib/suppressions.h b/lib/suppressions.h index 79bcb1c67..8ebfa209f 100644 --- a/lib/suppressions.h +++ b/lib/suppressions.h @@ -26,6 +26,7 @@ #include #include #include +#include /// @addtogroup Core /// @{ @@ -115,6 +116,14 @@ public: */ std::string parseXmlFile(const char *filename); + /** + * Parse multi inline suppression in comment + * @param comment the full comment text + * @param errorMessage output parameter for error message (wrong suppression attribute) + * @return empty vector if something wrong. + */ + static std::vector parseMultiSuppressComment(std::string comment, std::string *errorMessage); + /** * @brief Don't show the given error. * @param line Description of error to suppress (in id:file:line format). diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index e94c845c6..5fff8ceeb 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -54,6 +54,9 @@ private: TEST_CASE(inlinesuppress_symbolname); TEST_CASE(inlinesuppress_comment); + TEST_CASE(multi_inlinesuppress); + TEST_CASE(multi_inlinesuppress_comment); + TEST_CASE(globalSuppressions); // Testing that global suppressions work (#8515) TEST_CASE(inlinesuppress_unusedFunction); // #4210 - unusedFunction @@ -323,6 +326,14 @@ private: ""); ASSERT_EQUALS("", errout.str()); + // suppress uninitvar inline + (this->*check)("void f() {\n" + " int a;\n" + " a++;// cppcheck-suppress uninitvar\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + // suppress uninitvar inline (this->*check)("void f() {\n" " int a;\n" @@ -342,6 +353,69 @@ private: ""); ASSERT_EQUALS("", errout.str()); + // suppress uninitvar inline + (this->*check)("void f() {\n" + " int a;\n" + " a++;/* cppcheck-suppress uninitvar */\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + // suppress uninitvar inline + (this->*check)("void f() {\n" + " int a;\n" + " // cppcheck-suppress[uninitvar]\n" + " a++;\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + // suppress uninitvar inline + (this->*check)("void f() {\n" + " int a;\n" + " // cppcheck-suppress[uninitvar]\n" + " a++;\n" + "\n" + " a++;\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + // suppress uninitvar inline + (this->*check)("void f() {\n" + " int a;\n" + " a++;// cppcheck-suppress[uninitvar]\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + // suppress uninitvar inline + (this->*check)("void f() {\n" + " int a;\n" + " /* cppcheck-suppress[uninitvar]*/\n" + " a++;\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + // suppress uninitvar inline + (this->*check)("void f() {\n" + " int a;\n" + " /* cppcheck-suppress[uninitvar]*/\n" + "\n" + " a++;\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + // suppress uninitvar inline + (this->*check)("void f() {\n" + " int a;\n" + " a++;/* cppcheck-suppress[uninitvar]*/\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + // suppress uninitvar inline, with asm before (#6813) (this->*check)("void f() {\n" " __asm {\n" @@ -467,6 +541,110 @@ private: ASSERT_EQUALS("", errMsg); } + void multi_inlinesuppress() { + Suppressions ss; + std::vector suppressions; + std::string errMsg; + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("// cppcheck-suppress[errorId]", &errMsg); + ASSERT_EQUALS(1, suppressions.size()); + ASSERT_EQUALS("errorId", suppressions[0].errorId); + ASSERT_EQUALS("", suppressions[0].symbolName); + ASSERT_EQUALS("", errMsg); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("// cppcheck-suppress[errorId symbolName=arr]", &errMsg); + ASSERT_EQUALS(1, suppressions.size()); + ASSERT_EQUALS("errorId", suppressions[0].errorId); + ASSERT_EQUALS("arr", suppressions[0].symbolName); + ASSERT_EQUALS("", errMsg); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("// cppcheck-suppress[errorId symbolName=]", &errMsg); + ASSERT_EQUALS(1, suppressions.size()); + ASSERT_EQUALS("errorId", suppressions[0].errorId); + ASSERT_EQUALS("", suppressions[0].symbolName); + ASSERT_EQUALS("", errMsg); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("// cppcheck-suppress[errorId1, errorId2 symbolName=arr]", &errMsg); + ASSERT_EQUALS(2, suppressions.size()); + ASSERT_EQUALS("errorId1", suppressions[0].errorId); + ASSERT_EQUALS("", suppressions[0].symbolName); + ASSERT_EQUALS("errorId2", suppressions[1].errorId); + ASSERT_EQUALS("arr", suppressions[1].symbolName); + ASSERT_EQUALS("", errMsg); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("// cppcheck-suppress [errorId]", &errMsg); + ASSERT_EQUALS(0, suppressions.size()); + ASSERT_EQUALS(false, errMsg.empty()); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("// cppcheck-suppress[]", &errMsg); + ASSERT_EQUALS(0, suppressions.size()); + ASSERT_EQUALS(false, errMsg.empty()); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("// cppcheck-suppress[errorId", &errMsg); + ASSERT_EQUALS(0, suppressions.size()); + ASSERT_EQUALS(false, errMsg.empty()); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("// cppcheck-suppress errorId", &errMsg); + ASSERT_EQUALS(0, suppressions.size()); + ASSERT_EQUALS(false, errMsg.empty()); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("// cppcheck-suppress[errorId1 errorId2 symbolName=arr]", &errMsg); + ASSERT_EQUALS(0, suppressions.size()); + ASSERT_EQUALS(false, errMsg.empty()); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("// cppcheck-suppress[errorId1, errorId2 symbol=arr]", &errMsg); + ASSERT_EQUALS(0, suppressions.size()); + ASSERT_EQUALS(false, errMsg.empty()); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("// cppcheck-suppress[errorId1, errorId2 symbolName]", &errMsg); + ASSERT_EQUALS(0, suppressions.size()); + ASSERT_EQUALS(false, errMsg.empty()); + } + + void multi_inlinesuppress_comment() { + Suppressions ss; + std::vector suppressions; + std::string errMsg; + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("//cppcheck-suppress[errorId1, errorId2 symbolName=arr]", &errMsg); + ASSERT_EQUALS(2, suppressions.size()); + ASSERT_EQUALS(true, errMsg.empty()); + + //error, shouldn't have "some text" at beginning + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("//some text cppcheck-suppress[errorId1, errorId2 symbolName=arr]", &errMsg); + ASSERT_EQUALS(0, suppressions.size()); + ASSERT_EQUALS(false, errMsg.empty()); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("//cppcheck-suppress[errorId1, errorId2 symbolName=arr] some text", &errMsg); + ASSERT_EQUALS(2, suppressions.size()); + ASSERT_EQUALS(true, errMsg.empty()); + + //error, shouldn't have "some text" at beginning + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("//some text cppcheck-suppress[errorId1, errorId2 symbolName=arr] some text", &errMsg); + ASSERT_EQUALS(0, suppressions.size()); + ASSERT_EQUALS(false, errMsg.empty()); + + errMsg = ""; + suppressions=ss.parseMultiSuppressComment("/*cppcheck-suppress[errorId1, errorId2 symbolName=arr]*/", &errMsg); + ASSERT_EQUALS(2, suppressions.size()); + ASSERT_EQUALS(true, errMsg.empty()); + } + void globalSuppressions() { // Testing that Cppcheck::useGlobalSuppressions works (#8515) errout.str("");