From 818db41ac490404cae18f9b8232c981570f44bfe Mon Sep 17 00:00:00 2001 From: glank Date: Fri, 26 Jun 2026 14:53:56 +0200 Subject: [PATCH 01/10] Fix #14113 --- .github/workflows/selfcheck.yml | 2 +- .selfcheck_suppressions | 2 + lib/cppcheck.cpp | 115 +++++++++++++++++++++------- lib/cppcheck.h | 5 +- lib/errorlogger.cpp | 9 +-- lib/errorlogger.h | 18 +++-- lib/preprocessor.cpp | 131 ++++++++++++++------------------ lib/preprocessor.h | 35 ++++++--- test/cli/performance_test.py | 25 ++++++ test/helpers.cpp | 3 +- test/testcppcheck.cpp | 7 +- test/testpreprocessor.cpp | 30 ++++++-- test/testtokenize.cpp | 8 +- 13 files changed, 250 insertions(+), 140 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/.selfcheck_suppressions b/.selfcheck_suppressions index f21492e783a..63d33fd2704 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/lib/cppcheck.cpp b/lib/cppcheck.cpp index 50f7fa36e80..bf7f2461b32 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,48 @@ 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(); + std::set 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); + preprocessor.loadFiles(files); }); } else { - configurations.insert(mSettings.userDefines); + configurations = { mSettings.userDefines }; } if (mSettings.checkConfiguration) { @@ -1089,7 +1119,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 +1288,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 +1929,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 +1983,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..cc00d3251ac 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) @@ -769,36 +762,21 @@ static void getConfigs(const simplecpp::TokenList &tokens, std::set ret.insert(std::move(elseError)); } - -std::set Preprocessor::getConfigs() const +void Preprocessor::getConfigs(std::set &defined, std::set &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::set &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 +849,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 +996,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 +1039,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 +1123,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 +1173,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..cdfd8d5c3bc 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::set &configs) const; + + void getConfigs(const std::string &filename, const simplecpp::TokenList &tokens, std::set &defined, std::set &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::load_callback_type cb) { + mFileCache.set_load_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/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..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..c3c16490219 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -137,8 +137,11 @@ class TestPreprocessor : public TestFixture { preprocessor.simplifyPragmaAsm(); std::map cfgcode; - if (cfgs.empty()) - cfgs = preprocessor.getConfigs(); + if (cfgs.empty()) { + cfgs.insert(""); + 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,24 @@ class TestPreprocessor : public TestFixture { simplecpp::OutputList outputList; simplecpp::TokenList tokens(code,files,"test.c",&outputList); Preprocessor preprocessor(tokens, settings, *this, Standards::Language::C); + std::set 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(); std::string ret; for (const std::string & config : configs) ret += config + '\n'; @@ -409,8 +426,11 @@ 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.setLoadCallback([](simplecpp::FileData &data) { + Preprocessor::removeComments(data.tokens); + }); preprocessor.removeComments(); + ASSERT(preprocessor.loadFiles(files)); return preprocessor.calculateHash(""); } 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); From c9a5bda17e0eb12bb771aa62f90150cbbfba7539 Mon Sep 17 00:00:00 2001 From: glank Date: Tue, 7 Jul 2026 10:19:09 +0200 Subject: [PATCH 02/10] Add test --- lib/preprocessor.h | 2 ++ test/testpreprocessor.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/lib/preprocessor.h b/lib/preprocessor.h index cdfd8d5c3bc..07d96104a67 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -98,6 +98,8 @@ class CPPCHECKLIB RemarkComment { * configurations that exist in a source file. */ class CPPCHECKLIB WARN_UNUSED Preprocessor { + friend class TestPreprocessor; + public: /** character that is inserted in expanded macros */ static char macroChar; diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index c3c16490219..99b0d14ef92 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -371,6 +371,8 @@ class TestPreprocessor : public TestFixture { TEST_CASE(testMissingIncludeMixed); TEST_CASE(testMissingIncludeCheckConfig); + TEST_CASE(testLazyInclude); + TEST_CASE(hasInclude); TEST_CASE(limitsDefines); @@ -3053,6 +3055,36 @@ class TestPreprocessor : public TestFixture { "test.c:11:2: information: Include file: <" + missing4 + "> not found. Please note: Standard library headers do not need to be provided to get proper results. [missingIncludeSystem]\n", errout_str()); } + void testLazyInclude() { + const char *code = "#ifdef CONFIG1\n" + "#include \"header1.h\"\n" + "#include \"missing1.h\"\n" + "#else\n" + "#include \"header2.h\"\n" + "#include \"missing2.h\"\n" + "#endif\n"; + + std::vector files; + simplecpp::TokenList tokens(code, files, "test.c"); + + ScopedFile header1("header1.h", "1"); + ScopedFile header2("header2.h", "2"); + + Settings settings; + Preprocessor preprocessor(tokens, settings, *this, Standards::Language::CPP); + + simplecpp::OutputList outputList; + simplecpp::TokenList tokens2 = preprocessor.preprocess("CONFIG1", files, outputList); + std::string out = tokens2.stringify(); + + simplecpp::FileDataCache &cache = preprocessor.mFileCache; + + ASSERT_EQUALS("\n#line 1 \"header1.h\"\n1", out); + ASSERT_EQUALS(1, outputList.size()); + ASSERT_EQUALS("Header not found: \"missing1.h\"", outputList.begin()->msg); + ASSERT_EQUALS(1, cache.size()); + } + void hasInclude() { const char code[] = "#if __has_include()\n123\n#endif"; Settings settings; From e4c9d54ba897bfb18d0777b3e6bc277e08ff889e Mon Sep 17 00:00:00 2001 From: glank Date: Tue, 7 Jul 2026 11:24:07 +0200 Subject: [PATCH 03/10] Fix selfcheck issues --- test/testpreprocessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 99b0d14ef92..3a00ac0592e 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -3077,7 +3077,7 @@ class TestPreprocessor : public TestFixture { simplecpp::TokenList tokens2 = preprocessor.preprocess("CONFIG1", files, outputList); std::string out = tokens2.stringify(); - simplecpp::FileDataCache &cache = preprocessor.mFileCache; + const simplecpp::FileDataCache &cache = preprocessor.mFileCache; ASSERT_EQUALS("\n#line 1 \"header1.h\"\n1", out); ASSERT_EQUALS(1, outputList.size()); From 8f889562036337bcaa3f5bb8d80924eff209561d Mon Sep 17 00:00:00 2001 From: glank Date: Tue, 7 Jul 2026 15:40:09 +0200 Subject: [PATCH 04/10] Fix weird handling of remark comments --- lib/cppcheck.cpp | 8 ++++---- lib/preprocessor.cpp | 21 +++++++-------------- lib/preprocessor.h | 4 +--- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index bf7f2461b32..1ee11d28996 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -100,9 +100,9 @@ class CppCheck::CppCheckLogger : public ErrorLogger closePlist(); } - std::vector& remarkComments() + void addRemarkComments(std::vector remarkComments) { - return mRemarkComments; + mRemarkComments.insert(mRemarkComments.end(), remarkComments.begin(), remarkComments.end()); } void setLocationMacros(const Token* startTok, const std::vector& files) @@ -1017,7 +1017,7 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str } // Parse comments and then remove them - preprocessor.addRemarkComments(mLogger->remarkComments()); + mLogger->addRemarkComments(preprocessor.getRemarkComments()); preprocessor.inlineSuppressions(mSuppressions.nomsg); preprocessor.removeComments(); @@ -1062,7 +1062,7 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str preprocessor.setLoadCallback([&](simplecpp::FileData &data) { // Do preprocessing on included file - preprocessor.addRemarkComments(data.tokens, mLogger->remarkComments()); + mLogger->addRemarkComments(preprocessor.getRemarkComments(data.tokens)); preprocessor.inlineSuppressions(data.tokens, mSuppressions.nomsg); Preprocessor::removeComments(data.tokens); Preprocessor::createDirectives(data.tokens, directives); diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index cc00d3251ac..3e0b33c7f10 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -352,17 +352,6 @@ void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens, Suppre } } -// cppcheck-suppress unusedFunction - only used in tests -std::vector Preprocessor::getRemarkComments() const -{ - std::vector ret; - addRemarkComments(mTokens, ret); - for (const auto &filedata : mFileCache) { - addRemarkComments(filedata->tokens, ret); - } - return ret; -} - void Preprocessor::createDirectives(std::list &directives) const { createDirectives(mTokens, directives); @@ -1173,13 +1162,15 @@ void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList &tokenList) } } -void Preprocessor::addRemarkComments(std::vector& remarkComments) const +std::vector Preprocessor::getRemarkComments() const { - addRemarkComments(mTokens, remarkComments); + return getRemarkComments(mTokens); } -void Preprocessor::addRemarkComments(const simplecpp::TokenList &tokens, std::vector &remarkComments) const +std::vector Preprocessor::getRemarkComments(const simplecpp::TokenList &tokens) const { + std::vector remarkComments; + for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { if (!tok->comment) continue; @@ -1226,4 +1217,6 @@ void Preprocessor::addRemarkComments(const simplecpp::TokenList &tokens, std::ve // Add the suppressions. remarkComments.emplace_back(relativeFilename, remarkedToken->location.line, remarkText); } + + return remarkComments; } diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 07d96104a67..3a5a8393a3e 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -120,9 +120,7 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { std::vector getRemarkComments() const; - void addRemarkComments(std::vector &remarkComments) const; - - void addRemarkComments(const simplecpp::TokenList &tokens, std::vector &remarkComments) const; + std::vector getRemarkComments(const simplecpp::TokenList &tokens) const; bool loadFiles(std::vector &files); From abdf5ff4e1b793443e0010b3920158e5aebad513 Mon Sep 17 00:00:00 2001 From: glank Date: Tue, 7 Jul 2026 19:59:26 +0200 Subject: [PATCH 05/10] Make addRemarkComments take const ref --- lib/cppcheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 1ee11d28996..bf8d0d29423 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -100,7 +100,7 @@ class CppCheck::CppCheckLogger : public ErrorLogger closePlist(); } - void addRemarkComments(std::vector remarkComments) + void addRemarkComments(const std::vector &remarkComments) { mRemarkComments.insert(mRemarkComments.end(), remarkComments.begin(), remarkComments.end()); } From fd3b00a084ec23c555495f17334e33a23b81555e Mon Sep 17 00:00:00 2001 From: glank Date: Tue, 7 Jul 2026 23:18:24 +0200 Subject: [PATCH 06/10] Simplify library define code --- lib/cppcheck.cpp | 12 ++---------- test/testpreprocessor.cpp | 10 ++-------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index bf8d0d29423..80e17274d6a 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1049,16 +1049,8 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str 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)); - } + for (const auto &define : mSettings.library.defines()) + configDefines.insert(define.substr(0, define.find_first_of("( "))); preprocessor.setLoadCallback([&](simplecpp::FileData &data) { // Do preprocessing on included file diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 3a00ac0592e..2b3aa683a70 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -401,14 +401,8 @@ class TestPreprocessor : public TestFixture { Preprocessor preprocessor(tokens, settings, *this, Standards::Language::C); std::set 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)); - } + for (const auto &define : settings.library.defines()) + configDefines.insert(define.substr(0, define.find_first_of("( "))); preprocessor.setLoadCallback([&](simplecpp::FileData &data) { Preprocessor::removeComments(data.tokens); preprocessor.getConfigs(data.filename, data.tokens, configDefines, configs); From 3580dff65eee1e153817efccad40df9e8e0f4687 Mon Sep 17 00:00:00 2001 From: glank Date: Wed, 8 Jul 2026 09:42:07 +0200 Subject: [PATCH 07/10] Use std::tranfsorm for inserting library defines --- lib/cppcheck.cpp | 7 +++++-- test/testpreprocessor.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 80e17274d6a..d933748815d 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -61,6 +61,7 @@ #include // IWYU pragma: keep #include #include +#include #include #include #include @@ -1049,8 +1050,10 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str std::set configDefines = { "__cplusplus" }; // Insert library defines - for (const auto &define : mSettings.library.defines()) - configDefines.insert(define.substr(0, define.find_first_of("( "))); + std::transform(mSettings.library.defines().begin(), + mSettings.library.defines().end(), + std::inserter(configDefines, configDefines.end()), + [](const auto &define) { return define.substr(0, define.find_first_of("( ")); }); preprocessor.setLoadCallback([&](simplecpp::FileData &data) { // Do preprocessing on included file diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 2b3aa683a70..1caef2a856b 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -33,6 +33,7 @@ #include "helpers.h" #include +#include #include #include #include @@ -401,8 +402,10 @@ class TestPreprocessor : public TestFixture { Preprocessor preprocessor(tokens, settings, *this, Standards::Language::C); std::set configs = { "" }; std::set configDefines = { "__cplusplus" }; - for (const auto &define : settings.library.defines()) - configDefines.insert(define.substr(0, define.find_first_of("( "))); + std::transform(settings.library.defines().begin(), + settings.library.defines().end(), + std::inserter(configDefines, configDefines.end()), + [](const auto &define) { return define.substr(0, define.find_first_of("( ")); }); preprocessor.setLoadCallback([&](simplecpp::FileData &data) { Preprocessor::removeComments(data.tokens); preprocessor.getConfigs(data.filename, data.tokens, configDefines, configs); From 600d8939e28b54169b00c49ecf3ff9a89b945b0e Mon Sep 17 00:00:00 2001 From: glank Date: Wed, 8 Jul 2026 09:47:16 +0200 Subject: [PATCH 08/10] Fix transform lambda --- lib/cppcheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index d933748815d..a3df95e044a 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1053,7 +1053,7 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str std::transform(mSettings.library.defines().begin(), mSettings.library.defines().end(), std::inserter(configDefines, configDefines.end()), - [](const auto &define) { return define.substr(0, define.find_first_of("( ")); }); + [](const std::string &define) { return define.substr(0, define.find_first_of("( ")); }); preprocessor.setLoadCallback([&](simplecpp::FileData &data) { // Do preprocessing on included file From f4b4b717bb3c0a1a04d92b3ba38700cf63a4af72 Mon Sep 17 00:00:00 2001 From: glank Date: Wed, 8 Jul 2026 09:56:08 +0200 Subject: [PATCH 09/10] Fix transform lambda --- test/testpreprocessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 1caef2a856b..e5c5dd233e2 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -405,7 +405,7 @@ class TestPreprocessor : public TestFixture { std::transform(settings.library.defines().begin(), settings.library.defines().end(), std::inserter(configDefines, configDefines.end()), - [](const auto &define) { return define.substr(0, define.find_first_of("( ")); }); + [](const std::string &define) { return define.substr(0, define.find_first_of("( ")); }); preprocessor.setLoadCallback([&](simplecpp::FileData &data) { Preprocessor::removeComments(data.tokens); preprocessor.getConfigs(data.filename, data.tokens, configDefines, configs); From 506847e4636fdf1e7549aa5a7afd2bed20eda3e2 Mon Sep 17 00:00:00 2001 From: glank Date: Wed, 8 Jul 2026 10:26:21 +0200 Subject: [PATCH 10/10] Fix formatting --- lib/cppcheck.cpp | 5 ++++- test/testpreprocessor.cpp | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index a3df95e044a..26d98c0c7b4 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1050,10 +1050,13 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str std::set configDefines = { "__cplusplus" }; // Insert library defines + const auto getDefineName = [](const std::string &defineString) { + return defineString.substr(0, defineString.find_first_of("( ")); + }; std::transform(mSettings.library.defines().begin(), mSettings.library.defines().end(), std::inserter(configDefines, configDefines.end()), - [](const std::string &define) { return define.substr(0, define.find_first_of("( ")); }); + getDefineName); preprocessor.setLoadCallback([&](simplecpp::FileData &data) { // Do preprocessing on included file diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index e5c5dd233e2..5da298a2714 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -32,6 +32,7 @@ #include "fixture.h" #include "helpers.h" +#include #include #include #include @@ -402,10 +403,13 @@ class TestPreprocessor : public TestFixture { Preprocessor preprocessor(tokens, settings, *this, Standards::Language::C); std::set configs = { "" }; std::set configDefines = { "__cplusplus" }; + const auto getDefineName = [](const std::string &defineString) { + return defineString.substr(0, defineString.find_first_of("( ")); + }; std::transform(settings.library.defines().begin(), settings.library.defines().end(), std::inserter(configDefines, configDefines.end()), - [](const std::string &define) { return define.substr(0, define.find_first_of("( ")); }); + getDefineName); preprocessor.setLoadCallback([&](simplecpp::FileData &data) { Preprocessor::removeComments(data.tokens); preprocessor.getConfigs(data.filename, data.tokens, configDefines, configs);