From 7d27583e0f80ee0f257e71cb667dc129ed9315b1 Mon Sep 17 00:00:00 2001 From: glank Date: Fri, 26 Jun 2026 14:53:56 +0200 Subject: [PATCH] 14113 --- .github/workflows/selfcheck.yml | 2 +- externals/simplecpp/simplecpp.cpp | 3 + externals/simplecpp/simplecpp.h | 8 ++ lib/cppcheck.cpp | 119 +++++++++++++++----- lib/cppcheck.h | 5 +- lib/errorlogger.cpp | 9 +- lib/errorlogger.h | 18 +++- lib/preprocessor.cpp | 173 ++++++++++++++---------------- lib/preprocessor.h | 35 ++++-- test/helpers.cpp | 3 +- test/testcppcheck.cpp | 7 +- test/testpreprocessor.cpp | 30 ++++-- test/testtokenize.cpp | 8 +- 13 files changed, 261 insertions(+), 159 deletions(-) diff --git a/.github/workflows/selfcheck.yml b/.github/workflows/selfcheck.yml index 6d100c4ddc8..1a213039f93 100644 --- a/.github/workflows/selfcheck.yml +++ b/.github/workflows/selfcheck.yml @@ -121,7 +121,7 @@ jobs: - name: Self check (unusedFunction / no test / no gui) run: | - supprs="--suppress=unusedFunction:lib/errorlogger.h:197 --suppress=unusedFunction:lib/importproject.cpp:1671 --suppress=unusedFunction:lib/importproject.cpp:1695" + supprs="--suppress=unusedFunction:lib/errorlogger.h:198 --suppress=unusedFunction:lib/importproject.cpp:1671 --suppress=unusedFunction:lib/importproject.cpp:1695" ./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib -D__CPPCHECK__ -D__GNUC__ --enable=unusedFunction,information --exception-handling -rp=. --project=cmake.output.notest_nogui/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr $supprs env: DISABLE_VALUEFLOW: 1 diff --git a/externals/simplecpp/simplecpp.cpp b/externals/simplecpp/simplecpp.cpp index 47e674837ba..c453c9a92a2 100644 --- a/externals/simplecpp/simplecpp.cpp +++ b/externals/simplecpp/simplecpp.cpp @@ -3113,6 +3113,9 @@ std::pair simplecpp::FileDataCache::tryload(FileDat mIdMap.emplace(fileId, data); mData.emplace_back(data); + if (mCallback) + mCallback(*data); + return {data, true}; } diff --git a/externals/simplecpp/simplecpp.h b/externals/simplecpp/simplecpp.h index f29166ff061..9793518cea8 100644 --- a/externals/simplecpp/simplecpp.h +++ b/externals/simplecpp/simplecpp.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -505,6 +506,12 @@ namespace simplecpp { return mData.cend(); } + using callback_type = std::function; + + void set_callback(callback_type cb) { + mCallback = std::move(cb); + } + private: struct FileID { #ifdef _WIN32 @@ -551,6 +558,7 @@ namespace simplecpp { container_type mData; name_map_type mNameMap; id_map_type mIdMap; + callback_type mCallback; }; /** Converts character literal (including prefix, but not ud-suffix) to long long value. diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 9a3e2e2a053..99d1cd9705b 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -100,9 +100,9 @@ class CppCheck::CppCheckLogger : public ErrorLogger closePlist(); } - void setRemarkComments(std::vector remarkComments) + std::vector& remarkComments() { - mRemarkComments = std::move(remarkComments); + return mRemarkComments; } void setLocationMacros(const Token* startTok, const std::vector& files) @@ -124,17 +124,25 @@ class CppCheck::CppCheckLogger : public ErrorLogger mErrorList.clear(); } - void openPlist(const std::string& filename, const std::vector& files) + void openPlist(const std::string& filename) { mPlistFile.open(filename); - mPlistFile << ErrorLogger::plistHeader(version(), files); + mPlistFile << ErrorLogger::plistHeader(version()); + } + + void setPlistFilenames(std::vector files) + { + if (mPlistFile.is_open()) { + mPlistFilenames = std::move(files); + } } void closePlist() { if (mPlistFile.is_open()) { - mPlistFile << ErrorLogger::plistFooter(); + mPlistFile << ErrorLogger::plistFooter(mPlistFilenames); mPlistFile.close(); + mPlistFilenames.clear(); } } @@ -282,6 +290,7 @@ class CppCheck::CppCheckLogger : public ErrorLogger std::map> mLocationMacros; // What macros are used on a location? std::ofstream mPlistFile; + std::vector mPlistFilenames; unsigned int mExitCode{}; @@ -898,7 +907,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string return checkInternal(file, cfgname, f); } -void CppCheck::checkPlistOutput(const FileWithDetails& file, const std::vector& files) +void CppCheck::checkPlistOutput(const FileWithDetails& file) { if (!mSettings.plistOutput.empty()) { const bool slashFound = file.spath().find('/') != std::string::npos; @@ -909,7 +918,7 @@ void CppCheck::checkPlistOutput(const FileWithDetails& file, const std::vector {}(file.spath()); filename = mSettings.plistOutput + noSuffixFilename + "_" + std::to_string(fileNameHash) + ".plist"; - mLogger->openPlist(filename, files); + mLogger->openPlist(filename); } } @@ -1000,24 +1009,16 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str if (preprocessor.reportOutput(outputList, true)) return mLogger->exitcode(); - if (!preprocessor.loadFiles(files)) - return mLogger->exitcode(); - - checkPlistOutput(file, files); + checkPlistOutput(file); - std::string dumpProlog; + std::string dumpFooter; if (mSettings.dump || !mSettings.addons.empty()) { - dumpProlog += getDumpFileContentsRawTokens(files, tokens1); + dumpFooter += getDumpFileContentsRawTokensFooter(tokens1); } // Parse comments and then remove them - mLogger->setRemarkComments(preprocessor.getRemarkComments()); + preprocessor.addRemarkComments(mLogger->remarkComments()); preprocessor.inlineSuppressions(mSuppressions.nomsg); - if (mSettings.dump || !mSettings.addons.empty()) { - std::ostringstream oss; - mSuppressions.nomsg.dump(oss); - dumpProlog += oss.str(); - } preprocessor.removeComments(); if (!mSettings.buildDir.empty()) { @@ -1040,19 +1041,52 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str } // Get directives - std::list directives = preprocessor.createDirectives(); + std::list directives; + preprocessor.createDirectives(directives); preprocessor.simplifyPragmaAsm(); + // This needs to be a linked list to allow new configurations to be discovered + // and added while iterating and checking existing configurations + std::list configurations; + std::set configDefines = { "__cplusplus" }; + + // Insert library defines + for (const auto &define : mSettings.library.defines()) { + const std::string::size_type paren = define.find("("); + const std::string::size_type space = define.find(" "); + std::string::size_type end = space; + + if (paren != std::string::npos && paren < space) + end = paren; + + configDefines.insert(define.substr(0, end)); + } + + // cppcheck-suppress danglingLifetime - false positive: lambda is owned by preprocessor + preprocessor.setLoadCallback([&](simplecpp::FileData &data) { + // Do preprocessing on included file + preprocessor.addRemarkComments(data.tokens, mLogger->remarkComments()); + preprocessor.inlineSuppressions(data.tokens, mSuppressions.nomsg); + Preprocessor::removeComments(data.tokens); + Preprocessor::createDirectives(data.tokens, directives); + Preprocessor::simplifyPragmaAsm(data.tokens); + // Discover new configurations from included file + if (maxConfigs > 1) + preprocessor.getConfigs(data.filename, data.tokens, configDefines, configurations); + }); + preprocessor.setPlatformInfo(); // Get configurations.. - std::set configurations; if (maxConfigs > 1) { Timer::run("Preprocessor::getConfigs", mTimerResults, [&]() { - configurations = preprocessor.getConfigs(); + configurations = { "" }; + preprocessor.getConfigs(configDefines, configurations); + preprocessor.loadFiles(files); + configurations.sort(); }); } else { - configurations.insert(mSettings.userDefines); + configurations = { mSettings.userDefines }; } if (mSettings.checkConfiguration) { @@ -1089,7 +1123,6 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str createDumpFile(mSettings, file, fdump, dumpFile); if (fdump.is_open()) { fdump << getLibraryDumpData(); - fdump << dumpProlog; if (!mSettings.dump) filesDeleter.addFile(dumpFile); } @@ -1259,12 +1292,20 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str } // TODO: will not be closed if we encountered an exception - // dumped all configs, close root element now if (fdump.is_open()) { + // dump all filenames, raw tokens, suppressions + std::string dumpHeader = getDumpFileContentsRawTokensHeader(files); + fdump << getDumpFileContentsRawTokens(dumpHeader, dumpFooter); + mSuppressions.nomsg.dump(fdump); + // dumped all configs, close root element now fdump << "" << std::endl; fdump.close(); } + if (!mSettings.plistOutput.empty()) { + mLogger->setPlistFilenames(std::move(files)); + } + executeAddons(dumpFile, file); } catch (const TerminateException &) { // Analysis is terminated @@ -1892,9 +1933,26 @@ bool CppCheck::isPremiumCodingStandardId(const std::string& id) const { return false; } -std::string CppCheck::getDumpFileContentsRawTokens(const std::vector& files, const simplecpp::TokenList& tokens1) const { +std::string CppCheck::getDumpFileContentsRawTokens(const std::vector& files, const simplecpp::TokenList& tokens1) const +{ + std::string header = getDumpFileContentsRawTokensHeader(files); + std::string footer = getDumpFileContentsRawTokensFooter(tokens1); + return getDumpFileContentsRawTokens(header, footer); +} + +std::string CppCheck::getDumpFileContentsRawTokens(const std::string& header, const std::string& footer) +{ std::string dumpProlog; dumpProlog += " \n"; + dumpProlog += header; + dumpProlog += footer; + dumpProlog += " \n"; + return dumpProlog; +} + +std::string CppCheck::getDumpFileContentsRawTokensHeader(const std::vector& files) const +{ + std::string dumpProlog; for (unsigned int i = 0; i < files.size(); ++i) { dumpProlog += " \n"; } + return dumpProlog; +} + +std::string CppCheck::getDumpFileContentsRawTokensFooter(const simplecpp::TokenList& tokens1) +{ + std::string dumpProlog; for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) { dumpProlog += " location.line); dumpProlog += "\" "; - dumpProlog +="column=\""; + dumpProlog += "column=\""; dumpProlog += std::to_string(tok->location.col); dumpProlog += "\" "; @@ -1923,6 +1987,5 @@ std::string CppCheck::getDumpFileContentsRawTokens(const std::vector contents, this is only public for testing purposes */ std::string getDumpFileContentsRawTokens(const std::vector& files, const simplecpp::TokenList& tokens1) const; + static std::string getDumpFileContentsRawTokens(const std::string& header, const std::string& footer); + std::string getDumpFileContentsRawTokensHeader(const std::vector& files) const; + static std::string getDumpFileContentsRawTokensFooter(const simplecpp::TokenList& tokens1); std::string getLibraryDumpData() const; @@ -183,7 +186,7 @@ class CPPCHECKLIB CppCheck { */ unsigned int checkFile(const FileWithDetails& file, const std::string &cfgname); - void checkPlistOutput(const FileWithDetails& file, const std::vector& files); + void checkPlistOutput(const FileWithDetails& file); /** * @brief Check a file using buffer diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index 3b05a7d5a8f..d58c5abcbba 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -821,7 +821,7 @@ std::string ErrorLogger::toxml(const std::string &str) return xml; } -std::string ErrorLogger::plistHeader(const std::string &version, const std::vector &files) +std::string ErrorLogger::plistHeader(const std::string &version) { std::ostringstream ostr; ostr << "\r\n" @@ -829,12 +829,7 @@ std::string ErrorLogger::plistHeader(const std::string &version, const std::vect << "\r\n" << "\r\n" << " clang_version\r\n" - << "cppcheck version " << version << "\r\n" - << " files\r\n" - << " \r\n"; - for (const std::string & file : files) - ostr << " " << ErrorLogger::toxml(file) << "\r\n"; - ostr << " \r\n" + << " cppcheck version " << version << "\r\n" << " diagnostics\r\n" << " \r\n"; return ostr.str(); diff --git a/lib/errorlogger.h b/lib/errorlogger.h index daf683bd0a3..97cc3e7f82e 100644 --- a/lib/errorlogger.h +++ b/lib/errorlogger.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -271,12 +272,19 @@ class CPPCHECKLIB ErrorLogger { */ static std::string toxml(const std::string &str); - static std::string plistHeader(const std::string &version, const std::vector &files); + static std::string plistHeader(const std::string &version); static std::string plistData(const ErrorMessage &msg); - static const char *plistFooter() { - return " \r\n" - "\r\n" - ""; + static std::string plistFooter(const std::vector& files) { + std::ostringstream ostr; + ostr << " \r\n" + << " files\r\n" + << " \r\n"; + for (const std::string& file : files) + ostr << " " << ErrorLogger::toxml(file) << "\r\n"; + ostr << " \r\n" + << "\r\n" + << ""; + return ostr.str(); } static bool isCriticalErrorId(const std::string& id) { diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 4246da39c83..3d32f9e837d 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -336,20 +336,23 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett } } -void Preprocessor::inlineSuppressions(SuppressionList &suppressions) +void Preprocessor::inlineSuppressions(SuppressionList &suppressions) const +{ + inlineSuppressions(mTokens, suppressions); +} + +void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens, SuppressionList &suppressions) const { if (!mSettings.inlineSuppressions) return; std::list err; - ::addInlineSuppressions(mTokens, mSettings, suppressions, err); - for (const auto &filedata : mFileCache) { - ::addInlineSuppressions(filedata->tokens, mSettings, suppressions, err); - } + ::addInlineSuppressions(tokens, mSettings, suppressions, err); for (const BadInlineSuppression &bad : err) { invalidSuppression(bad.location, bad.errmsg); } } +// cppcheck-suppress unusedFunction - only used in tests std::vector Preprocessor::getRemarkComments() const { std::vector ret; @@ -360,43 +363,33 @@ std::vector Preprocessor::getRemarkComments() const return ret; } -std::list Preprocessor::createDirectives() const +void Preprocessor::createDirectives(std::list &directives) const { - // directive list.. - std::list directives; - - std::vector list; - list.reserve(1U + mFileCache.size()); - list.push_back(&mTokens); - std::transform(mFileCache.cbegin(), mFileCache.cend(), std::back_inserter(list), - [](const std::unique_ptr &filedata) { - return &filedata->tokens; - }); + createDirectives(mTokens, directives); +} - for (const simplecpp::TokenList *tokenList : list) { - for (const simplecpp::Token *tok = tokenList->cfront(); tok; tok = tok->next) { - if ((tok->op != '#') || (tok->previous && tok->previous->location.line == tok->location.line)) - continue; - if (tok->next && tok->next->str() == "endfile") +void Preprocessor::createDirectives(const simplecpp::TokenList &tokens, std::list &directives) +{ + for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { + if ((tok->op != '#') || (tok->previous && tok->previous->location.line == tok->location.line)) + continue; + if (tok->next && tok->next->str() == "endfile") + continue; + Directive directive(tokens, tok->location, ""); + for (const simplecpp::Token *tok2 = tok; tok2 && tok2->location.line == directive.linenr; tok2 = tok2->next) { + if (tok2->comment) continue; - Directive directive(mTokens, tok->location, ""); - for (const simplecpp::Token *tok2 = tok; tok2 && tok2->location.line == directive.linenr; tok2 = tok2->next) { - if (tok2->comment) - continue; - if (!directive.str.empty() && (tok2->location.col > tok2->previous->location.col + tok2->previous->str().size())) - directive.str += ' '; - if (directive.str == "#" && tok2->str() == "file") - directive.str += "include"; - else - directive.str += tok2->str(); - - directive.strTokens.emplace_back(*tok2); - } - directives.push_back(std::move(directive)); + if (!directive.str.empty() && (tok2->location.col > tok2->previous->location.col + tok2->previous->str().size())) + directive.str += ' '; + if (directive.str == "#" && tok2->str() == "file") + directive.str += "include"; + else + directive.str += tok2->str(); + + directive.strTokens.emplace_back(*tok2); } + directives.push_back(std::move(directive)); } - - return directives; } static std::string readcondition(const simplecpp::Token *iftok, const std::set &defined, const std::set &undefined) @@ -596,7 +589,13 @@ static const simplecpp::Token *gotoEndIf(const simplecpp::Token *cmdtok) return nullptr; } -static void getConfigs(const simplecpp::TokenList &tokens, std::set &defined, const std::string &userDefines, const std::set &undefined, std::set &ret) +static void insertUniqueCfg(std::list &configs, std::string &&config) +{ + if (std::find(configs.begin(), configs.end(), config) == configs.end()) + configs.push_back(std::move(config)); +} + +static void getConfigs(const simplecpp::TokenList &tokens, std::set &defined, const std::string &userDefines, const std::set &undefined, std::list &ret) { std::vector configs_if; std::vector configs_ifndef; @@ -658,12 +657,12 @@ static void getConfigs(const simplecpp::TokenList &tokens, std::set } } - // check if config already exists in the ret set, but as a more general or more specific version + // check if config already exists in the ret list, but as a more general or more specific version if (cmdtok->str() != "ifndef") { const std::string::size_type eq = config.find('='); const std::string config2 = (eq != std::string::npos) ? config.substr(0, eq) : config + "=" + config; - const std::set::iterator it2 = ret.find(config2); + const std::list::iterator it2 = std::find(ret.begin(), ret.end(), config2); if (it2 != ret.end()) { if (eq == std::string::npos) { // The instance in ret is more specific than the one in config (no =value), replace it with the one in config @@ -678,7 +677,7 @@ static void getConfigs(const simplecpp::TokenList &tokens, std::set configs_if.push_back((cmdtok->str() == "ifndef") ? std::string() : config); configs_ifndef.push_back((cmdtok->str() == "ifndef") ? std::move(config) : std::string()); - ret.insert(cfg(configs_if,userDefines)); + insertUniqueCfg(ret, cfg(configs_if,userDefines)); } else if (cmdtok->str() == "elif" || cmdtok->str() == "else") { if (getConfigsElseIsFalse(configs_if,userDefines)) { tok = gotoEndIf(tok); @@ -707,19 +706,19 @@ static void getConfigs(const simplecpp::TokenList &tokens, std::set if (isUndefined(config,undefined)) config.clear(); configs_if.push_back(std::move(config)); - ret.insert(cfg(configs_if, userDefines)); + insertUniqueCfg(ret, cfg(configs_if, userDefines)); } else if (!configs_ifndef.empty()) { //Check if ifndef already existing in ret as more general/specific version const std::string &confCandidate = configs_ifndef.back(); - if (ret.find(confCandidate) == ret.end()) { + if (std::find(ret.begin(), ret.end(), confCandidate) == ret.end()) { // No instance of config_ifndef in ret. Check if a more specific version exists, in that case replace it - const std::set::iterator it = ret.find(confCandidate + "=" + confCandidate); + const std::list::iterator it = std::find(ret.begin(), ret.end(), confCandidate + "=" + confCandidate); if (it != ret.end()) { // The instance in ret is more specific than the one in confCandidate (no =value), replace it with the one in confCandidate ret.erase(it); } configs_if.push_back(configs_ifndef.back()); - ret.insert(cfg(configs_if, userDefines)); + insertUniqueCfg(ret, cfg(configs_if, userDefines)); } } } else if (cmdtok->str() == "endif" && !sameline(tok, cmdtok->next)) { @@ -730,19 +729,19 @@ static void getConfigs(const simplecpp::TokenList &tokens, std::set } else if (cmdtok->str() == "error") { if (!configs_ifndef.empty() && !configs_ifndef.back().empty()) { if (configs_ifndef.size() == 1U) - ret.erase(""); + ret.remove(""); std::vector configs(configs_if); configs.push_back(configs_ifndef.back()); - ret.erase(cfg(configs, userDefines)); - std::set temp; + ret.remove(cfg(configs, userDefines)); + std::list temp; temp.swap(ret); - for (const std::string &c: temp) { + for (std::string &c: temp) { if (c.find(configs_ifndef.back()) != std::string::npos) - ret.insert(c); + insertUniqueCfg(ret, std::move(c)); else if (c.empty()) - ret.insert(""); + insertUniqueCfg(ret, ""); else - ret.insert(c + ";" + configs_ifndef.back()); + insertUniqueCfg(ret, c + ";" + configs_ifndef.back()); } if (!elseError.empty()) elseError += ';'; @@ -752,10 +751,10 @@ static void getConfigs(const simplecpp::TokenList &tokens, std::set const std::string &last = configs_if.back(); if (last.size() > 2U && last.compare(last.size()-2U,2,"=0") == 0) { std::vector configs(configs_if); - ret.erase(cfg(configs, userDefines)); + ret.remove(cfg(configs, userDefines)); configs[configs.size() - 1U] = last.substr(0,last.size()-2U); if (configs.size() == 1U) - ret.erase(""); + ret.remove(""); if (!elseError.empty()) elseError += ';'; elseError += cfg(configs, userDefines); @@ -766,39 +765,24 @@ static void getConfigs(const simplecpp::TokenList &tokens, std::set } } if (!elseError.empty()) - ret.insert(std::move(elseError)); + insertUniqueCfg(ret, std::move(elseError)); } - -std::set Preprocessor::getConfigs() const +void Preprocessor::getConfigs(std::set &defined, std::list &configs) const { - std::set ret = { "" }; if (!mTokens.cfront()) - return ret; - - std::set defined = { "__cplusplus" }; - - // Insert library defines - for (const auto &define : mSettings.library.defines()) { - - const std::string::size_type paren = define.find("("); - const std::string::size_type space = define.find(" "); - std::string::size_type end = space; - - if (paren != std::string::npos && paren < space) - end = paren; - - defined.insert(define.substr(0, end)); - } + return; - ::getConfigs(mTokens, defined, mSettings.userDefines, mSettings.userUndefs, ret); + ::getConfigs(mTokens, defined, mSettings.userDefines, mSettings.userUndefs, configs); +} - for (const auto &filedata : mFileCache) { - if (!mSettings.configurationExcluded(filedata->filename)) - ::getConfigs(filedata->tokens, defined, mSettings.userDefines, mSettings.userUndefs, ret); - } +void Preprocessor::getConfigs(const std::string &filename, const simplecpp::TokenList &tokens, std::set &defined, std::list &configs) const +{ + if (!tokens.cfront()) + return; - return ret; + if (!mSettings.configurationExcluded(filename)) + ::getConfigs(tokens, defined, mSettings.userDefines, mSettings.userUndefs, configs); } static void splitcfg(const std::string &cfgStr, std::list &defines, const std::string &defaultValue) @@ -871,16 +855,18 @@ bool Preprocessor::loadFiles(std::vector &files) const simplecpp::DUI dui = createDUI(mSettings, "", mLang); simplecpp::OutputList outputList; - mFileCache = simplecpp::load(mTokens, files, dui, &outputList); + mFileCache = simplecpp::load(mTokens, files, dui, &outputList, std::move(mFileCache)); return !handleErrors(outputList); } void Preprocessor::removeComments() { - mTokens.removeComments(); - for (const auto &filedata : mFileCache) { - filedata->tokens.removeComments(); - } + removeComments(mTokens); +} + +void Preprocessor::removeComments(simplecpp::TokenList &tokens) +{ + tokens.removeComments(); } void Preprocessor::setPlatformInfo() @@ -1016,12 +1002,12 @@ static std::string simplecppErrToId(simplecpp::Output::Type type) cppcheck::unreachable(); } -void Preprocessor::error(const simplecpp::Location& loc, const std::string &msg, simplecpp::Output::Type type) +void Preprocessor::error(const simplecpp::Location& loc, const std::string &msg, simplecpp::Output::Type type) const { error(loc, msg, simplecppErrToId(type)); } -void Preprocessor::error(const simplecpp::Location& loc, const std::string &msg, const std::string& id) +void Preprocessor::error(const simplecpp::Location& loc, const std::string &msg, const std::string& id) const { std::list locationList; if (!mTokens.file(loc).empty()) { @@ -1059,7 +1045,7 @@ void Preprocessor::missingInclude(const simplecpp::Location& loc, const std::str mErrorLogger.reportErr(errmsg); } -void Preprocessor::invalidSuppression(const simplecpp::Location& loc, const std::string &msg) +void Preprocessor::invalidSuppression(const simplecpp::Location& loc, const std::string &msg) const { error(loc, msg, "invalidSuppression"); } @@ -1143,13 +1129,10 @@ std::size_t Preprocessor::calculateHash(const std::string &toolinfo) const void Preprocessor::simplifyPragmaAsm() { - Preprocessor::simplifyPragmaAsmPrivate(mTokens); - for (const auto &filedata : mFileCache) { - Preprocessor::simplifyPragmaAsmPrivate(filedata->tokens); - } + simplifyPragmaAsm(mTokens); } -void Preprocessor::simplifyPragmaAsmPrivate(simplecpp::TokenList &tokenList) +void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList &tokenList) { // assembler code.. for (simplecpp::Token *tok = tokenList.front(); tok; tok = tok->next) { @@ -1196,6 +1179,10 @@ void Preprocessor::simplifyPragmaAsmPrivate(simplecpp::TokenList &tokenList) } } +void Preprocessor::addRemarkComments(std::vector& remarkComments) const +{ + addRemarkComments(mTokens, remarkComments); +} void Preprocessor::addRemarkComments(const simplecpp::TokenList &tokens, std::vector &remarkComments) const { diff --git a/lib/preprocessor.h b/lib/preprocessor.h index ba4d55a1e60..07abd72011e 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -104,18 +104,30 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { Preprocessor(simplecpp::TokenList& tokens, const Settings& settings, ErrorLogger &errorLogger, Standards::Language lang); - void inlineSuppressions(SuppressionList &suppressions); + void inlineSuppressions(SuppressionList &suppressions) const; - std::list createDirectives() const; + void inlineSuppressions(const simplecpp::TokenList &tokens, SuppressionList &suppressions) const; - std::set getConfigs() const; + void createDirectives(std::list &directives) const; + + static void createDirectives(const simplecpp::TokenList &tokens, std::list &directives); + + void getConfigs(std::set &defined, std::list &configs) const; + + void getConfigs(const std::string &filename, const simplecpp::TokenList &tokens, std::set &defined, std::list &configs) const; std::vector getRemarkComments() const; + void addRemarkComments(std::vector &remarkComments) const; + + void addRemarkComments(const simplecpp::TokenList &tokens, std::vector &remarkComments) const; + bool loadFiles(std::vector &files); void removeComments(); + static void removeComments(simplecpp::TokenList &tokens); + void setPlatformInfo(); simplecpp::TokenList preprocess(const std::string &cfgStr, std::vector &files, simplecpp::OutputList& outputList); @@ -132,6 +144,8 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { void simplifyPragmaAsm(); + static void simplifyPragmaAsm(simplecpp::TokenList &tokenList); + static void getErrorMessages(ErrorLogger &errorLogger, const Settings &settings); /** @@ -141,12 +155,15 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { const simplecpp::Output* reportOutput(const simplecpp::OutputList &outputList, bool showerror); - void error(const simplecpp::Location& loc, const std::string &msg, simplecpp::Output::Type type); + void error(const simplecpp::Location& loc, const std::string &msg, simplecpp::Output::Type type) const; const simplecpp::Output* handleErrors(const simplecpp::OutputList &outputList); + void setLoadCallback(simplecpp::FileDataCache::callback_type cb) { + mFileCache.set_callback(std::move(cb)); + } + private: - static void simplifyPragmaAsmPrivate(simplecpp::TokenList &tokenList); /** * Include file types. @@ -157,18 +174,14 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { }; void missingInclude(const simplecpp::Location& loc, const std::string &header, HeaderTypes headerType); - void invalidSuppression(const simplecpp::Location& loc, const std::string &msg); - void error(const simplecpp::Location& loc, const std::string &msg, const std::string& id); - - void addRemarkComments(const simplecpp::TokenList &tokens, std::vector &remarkComments) const; + void invalidSuppression(const simplecpp::Location& loc, const std::string &msg) const; + void error(const simplecpp::Location& loc, const std::string &msg, const std::string& id) const; simplecpp::TokenList& mTokens; const Settings& mSettings; ErrorLogger &mErrorLogger; - /** list of all directives met while preprocessing file */ - simplecpp::FileDataCache mFileCache; /** filename for cpp/c file - useful when reporting errors */ diff --git a/test/helpers.cpp b/test/helpers.cpp index 252658cc531..70437207a84 100644 --- a/test/helpers.cpp +++ b/test/helpers.cpp @@ -124,7 +124,8 @@ void SimpleTokenizer2::preprocess(const char* code, std::size_t size, std::vecto // Tokenizer.. tokenizer.list.createTokens(std::move(tokens2)); - std::list directives = preprocessor.createDirectives(); + std::list directives; + preprocessor.createDirectives(directives); tokenizer.setDirectives(std::move(directives)); } diff --git a/test/testcppcheck.cpp b/test/testcppcheck.cpp index 84db6db6c28..17c5b8eff3c 100644 --- a/test/testcppcheck.cpp +++ b/test/testcppcheck.cpp @@ -527,7 +527,6 @@ class TestCppcheck : public TestFixture { void checkPlistOutput() const { Suppressions supprs; ErrorLogger2 errorLogger; - std::vector files = {"textfile.txt"}; { const auto s = dinit(Settings, $.templateFormat = templateFormat, $.plistOutput = "output"); @@ -535,7 +534,7 @@ class TestCppcheck : public TestFixture { CppCheck cppcheck(s, supprs, errorLogger, nullptr, false, {}); const FileWithDetails fileWithDetails {file.path(), Path::identify(file.path(), false), 0}; - cppcheck.checkPlistOutput(fileWithDetails, files); + cppcheck.checkPlistOutput(fileWithDetails); const std::string outputFile {"outputfile_" + std::to_string(std::hash {}(fileWithDetails.spath())) + ".plist"}; ASSERT(Path::exists(outputFile)); std::remove(outputFile.c_str()); @@ -547,7 +546,7 @@ class TestCppcheck : public TestFixture { CppCheck cppcheck(s, supprs, errorLogger, nullptr, false, {}); const FileWithDetails fileWithDetails {file.path(), Path::identify(file.path(), false), 0}; - cppcheck.checkPlistOutput(fileWithDetails, files); + cppcheck.checkPlistOutput(fileWithDetails); const std::string outputFile {"outputfile_" + std::to_string(std::hash {}(fileWithDetails.spath())) + ".plist"}; ASSERT(Path::exists(outputFile)); std::remove(outputFile.c_str()); @@ -557,7 +556,7 @@ class TestCppcheck : public TestFixture { Settings s; const ScopedFile file("file.c", ""); CppCheck cppcheck(s, supprs, errorLogger, nullptr, false, {}); - cppcheck.checkPlistOutput(FileWithDetails(file.path(), Path::identify(file.path(), false), 0), files); + cppcheck.checkPlistOutput(FileWithDetails(file.path(), Path::identify(file.path(), false), 0)); } } diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index b90a879738d..188ed99ba2e 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -98,7 +98,7 @@ class TestPreprocessor : public TestFixture { static std::string getcodeforcfg(const Settings& settings, ErrorLogger& errorlogger, const char* code, std::size_t size, const std::string &cfg, const std::string &filename, SuppressionList *inlineSuppression = nullptr) { - std::map cfgcode = getcode(settings, errorlogger, code, size, std::set{cfg}, filename, inlineSuppression); + std::map cfgcode = getcode(settings, errorlogger, code, size, std::list{cfg}, filename, inlineSuppression); const auto it = cfgcode.find(cfg); if (it == cfgcode.end()) return ""; @@ -117,7 +117,7 @@ class TestPreprocessor : public TestFixture { return getcode(settings, errorlogger, code, size-1, {}, filename, nullptr); } - static std::map getcode(const Settings& settings, ErrorLogger& errorlogger, const char* code, std::size_t size, std::set cfgs, const std::string &filename, SuppressionList *inlineSuppression) + static std::map getcode(const Settings& settings, ErrorLogger& errorlogger, const char* code, std::size_t size, std::list cfgs, const std::string &filename, SuppressionList *inlineSuppression) { simplecpp::OutputList outputList; std::vector files; @@ -137,8 +137,11 @@ class TestPreprocessor : public TestFixture { preprocessor.simplifyPragmaAsm(); std::map cfgcode; - if (cfgs.empty()) - cfgs = preprocessor.getConfigs(); + if (cfgs.empty()) { + cfgs.emplace_back(""); + std::set configDefines = { "__cplusplus" }; + preprocessor.getConfigs(configDefines, cfgs); + } for (const std::string & config : cfgs) { try { const bool writeLocations = (strstr(code, "#include") != nullptr); @@ -394,10 +397,25 @@ class TestPreprocessor : public TestFixture { simplecpp::OutputList outputList; simplecpp::TokenList tokens(code,files,"test.c",&outputList); Preprocessor preprocessor(tokens, settings, *this, Standards::Language::C); + std::list configs = { "" }; + std::set configDefines = { "__cplusplus" }; + for (const auto &define : settings.library.defines()) { + const std::string::size_type paren = define.find("("); + const std::string::size_type space = define.find(" "); + std::string::size_type end = space; + if (paren != std::string::npos && paren < space) + end = paren; + configDefines.insert(define.substr(0, end)); + } + preprocessor.setLoadCallback([&](simplecpp::FileData &data) { + Preprocessor::removeComments(data.tokens); + preprocessor.getConfigs(data.filename, data.tokens, configDefines, configs); + }); + preprocessor.removeComments(); + preprocessor.getConfigs(configDefines, configs); ASSERT(preprocessor.loadFiles(files)); ASSERT(!preprocessor.reportOutput(outputList, true)); - preprocessor.removeComments(); - const std::set configs = preprocessor.getConfigs(); + configs.sort(); std::string ret; for (const std::string & config : configs) ret += config + '\n'; diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 167d5411553..0617007744a 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -603,9 +603,13 @@ class TestTokenizer : public TestFixture { std::vector files; simplecpp::TokenList tokens1(code, files, filename, &outputList); Preprocessor preprocessor(tokens1, settings, *this, Path::identify(tokens1.getFiles()[0], false)); - (void)preprocessor.reportOutput(outputList, true); + std::list directives; + preprocessor.setLoadCallback([&](const simplecpp::FileData &data) { + Preprocessor::createDirectives(data.tokens, directives); + }); + preprocessor.createDirectives(directives); ASSERT(preprocessor.loadFiles(files)); - std::list directives = preprocessor.createDirectives(); + (void)preprocessor.reportOutput(outputList, true); TokenList tokenlist{settings, Path::identify(filename, false)}; Tokenizer tokenizer(std::move(tokenlist), *this);