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/.selfcheck_suppressions b/.selfcheck_suppressions index 402fdbeb75d..4cdc8fec863 100644 --- a/.selfcheck_suppressions +++ b/.selfcheck_suppressions @@ -62,6 +62,8 @@ templateInstantiation:test/testutils.cpp naming-varname:externals/simplecpp/simplecpp.h naming-privateMemberVariable:externals/simplecpp/simplecpp.h +# false positive; lambda captures its owner +danglingLifetime:externals/simplecpp/simplecpp.h:505 # TODO: these warnings need to be addressed upstream uninitMemberVar:externals/tinyxml2/tinyxml2.h diff --git a/externals/simplecpp/simplecpp.cpp b/externals/simplecpp/simplecpp.cpp index 47e674837ba..cba58908962 100644 --- a/externals/simplecpp/simplecpp.cpp +++ b/externals/simplecpp/simplecpp.cpp @@ -3,18 +3,12 @@ * Copyright (C) 2016-2023 simplecpp team */ +// needs to be specified here otherwise _mingw.h will define it as 0x0601 +// causing FileIdInfo not to be available #if defined(_WIN32) # ifndef _WIN32_WINNT # define _WIN32_WINNT 0x0602 # endif -# ifndef NOMINMAX -# define NOMINMAX -# endif -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# include -# undef ERROR #endif #include "simplecpp.h" @@ -51,10 +45,19 @@ #include #include -#ifdef _WIN32 +#if defined(_WIN32) +# ifndef NOMINMAX +# define NOMINMAX +# endif +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# undef ERROR # include #else # include +# include #endif static bool isHex(const std::string &s) @@ -658,8 +661,6 @@ static const std::string COMMENT_END("*/"); void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, OutputList *outputList) { - std::stack loc; - unsigned int multiline = 0U; const Token *oldLastToken = nullptr; @@ -698,59 +699,44 @@ void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, if (oldLastToken != cback()) { oldLastToken = cback(); - const Token * const llTok = isLastLinePreprocessor(); - if (!llTok) + + // #line 3 + // #line 3 "file.c" + // #3 + // #3 "file.c" + const Token * ppTok = isLastLinePreprocessor(); + if (!ppTok) continue; - const Token * const llNextToken = llTok->next; - if (!llTok->next) + + const auto advanceAndSkipComments = [](const Token* tok) { + do { + tok = tok->next; + } while (tok && tok->comment); + return tok; + }; + + // skip # + ppTok = advanceAndSkipComments(ppTok); + if (!ppTok) continue; - if (llNextToken->next) { - // #file "file.c" - if (llNextToken->str() == "file" && - llNextToken->next->str()[0] == '\"') - { - const Token *strtok = cback(); - while (strtok->comment) - strtok = strtok->previous; - loc.push(location); - location.fileIndex = fileIndex(strtok->str().substr(1U, strtok->str().size() - 2U)); - location.line = 1U; - } - // TODO: add support for "# 3" - // #3 "file.c" - // #line 3 "file.c" - else if ((llNextToken->number && - llNextToken->next->str()[0] == '\"') || - (llNextToken->str() == "line" && - llNextToken->next->number && - llNextToken->next->next && - llNextToken->next->next->str()[0] == '\"')) - { - const Token *strtok = cback(); - while (strtok->comment) - strtok = strtok->previous; - const Token *numtok = strtok->previous; - while (numtok->comment) - numtok = numtok->previous; - lineDirective(fileIndex(replaceAll(strtok->str().substr(1U, strtok->str().size() - 2U),"\\\\","\\")), - std::atol(numtok->str().c_str()), location); - } - // #line 3 - else if (llNextToken->str() == "line" && - llNextToken->next->number) - { - const Token *numtok = cback(); - while (numtok->comment) - numtok = numtok->previous; - lineDirective(location.fileIndex, std::atol(numtok->str().c_str()), location); - } - } - // #endfile - else if (llNextToken->str() == "endfile" && !loc.empty()) - { - location = loc.top(); - loc.pop(); - } + + if (ppTok->str() == "line") + ppTok = advanceAndSkipComments(ppTok); + + if (!ppTok || !ppTok->number) + continue; + + const unsigned int line = std::atol(ppTok->str().c_str()); + ppTok = advanceAndSkipComments(ppTok); + + unsigned int fileindex; + + if (ppTok && ppTok->str()[0] == '\"') + fileindex = fileIndex(replaceAll(ppTok->str().substr(1U, ppTok->str().size() - 2U),"\\\\","\\")); + else + fileindex = location.fileIndex; + + lineDirective(fileindex, line, location); } continue; @@ -1040,7 +1026,7 @@ void simplecpp::TokenList::combineOperators() continue; } const Token *prev = tok->previous; - while (prev && prev->isOneOf(";{}()")) + while (prev && prev->isOneOf(";{}(")) prev = prev->previous; executableScope.push(prev && prev->op == ')'); continue; @@ -2332,9 +2318,6 @@ namespace simplecpp { const Token *nextTok = B->next; if (canBeConcatenatedStringOrChar) { - if (unexpectedA) - throw invalidHashHash::unexpectedToken(tok->location, name(), A); - // It seems clearer to handle this case separately even though the code is similar-ish, but we don't want to merge here. // TODO The question is whether the ## or varargs may still apply, and how to provoke? if (expandArg(tokensB, B, parametertokens)) { @@ -3090,6 +3073,65 @@ static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const return ""; } +namespace { + struct FileID { +#ifdef _WIN32 + struct { + std::uint64_t VolumeSerialNumber; + struct { + std::uint64_t IdentifierHi; + std::uint64_t IdentifierLo; + } FileId; + } fileIdInfo; + + bool operator==(const FileID &that) const noexcept { + return fileIdInfo.VolumeSerialNumber == that.fileIdInfo.VolumeSerialNumber && + fileIdInfo.FileId.IdentifierHi == that.fileIdInfo.FileId.IdentifierHi && + fileIdInfo.FileId.IdentifierLo == that.fileIdInfo.FileId.IdentifierLo; + } +#else + dev_t dev; + ino_t ino; + + bool operator==(const FileID& that) const noexcept { + return dev == that.dev && ino == that.ino; + } +#endif + struct Hasher { + std::size_t operator()(const FileID &id) const { +#ifdef _WIN32 + return static_cast(id.fileIdInfo.FileId.IdentifierHi ^ id.fileIdInfo.FileId.IdentifierLo ^ + id.fileIdInfo.VolumeSerialNumber); +#else + return static_cast(id.dev) ^ static_cast(id.ino); +#endif + } + }; + }; +} + +struct simplecpp::FileDataCache::Impl +{ + void clear() + { + mIdMap.clear(); + } + + using id_map_type = std::unordered_map; + + id_map_type mIdMap; +}; + +simplecpp::FileDataCache::FileDataCache() + : mImpl(new Impl) +{} + +simplecpp::FileDataCache::~FileDataCache() = default; +simplecpp::FileDataCache::FileDataCache(FileDataCache &&) noexcept = default; +simplecpp::FileDataCache &simplecpp::FileDataCache::operator=(simplecpp::FileDataCache &&) noexcept = default; + +static bool getFileId(const std::string &path, FileID &id); + std::pair simplecpp::FileDataCache::tryload(FileDataCache::name_map_type::iterator &name_it, const simplecpp::DUI &dui, std::vector &filenames, simplecpp::OutputList *outputList) { const std::string &path = name_it->first; @@ -3098,8 +3140,8 @@ std::pair simplecpp::FileDataCache::tryload(FileDat if (!getFileId(path, fileId)) return {nullptr, false}; - const auto id_it = mIdMap.find(fileId); - if (id_it != mIdMap.end()) { + const auto id_it = mImpl->mIdMap.find(fileId); + if (id_it != mImpl->mIdMap.end()) { name_it->second = id_it->second; return {id_it->second, false}; } @@ -3110,9 +3152,12 @@ std::pair simplecpp::FileDataCache::tryload(FileDat data->tokens.removeComments(); name_it->second = data; - mIdMap.emplace(fileId, data); + mImpl->mIdMap.emplace(fileId, data); mData.emplace_back(data); + if (mLoadCallback) + mLoadCallback(*data); + return {data, true}; } @@ -3162,7 +3207,14 @@ std::pair simplecpp::FileDataCache::get(const std:: return {nullptr, false}; } -bool simplecpp::FileDataCache::getFileId(const std::string &path, FileID &id) +void simplecpp::FileDataCache::clear() +{ + mImpl->clear(); + mNameMap.clear(); + mData.clear(); +} + +static bool getFileId(const std::string &path, FileID &id) { #ifdef _WIN32 HANDLE hFile = CreateFileA(path.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); @@ -3349,20 +3401,20 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL std::map sizeOfType(rawtokens.sizeOfType); sizeOfType.insert(std::make_pair("char", sizeof(char))); sizeOfType.insert(std::make_pair("short", sizeof(short))); - sizeOfType.insert(std::make_pair("short int", sizeOfType["short"])); + sizeOfType.insert(std::make_pair("short int", sizeof(short))); sizeOfType.insert(std::make_pair("int", sizeof(int))); sizeOfType.insert(std::make_pair("long", sizeof(long))); - sizeOfType.insert(std::make_pair("long int", sizeOfType["long"])); + sizeOfType.insert(std::make_pair("long int", sizeof(long))); sizeOfType.insert(std::make_pair("long long", sizeof(long long))); sizeOfType.insert(std::make_pair("float", sizeof(float))); sizeOfType.insert(std::make_pair("double", sizeof(double))); sizeOfType.insert(std::make_pair("long double", sizeof(long double))); sizeOfType.insert(std::make_pair("char *", sizeof(char *))); sizeOfType.insert(std::make_pair("short *", sizeof(short *))); - sizeOfType.insert(std::make_pair("short int *", sizeOfType["short *"])); + sizeOfType.insert(std::make_pair("short int *", sizeof(short *))); sizeOfType.insert(std::make_pair("int *", sizeof(int *))); sizeOfType.insert(std::make_pair("long *", sizeof(long *))); - sizeOfType.insert(std::make_pair("long int *", sizeOfType["long *"])); + sizeOfType.insert(std::make_pair("long int *", sizeof(long *))); sizeOfType.insert(std::make_pair("long long *", sizeof(long long *))); sizeOfType.insert(std::make_pair("float *", sizeof(float *))); sizeOfType.insert(std::make_pair("double *", sizeof(double *))); @@ -3466,8 +3518,19 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL includetokenstack.push(rawtokens.cfront()); for (auto it = dui.includes.cbegin(); it != dui.includes.cend(); ++it) { const FileData *const filedata = cache.get("", *it, dui, false, files, outputList).first; - if (filedata != nullptr && filedata->tokens.cfront() != nullptr) + if (filedata == nullptr) { + if (outputList) { + simplecpp::Output err{ + simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND, + {}, + "Can not open include file '" + *it + "' that is explicitly included." + }; + outputList->emplace_back(std::move(err)); + } + } + else if (filedata->tokens.cfront() != nullptr) { includetokenstack.push(filedata->tokens.cfront()); + } } std::map> maybeUsedMacros; diff --git a/externals/simplecpp/simplecpp.h b/externals/simplecpp/simplecpp.h index f29166ff061..3d6fc5262b9 100644 --- a/externals/simplecpp/simplecpp.h +++ b/externals/simplecpp/simplecpp.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -41,10 +42,6 @@ # define SIMPLECPP_LIB #endif -#ifndef _WIN32 -# include -#endif - #if defined(_MSC_VER) # pragma warning(push) // suppress warnings about "conversion from 'type1' to 'type2', possible loss of data" @@ -452,13 +449,14 @@ namespace simplecpp { class SIMPLECPP_LIB FileDataCache { public: - FileDataCache() = default; + FileDataCache(); + ~FileDataCache(); FileDataCache(const FileDataCache &) = delete; - FileDataCache(FileDataCache &&) = default; + FileDataCache(FileDataCache &&) noexcept; FileDataCache &operator=(const FileDataCache &) = delete; - FileDataCache &operator=(FileDataCache &&) = default; + FileDataCache &operator=(FileDataCache &&) noexcept; /** Get the cached data for a file, or load and then return it if it isn't cached. * returns the file data and true if the file was loaded, false if it was cached. */ @@ -472,11 +470,7 @@ namespace simplecpp { mNameMap.emplace(newdata->filename, newdata); } - void clear() { - mNameMap.clear(); - mIdMap.clear(); - mData.clear(); - } + void clear(); using container_type = std::vector>; using iterator = container_type::iterator; @@ -505,52 +499,23 @@ namespace simplecpp { return mData.cend(); } - private: - struct FileID { -#ifdef _WIN32 - struct { - std::uint64_t VolumeSerialNumber; - struct { - std::uint64_t IdentifierHi; - std::uint64_t IdentifierLo; - } FileId; - } fileIdInfo; - - bool operator==(const FileID &that) const noexcept { - return fileIdInfo.VolumeSerialNumber == that.fileIdInfo.VolumeSerialNumber && - fileIdInfo.FileId.IdentifierHi == that.fileIdInfo.FileId.IdentifierHi && - fileIdInfo.FileId.IdentifierLo == that.fileIdInfo.FileId.IdentifierLo; - } -#else - dev_t dev; - ino_t ino; + using load_callback_type = std::function; - bool operator==(const FileID& that) const noexcept { - return dev == that.dev && ino == that.ino; - } -#endif - struct Hasher { - std::size_t operator()(const FileID &id) const { -#ifdef _WIN32 - return static_cast(id.fileIdInfo.FileId.IdentifierHi ^ id.fileIdInfo.FileId.IdentifierLo ^ - id.fileIdInfo.VolumeSerialNumber); -#else - return static_cast(id.dev) ^ static_cast(id.ino); -#endif - } - }; - }; + void set_load_callback(load_callback_type cb) { + mLoadCallback = std::move(cb); + } - using name_map_type = std::unordered_map; - using id_map_type = std::unordered_map; + private: + struct Impl; + std::unique_ptr mImpl; - static bool getFileId(const std::string &path, FileID &id); + using name_map_type = std::unordered_map; std::pair tryload(name_map_type::iterator &name_it, const DUI &dui, std::vector &filenames, OutputList *outputList); container_type mData; name_map_type mNameMap; - id_map_type mIdMap; + load_callback_type mLoadCallback; }; /** 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..acebaebb14a 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,49 @@ 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)); + } + + 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 (configurations.size() < maxConfigs) + 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); }); } else { - configurations.insert(mSettings.userDefines); + configurations = { mSettings.userDefines }; } if (mSettings.checkConfiguration) { @@ -1089,7 +1120,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 +1289,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 +1930,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 +1984,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/path.cpp b/lib/path.cpp index c4758dfeae9..5ced2baefd7 100644 --- a/lib/path.cpp +++ b/lib/path.cpp @@ -236,7 +236,6 @@ bool Path::acceptFile(const std::string &path, const std::set &extr static bool hasEmacsCppMarker(const char* path) { // TODO: identify is called three times for each file - // Preprocessor::loadFiles() -> createDUI() // Preprocessor::preprocess() -> createDUI() // TokenList::createTokens() -> TokenList::determineCppC() #ifdef LOG_EMACS_MARKER diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 4246da39c83..e7e05f69891 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) @@ -866,21 +850,14 @@ const simplecpp::Output* Preprocessor::handleErrors(const simplecpp::OutputList& return reportOutput(outputList, showerror); } -bool Preprocessor::loadFiles(std::vector &files) +void Preprocessor::removeComments() { - const simplecpp::DUI dui = createDUI(mSettings, "", mLang); - - simplecpp::OutputList outputList; - mFileCache = simplecpp::load(mTokens, files, dui, &outputList); - return !handleErrors(outputList); + removeComments(mTokens); } -void Preprocessor::removeComments() +void Preprocessor::removeComments(simplecpp::TokenList &tokens) { - mTokens.removeComments(); - for (const auto &filedata : mFileCache) { - filedata->tokens.removeComments(); - } + tokens.removeComments(); } void Preprocessor::setPlatformInfo() @@ -1016,12 +993,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 +1036,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 +1120,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 +1170,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..b0e43847e3e 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -104,18 +104,28 @@ 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; - bool loadFiles(std::vector &files); + void addRemarkComments(std::vector &remarkComments) const; + + void addRemarkComments(const simplecpp::TokenList &tokens, std::vector &remarkComments) const; 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 +142,8 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { void simplifyPragmaAsm(); + static void simplifyPragmaAsm(simplecpp::TokenList &tokenList); + static void getErrorMessages(ErrorLogger &errorLogger, const Settings &settings); /** @@ -141,12 +153,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::load_callback_type cb) { + mFileCache.set_load_callback(std::move(cb)); + } + private: - static void simplifyPragmaAsmPrivate(simplecpp::TokenList &tokenList); /** * Include file types. @@ -157,18 +172,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/cli/performance_test.py b/test/cli/performance_test.py index 55da3b3b04e..5be958619fc 100644 --- a/test/cli/performance_test.py +++ b/test/cli/performance_test.py @@ -3,6 +3,7 @@ import os import sys +import time import pytest @@ -413,3 +414,27 @@ class C { } }""") cppcheck([filename]) # should not take more than ~1 second + + +@pytest.mark.timeout(60) +def test_slow_many_headers(tmpdir): + # 14113 + c_file = os.path.join(tmpdir, 'source.c') + h_file = os.path.join(tmpdir, 'header.h') + n_hdr = 128 + with open(c_file, 'wt') as f: + f.write('#include "header.h"\n') + with open(h_file, 'wt') as f: + for i in range(n_hdr): + f.write(f'#ifdef CONFIG{i}\n#include "header{i}.h"\n#endif\n') + for i in range(n_hdr): + h_file_i = os.path.join(tmpdir, f"header{i}.h") + with open(h_file_i, 'wt') as f: + for j in range(2048): + f.write(f'#define MACRO{j}{(" "+str(j))*128}\n') + f.write(f'MACRO{i}\n') + # creating the files used for testing can be slow, so we use perf counter here instead + start = time.perf_counter_ns() + cppcheck(['-DCONFIG0', c_file]) + end = time.perf_counter_ns() + assert end - start < 2 * 10**9 # max 2 sec diff --git a/test/helpers.cpp b/test/helpers.cpp index 252658cc531..d6b2f2e8c77 100644 --- a/test/helpers.cpp +++ b/test/helpers.cpp @@ -117,14 +117,14 @@ void SimpleTokenizer2::preprocess(const char* code, std::size_t size, std::vecto simplecpp::TokenList tokens1({code, size}, files, file0, &outputList); Preprocessor preprocessor(tokens1, tokenizer.getSettings(), errorlogger, Path::identify(tokens1.getFiles()[0], false)); - (void)preprocessor.loadFiles(files); // TODO: check result simplecpp::TokenList tokens2 = preprocessor.preprocess("", files, outputList); (void)preprocessor.reportOutput(outputList, true); // 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..17b2be3679b 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)); } } @@ -574,7 +573,6 @@ class TestCppcheck : public TestFixture { simplecpp::TokenList tokens(code, files, "m1.c"); Preprocessor preprocessor(tokens, settings, errorLogger, Standards::Language::C); - ASSERT(preprocessor.loadFiles(files)); AddonInfo premiumaddon; premiumaddon.name = "premiumaddon.json"; diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index b90a879738d..f7e3f0d61d9 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -50,14 +50,12 @@ class TestPreprocessor : public TestFixture { TestPreprocessor() : TestFixture("TestPreprocessor") {} private: - #define expandMacros(...) expandMacros_(__FILE__, __LINE__, __VA_ARGS__) template - std::string expandMacros_(const char* file, int line, const char (&code)[size], ErrorLogger &errorLogger) const { + std::string expandMacros(const char (&code)[size], ErrorLogger &errorLogger) const { simplecpp::OutputList outputList; std::vector files; simplecpp::TokenList tokens1 = simplecpp::TokenList(code, files, "file.cpp", &outputList); Preprocessor p(tokens1, settingsDefault, errorLogger, Path::identify(tokens1.getFiles()[0], false)); - ASSERT_LOC(p.loadFiles(files), file, line); simplecpp::TokenList tokens2 = p.preprocess("", files, outputList); (void)p.reportOutput(outputList, true); return tokens2.stringify(); @@ -98,7 +96,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 +115,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 +135,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 +395,28 @@ class TestPreprocessor : public TestFixture { simplecpp::OutputList outputList; simplecpp::TokenList tokens(code,files,"test.c",&outputList); Preprocessor preprocessor(tokens, settings, *this, Standards::Language::C); - ASSERT(preprocessor.loadFiles(files)); - ASSERT(!preprocessor.reportOutput(outputList, true)); + 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(); - const std::set configs = preprocessor.getConfigs(); + preprocessor.getConfigs(configDefines, configs); + for (const std::string &currCfg : configs) { + simplecpp::OutputList outputList_cfg; + preprocessor.preprocess(currCfg, files, outputList_cfg); + } + ASSERT(!preprocessor.reportOutput(outputList, true)); + configs.sort(); std::string ret; for (const std::string & config : configs) ret += config + '\n'; @@ -409,7 +428,6 @@ class TestPreprocessor : public TestFixture { std::vector files; simplecpp::TokenList tokens(code,files,"test.c"); Preprocessor preprocessor(tokens, settingsDefault, *this, Standards::Language::C); - ASSERT(preprocessor.loadFiles(files)); preprocessor.removeComments(); return preprocessor.calculateHash(""); } diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 167d5411553..fbaf3c58dc9 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)); + std::list directives; + preprocessor.setLoadCallback([&](const simplecpp::FileData &data) { + Preprocessor::createDirectives(data.tokens, directives); + }); + preprocessor.createDirectives(directives); + preprocessor.preprocess("", files, outputList); (void)preprocessor.reportOutput(outputList, true); - ASSERT(preprocessor.loadFiles(files)); - std::list directives = preprocessor.createDirectives(); TokenList tokenlist{settings, Path::identify(filename, false)}; Tokenizer tokenizer(std::move(tokenlist), *this); @@ -8766,6 +8770,7 @@ class TestTokenizer : public TestFixture { void testDirectiveIncludeTypes() { const char filedata[] = "#define macro some definition\n" + "#define some(a) a function-like macro\n" "#undef macro\n" "#ifdef macro\n" "#elif some (complex) condition\n" @@ -8786,17 +8791,30 @@ class TestTokenizer : public TestFixture { " \n" " \n" " \n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" " \n" " \n" " \n" - " \n" + " \n" " \n" " \n" " \n" " \n" - " \n" + " \n" " \n" " \n" " \n" @@ -8805,50 +8823,50 @@ class TestTokenizer : public TestFixture { " \n" " \n" " \n" - " \n" + " \n" " \n" " \n" " \n" - " \n" + " \n" " \n" " \n" " \n" - " \n" + " \n" " \n" " \n" " \n" " \n" " \n" " \n" - " \n" + " \n" " \n" " \n" " \n" " \n" " \n" " \n" - " \n" + " \n" " \n" " \n" - " \n" + " \n" " \n" " \n" " \n" " \n" " \n" - " \n" + " \n" " \n" " \n" " \n" " \n" " \n" " \n" - " \n" + " \n" " \n" " \n" " \n" " \n" - " \n" + " \n" " \n" " \n" " \n"