diff --git a/AUTHORS b/AUTHORS index 50ba3a4f67e..cf210e045a1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -193,6 +193,7 @@ Jakub Melka Jan Egil Ruud Jan Hellwig János Maros +Jay Beavers Jay Sigbrandt Jean-François Deverge Jedrzej Klocek diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 8e32e3e3152..8ca2ce36a89 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -106,12 +106,27 @@ class CppCheck::CppCheckLogger : public ErrorLogger mRemarkComments.insert(mRemarkComments.end(), remarkComments.begin(), remarkComments.end()); } - void setLocationMacros(const Token* startTok, const std::vector& files) + void setLocationMacros(const Token* startTok, const std::vector& files, const std::list& macroUsage) { mLocationMacros.clear(); + std::set usedNames; + std::transform(macroUsage.cbegin(), macroUsage.cend(), std::inserter(usedNames, usedNames.end()), [](const simplecpp::MacroUsage& usage) { + return usage.macroName; + }); + std::set> unexpanded; for (const Token* tok = startTok; tok; tok = tok->next()) { + const Location loc(files[tok->fileIndex()], tok->linenr()); if (!tok->getMacroName().empty()) - mLocationMacros[Location(files[tok->fileIndex()], tok->linenr())].emplace(tok->getMacroName()); + mLocationMacros[loc].emplace(tok->getMacroName()); + else if (tok->isName() && usedNames.count(tok->str()) != 0) + unexpanded.emplace(loc, tok->str()); + } + // a token only knows the innermost macro it was expanded from; usages that produced no tokens are skipped + for (const simplecpp::MacroUsage& usage : macroUsage) { + const Location loc(files[usage.useLocation.fileIndex], usage.useLocation.line); + const auto it = mLocationMacros.find(loc); + if (it != mLocationMacros.end() && unexpanded.count({loc, usage.macroName}) == 0) + it->second.emplace(usage.macroName); } } @@ -1228,7 +1243,7 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str tokenizer.setDirectives(directives); // TODO: how to avoid repeated copies? // locations macros - mLogger->setLocationMacros(tokenizer.tokens(), files); + mLogger->setLocationMacros(tokenizer.tokens(), files, preprocessor.getMacroUsage()); // If only errors are printed, print filename after the check if (!mSettings.quiet && (!currentConfig.empty() || checkCount > 1)) { diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 5df52934dfc..89f225f0682 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -124,6 +124,10 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { std::vector getRemarkComments(const simplecpp::TokenList &tokens) const; + const std::list& getMacroUsage() const { + return mMacroUsage; + } + bool loadAllIncludes(std::vector &files); simplecpp::FileData *loadFile(std::vector &files, const std::string &file); diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index fbdcb6307e5..2781565650b 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -1052,6 +1052,39 @@ class TestSuppressions : public TestFixture { "a = DIV(10,1);\n", ""); ASSERT_EQUALS("[test.cpp:2:1]: (information) Unmatched suppression: abc [unmatchedSuppression]\n", errout_str()); + + // #15208 - macro whose body is a single call to another macro + ASSERT_EQUALS(0, + (this->*check)("#define DIV(A,B) A/B\n" + "// cppcheck-suppress-macro zerodiv\n" + "#define ALIAS(A,B) DIV(A,B)\n" + "void f() {\n" + " int a = ALIAS(10,0);\n" + "}\n", + "")); + ASSERT_EQUALS("", errout_str()); + + // function-like macro name that is not expanded + ASSERT_EQUALS(1, + (this->*check)("// cppcheck-suppress-macro zerodiv\n" + "#define ALIAS(A,B) A/B\n" + "void f() {\n" + " int ALIAS = 1/0;\n" + "}\n", + "")); + ASSERT_EQUALS("[test.cpp:4:18]: (error) Division by zero. [zerodiv]\n" + "[test.cpp:2:1]: (information) Unmatched suppression: zerodiv [unmatchedSuppression]\n", errout_str()); + + // empty macro on the line + ASSERT_EQUALS(1, + (this->*check)("// cppcheck-suppress-macro zerodiv\n" + "#define EMPTY\n" + "void f() {\n" + " EMPTY int a = 1/0;\n" + "}\n", + "")); + ASSERT_EQUALS("[test.cpp:4:20]: (error) Division by zero. [zerodiv]\n" + "[test.cpp:2:1]: (information) Unmatched suppression: zerodiv [unmatchedSuppression]\n", errout_str()); } void suppressionsSettingsFiles() {