diff --git a/cli/executor.cpp b/cli/executor.cpp index c7955d717..aab7242a1 100644 --- a/cli/executor.cpp +++ b/cli/executor.cpp @@ -33,7 +33,7 @@ Executor::Executor(const std::map &files, const Settin bool Executor::hasToLog(const ErrorMessage &msg) { - if (!mSuppressions.isSuppressed(msg)) + if (!mSuppressions.isSuppressed(msg, {})) { std::string errmsg = msg.toString(mSettings.verbose); diff --git a/cli/processexecutor.cpp b/cli/processexecutor.cpp index 04be345e9..543ee8df6 100644 --- a/cli/processexecutor.cpp +++ b/cli/processexecutor.cpp @@ -394,7 +394,7 @@ void ProcessExecutor::reportInternalChildErr(const std::string &childname, const "cppcheckError", Certainty::normal); - if (!mSuppressions.isSuppressed(errmsg)) + if (!mSuppressions.isSuppressed(errmsg, {})) mErrorLogger.reportErr(errmsg); } diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 126d9d936..55284f6fb 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -858,6 +858,13 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string } hasValidConfig = true; + // locations macros + mLocationMacros.clear(); + for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) { + if (!tok->getMacroName().empty()) + mLocationMacros[Location(files[tok->fileIndex()], tok->linenr())].emplace(tok->getMacroName()); + } + // If only errors are printed, print filename after the check if (!mSettings.quiet && (!mCurrentConfig.empty() || checkCount > 1)) { std::string fixedpath = Path::simplifyPath(filename); @@ -1556,8 +1563,17 @@ void CppCheck::reportErr(const ErrorMessage &msg) if (!mSettings.buildDir.empty()) mAnalyzerInformation.reportErr(msg); + std::set macroNames; + if (!msg.callStack.empty()) { + const std::string &file = msg.callStack.back().getfile(false); + int lineNumber = msg.callStack.back().line; + const auto it = mLocationMacros.find(Location(file, lineNumber)); + if (it != mLocationMacros.cend()) + macroNames = it->second; + } + // TODO: only convert if necessary - const auto errorMessage = Suppressions::ErrorMessage::fromErrorMessage(msg); + const auto errorMessage = Suppressions::ErrorMessage::fromErrorMessage(msg, macroNames); if (mSettings.nomsg.isSuppressed(errorMessage, mUseGlobalSuppressions)) { return; diff --git a/lib/cppcheck.h b/lib/cppcheck.h index 78d40c3aa..a9282a0e6 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -223,6 +224,9 @@ private: /** @brief Current preprocessor configuration */ std::string mCurrentConfig; + using Location = std::pair; + std::map> mLocationMacros; // What macros are used on a location? + unsigned int mExitCode{}; bool mUseGlobalSuppressions; diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 1712ac86c..d4c1804b8 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -119,15 +119,16 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std: const std::string suppressTypeString = comment.substr(pos1 + cppchecksuppress.size() + 1, argumentLength); - if ("file" == suppressTypeString) { + if ("file" == suppressTypeString) errorType = Suppressions::Type::file; - } else if ("begin" == suppressTypeString) { + else if ("begin" == suppressTypeString) errorType = Suppressions::Type::blockBegin; - } else if ("end" == suppressTypeString) { + else if ("end" == suppressTypeString) errorType = Suppressions::Type::blockEnd; - } else { + else if ("macro" == suppressTypeString) + errorType = Suppressions::Type::macro; + else return false; - } } if (comment[pos2] == '[') { @@ -217,6 +218,15 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett } relativeFilename = Path::simplifyPath(relativeFilename); + // Macro name + std::string macroName; + if (tok->str() == "#" && tok->next && tok->next->str() == "define") { + const simplecpp::Token *macroNameTok = tok->next->next; + if (sameline(tok, macroNameTok) && macroNameTok->name) { + macroName = macroNameTok->str(); + } + } + // Add the suppressions. for (Suppressions::Suppression &suppr : inlineSuppressions) { suppr.fileName = relativeFilename; @@ -252,7 +262,7 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett // 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) { + } else if (Suppressions::Type::unique == suppr.type || suppr.type == Suppressions::Type::macro) { // special handling when suppressing { warnings for backwards compatibility const bool thisAndNextLine = tok->previous && tok->previous->previous && @@ -264,6 +274,7 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett suppr.thisAndNextLine = thisAndNextLine; suppr.lineNumber = tok->location.line; + suppr.macroName = macroName; suppressions.addSuppression(std::move(suppr)); } else if (Suppressions::Type::file == suppr.type) { if (onlyComments) diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index 284ba30d7..d395a4c1e 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -35,7 +35,7 @@ #include -Suppressions::ErrorMessage Suppressions::ErrorMessage::fromErrorMessage(const ::ErrorMessage &msg) +Suppressions::ErrorMessage Suppressions::ErrorMessage::fromErrorMessage(const ::ErrorMessage &msg, const std::set ¯oNames) { Suppressions::ErrorMessage ret; ret.hash = msg.hash; @@ -48,6 +48,7 @@ Suppressions::ErrorMessage Suppressions::ErrorMessage::fromErrorMessage(const :: } ret.certainty = msg.certainty; ret.symbolNames = msg.symbolNames(); + ret.macroNames = macroNames; return ret; } @@ -308,7 +309,8 @@ bool Suppressions::Suppression::parseComment(std::string comment, std::string *e "cppcheck-suppress", "cppcheck-suppress-begin", "cppcheck-suppress-end", - "cppcheck-suppress-file" + "cppcheck-suppress-file", + "cppcheck-suppress-macro" }; std::istringstream iss(comment.substr(2)); @@ -343,14 +345,19 @@ bool Suppressions::Suppression::isSuppressed(const Suppressions::ErrorMessage &e return false; if (!errorId.empty() && !matchglob(errorId, errmsg.errorId)) return false; - if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName())) - return false; - if ((Suppressions::Type::unique == type) && (lineNumber != NO_LINE) && (lineNumber != errmsg.lineNumber)) { - if (!thisAndNextLine || lineNumber + 1 != errmsg.lineNumber) + if (type == Suppressions::Type::macro) { + if (errmsg.macroNames.count(macroName) == 0) + return false; + } else { + if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName())) + return false; + 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 ((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); @@ -412,11 +419,11 @@ bool Suppressions::isSuppressed(const Suppressions::ErrorMessage &errmsg, bool g return returnValue; } -bool Suppressions::isSuppressed(const ::ErrorMessage &errmsg) +bool Suppressions::isSuppressed(const ::ErrorMessage &errmsg, const std::set& macroNames) { if (mSuppressions.empty()) return false; - return isSuppressed(Suppressions::ErrorMessage::fromErrorMessage(errmsg)); + return isSuppressed(Suppressions::ErrorMessage::fromErrorMessage(errmsg, macroNames)); } void Suppressions::dump(std::ostream & out) const @@ -445,6 +452,8 @@ std::list Suppressions::getUnmatchedLocalSuppressions for (const Suppression &s : mSuppressions) { if (s.matched || ((s.lineNumber != Suppression::NO_LINE) && !s.checked)) continue; + if (s.type == Suppressions::Type::macro) + continue; if (s.hash > 0) continue; if (!unusedFunctionChecking && s.errorId == "unusedFunction") diff --git a/lib/suppressions.h b/lib/suppressions.h index 53f3b6e7f..d859ed02f 100644 --- a/lib/suppressions.h +++ b/lib/suppressions.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,7 @@ class CPPCHECKLIB Suppressions { public: enum class Type { - unique, file, block, blockBegin, blockEnd + unique, file, block, blockBegin, blockEnd, macro }; struct CPPCHECKLIB ErrorMessage { @@ -55,8 +56,9 @@ public: int lineNumber; Certainty certainty; std::string symbolNames; + std::set macroNames; - static Suppressions::ErrorMessage fromErrorMessage(const ::ErrorMessage &msg); + static Suppressions::ErrorMessage fromErrorMessage(const ::ErrorMessage &msg, const std::set ¯oNames); private: std::string mFileName; }; @@ -74,6 +76,8 @@ public: return fileName < other.fileName; if (symbolName != other.symbolName) return symbolName < other.symbolName; + if (macroName != other.macroName) + return macroName < other.macroName; if (hash != other.hash) return hash < other.hash; if (thisAndNextLine != other.thisAndNextLine) @@ -90,6 +94,8 @@ public: return false; if (symbolName != other.symbolName) return false; + if (macroName != other.macroName) + return false; if (hash != other.hash) return false; if (type != other.type) @@ -135,6 +141,7 @@ public: int lineEnd = NO_LINE; Type type = Type::unique; std::string symbolName; + std::string macroName; std::size_t hash{}; bool thisAndNextLine{}; // Special case for backwards compatibility: { // cppcheck-suppress something bool matched{}; @@ -200,7 +207,7 @@ public: * @param errmsg error message * @return true if this error is suppressed. */ - bool isSuppressed(const ::ErrorMessage &errmsg); + bool isSuppressed(const ::ErrorMessage &errmsg, const std::set& macroNames); /** * @brief Create an xml dump of suppressions diff --git a/lib/token.h b/lib/token.h index 346364b40..a56a61129 100644 --- a/lib/token.h +++ b/lib/token.h @@ -106,6 +106,9 @@ struct TokenImpl { // original name like size_t std::string* mOriginalName{}; + // If this token came from a macro replacement list, this is the name of that macro + std::string mMacroName; + // ValueType ValueType* mValueType{}; @@ -461,10 +464,7 @@ public: setFlag(fIsStandardType, b); } bool isExpandedMacro() const { - return getFlag(fIsExpandedMacro); - } - void isExpandedMacro(const bool m) { - setFlag(fIsExpandedMacro, m); + return !mImpl->mMacroName.empty(); } bool isCast() const { return getFlag(fIsCast); @@ -763,6 +763,13 @@ public: setFlag(fIsTemplateArg, value); } + std::string getMacroName() const { + return mImpl->mMacroName; + } + void setMacroName(std::string name) { + mImpl->mMacroName = std::move(name); + } + template static const Token *findsimplematch(const Token * const startTok, const char (&pattern)[count]) { return findsimplematch(startTok, pattern, count-1); @@ -1305,7 +1312,7 @@ private: fIsPointerCompare = (1ULL << 2), fIsLong = (1ULL << 3), fIsStandardType = (1ULL << 4), - fIsExpandedMacro = (1ULL << 5), + //fIsExpandedMacro = (1ULL << 5), fIsCast = (1ULL << 6), fIsAttributeConstructor = (1ULL << 7), // __attribute__((constructor)) __attribute__((constructor(priority))) fIsAttributeDestructor = (1ULL << 8), // __attribute__((destructor)) __attribute__((destructor(priority))) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 1aac2d76b..f967bb060 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -806,7 +806,7 @@ namespace { if (pointerType) { tok->insertToken("const"); tok->next()->column(tok->column()); - tok->next()->isExpandedMacro(tok->previous()->isExpandedMacro()); + tok->next()->setMacroName(tok->previous()->getMacroName()); tok->deletePrevious(); } } @@ -7169,7 +7169,7 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co endDecl = endDecl->next(); endDecl->next()->isSplittedVarDeclEq(true); endDecl->insertToken(varName->str()); - endDecl->next()->isExpandedMacro(varName->isExpandedMacro()); + endDecl->next()->setMacroName(varName->getMacroName()); continue; } //non-VLA case diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 2991cd7a2..0a8b9086b 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -306,6 +306,7 @@ void TokenList::insertTokens(Token *dest, const Token *src, nonneg int n) dest->varId(src->varId()); dest->tokType(src->tokType()); dest->flags(src->flags()); + dest->setMacroName(src->getMacroName()); src = src->next(); --n; } @@ -363,7 +364,7 @@ void TokenList::createTokens(simplecpp::TokenList&& tokenList) mTokensFrontBack.back->fileIndex(tok->location.fileIndex); mTokensFrontBack.back->linenr(tok->location.line); mTokensFrontBack.back->column(tok->location.col); - mTokensFrontBack.back->isExpandedMacro(!tok->macro.empty()); + mTokensFrontBack.back->setMacroName(tok->macro); tok = tok->next; if (tok) diff --git a/man/manual-premium.md b/man/manual-premium.md index ad6d51f01..d0cf7a5dd 100644 --- a/man/manual-premium.md +++ b/man/manual-premium.md @@ -530,6 +530,41 @@ 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] + +Suppressing warnings `aaaa` where macro is used: + + // cppcheck-suppress-macro aaaa + #define MACRO ... + ... + x = MACRO; // <- aaaa warnings are suppressed here + + +Suppressing multiple ids where macro is used: + + // cppcheck-suppress-macro [aaaa, bbbb] + #define MACRO ... + ... + x = MACRO; // <- aaaa and bbbb warnings are suppressed here + ### 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/man/manual.md b/man/manual.md index 7f4b1210c..332bc9a2a 100644 --- a/man/manual.md +++ b/man/manual.md @@ -551,6 +551,21 @@ Suppressing multiple ids for a whole file: // cppcheck-suppress-file [aaaa, bbbb] +Suppressing warnings `aaaa` where macro is used: + + // cppcheck-suppress-macro aaaa + #define MACRO ... + ... + x = MACRO; // <- aaaa warnings are suppressed here + + +Suppressing multiple ids where macro is used: + + // cppcheck-suppress-macro [aaaa, bbbb] + #define MACRO ... + ... + x = MACRO; // <- aaaa and bbbb warnings are suppressed here + ### 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/releasenotes.txt b/releasenotes.txt index dbecae7cf..089c188ac 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -24,3 +24,7 @@ Other: - "--project" can also no longer be used in conjunction with additional source files. - If a addon cannot be found it will bail out immediately instead of continously writing errors and failing the analysis at the end. - Added CMake option "BUILD_MANPAGE" which adds the "man" target which will build the manpage. This requires xsltproc and the docbook XSLs to be installed. +- Improved inline suppressions: + - You can suppress warnings in a block of code using "-begin" and "-end". + - You can suppress warnings in current file using "-file". + - You can suppress all warnings where macro is used using "-macro" diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 20f192cac..6374ac36f 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -777,6 +777,19 @@ private: " int a; return a;\n" "}\n", "uninitvar")); + + // cppcheck-suppress-macro + (this->*check)("// cppcheck-suppress-macro zerodiv\n" + "#define DIV(A,B) A/B\n" + "a = DIV(10,0);\n", + ""); + ASSERT_EQUALS("", errout.str()); + + (this->*check)("// cppcheck-suppress-macro abc\n" + "#define DIV(A,B) A/B\n" + "a = DIV(10,1);\n", + ""); + ASSERT_EQUALS("", errout.str()); // <- no unmatched suppression reported for macro suppression } void suppressionsSettings() {