diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index ee8bc857be1..180e089c9bf 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -210,7 +210,7 @@ int CppCheckExecutor::check(int argc, const char* const argv[]) CheckUnusedFunctions::clear(); - CppCheck cppCheck(*this, true, executeCommand); + CppCheck cppCheck(nullptr, *this, true, executeCommand); const Settings& settings = cppCheck.settings(); mSettings = &settings; @@ -936,6 +936,8 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck, int /*argc*/, const cha if (settings.clangTidy) cppcheck.analyseClangTidy(fs); } + + cppcheck.checkExceptionSafety (); } // second loop to parse all markup files which may not work until all diff --git a/cli/threadexecutor.cpp b/cli/threadexecutor.cpp index d447b2c55eb..32dc7e5cdc8 100644 --- a/cli/threadexecutor.cpp +++ b/cli/threadexecutor.cpp @@ -441,7 +441,7 @@ unsigned int __stdcall ThreadExecutor::threadProc(void *args) // guard static members of CppCheck against concurrent access EnterCriticalSection(&threadExecutor->mFileSync); - CppCheck fileChecker(*threadExecutor, false, CppCheckExecutor::executeCommand); + CppCheck fileChecker(nullptr, *threadExecutor, false, CppCheckExecutor::executeCommand); fileChecker.settings() = threadExecutor->mSettings; for (;;) { diff --git a/gui/checkthread.cpp b/gui/checkthread.cpp index adea526db08..dbf7a6e0f6e 100644 --- a/gui/checkthread.cpp +++ b/gui/checkthread.cpp @@ -62,7 +62,7 @@ static bool executeCommand(std::string exe, std::vector args, std:: CheckThread::CheckThread(ThreadResult &result) : mState(Ready), mResult(result), - mCppcheck(result, true, executeCommand), + mCppcheck(nullptr, result, true, executeCommand), mAnalyseWholeProgram(false) { //ctor @@ -129,6 +129,8 @@ void CheckThread::run() fileSettings = mResult.getNextFileSettings(); } + mCppcheck.checkExceptionSafety (); + if (mState == Running) mState = Ready; else diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 0a78fc6537a..cd446ab0b49 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -536,7 +536,7 @@ void MainWindow::analyzeCode(const QString& code, const QString& filename) mUI.mResults, SLOT(debugError(const ErrorItem &))); // Create CppCheck instance - CppCheck cppcheck(result, true, nullptr); + CppCheck cppcheck(nullptr, result, true, nullptr); cppcheck.settings() = getCppcheckSettings(); // Check diff --git a/gui/newsuppressiondialog.cpp b/gui/newsuppressiondialog.cpp index 522599655d5..65dbd33f1bd 100644 --- a/gui/newsuppressiondialog.cpp +++ b/gui/newsuppressiondialog.cpp @@ -21,7 +21,7 @@ NewSuppressionDialog::NewSuppressionDialog(QWidget *parent) : }; QErrorLogger errorLogger; - CppCheck cppcheck(errorLogger, false, nullptr); + CppCheck cppcheck(nullptr, errorLogger, false, nullptr); cppcheck.getErrorMessages(); errorLogger.errorIds.sort(); diff --git a/lib/checkexceptionsafety.cpp b/lib/checkexceptionsafety.cpp index 7faca40cbc5..65f4fc66537 100644 --- a/lib/checkexceptionsafety.cpp +++ b/lib/checkexceptionsafety.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -//--------------------------------------------------------------------------- + //--------------------------------------------------------------------------- #include "checkexceptionsafety.h" #include "settings.h" @@ -28,113 +28,114 @@ //--------------------------------------------------------------------------- -// Register CheckExceptionSafety.. -namespace { - CheckExceptionSafety instance; -} +//// Register CheckExceptionSafety.. +//namespace { +// CheckExceptionSafety instance; +//} +static const Token *functionThrows (const Function *function, const CheckExceptionSafety::FunctionImpls *impl); //--------------------------------------------------------------------------- -void CheckExceptionSafety::destructors() +void CheckExceptionSafety::destructors () { - if (!mSettings->isEnabled(Settings::WARNING)) - return; - - const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); - - // Perform check.. - for (const Scope * scope : symbolDatabase->functionScopes) { - const Function * function = scope->function; - if (!function) - continue; - // only looking for destructors - if (function->type == Function::eDestructor) { - // Inspect this destructor. - for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { - // Skip try blocks - if (Token::simpleMatch(tok, "try {")) { - tok = tok->next()->link(); - } - - // Skip uncaught exceptions - else if (Token::simpleMatch(tok, "if ( ! std :: uncaught_exception ( ) ) {")) { - tok = tok->next()->link(); // end of if ( ... ) - tok = tok->next()->link(); // end of { ... } - } - - // throw found within a destructor - else if (tok->str() == "throw") { - destructorsError(tok, scope->className); - break; - } - } - } - } + //if (!mSettings->isEnabled(Settings::WARNING)) + // return; + + const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase (); + + // Perform check.. + for (const Scope *scope : symbolDatabase->functionScopes) { + const Function *function = scope->function; + if (!function) + continue; + // only looking for destructors + if (function->type == Function::eDestructor) { + //// Inspect this destructor. + //for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + // // Skip try blocks + // if (Token::simpleMatch(tok, "try {")) { + // tok = tok->next()->link(); + // } + + // // Skip uncaught exceptions + // else if (Token::simpleMatch(tok, "if ( ! std :: uncaught_exception ( ) ) {")) { + // tok = tok->next()->link(); // end of if ( ... ) + // tok = tok->next()->link(); // end of { ... } + // } + + // // throw found within a destructor + // else if (tok->str() == "throw") { + // destructorsError(tok, scope->className); + // break; + // } + //} + if (auto token = functionThrows (function, functionImpls)) { + destructorsError (token, scope->className); + } + } + } } - - - -void CheckExceptionSafety::deallocThrow() +void CheckExceptionSafety::deallocThrow () { - if (!mSettings->isEnabled(Settings::WARNING)) - return; - - const bool printInconclusive = mSettings->inconclusive; - const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); - - // Deallocate a global/member pointer and then throw exception - // the pointer will be a dead pointer - for (const Scope * scope : symbolDatabase->functionScopes) { - for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { - // only looking for delete now - if (tok->str() != "delete") - continue; - - // Check if this is something similar with: "delete p;" - tok = tok->next(); - if (Token::simpleMatch(tok, "[ ]")) - tok = tok->tokAt(2); - if (!tok || tok == scope->bodyEnd) - break; - if (!Token::Match(tok, "%var% ;")) - continue; - - // we only look for global variables - const Variable *var = tok->variable(); - if (!var || !(var->isGlobal() || var->isStatic())) - continue; - - const unsigned int varid(tok->varId()); - - // Token where throw occurs - const Token *throwToken = nullptr; - - // is there a throw after the deallocation? - const Token* const end2 = tok->scope()->bodyEnd; - for (const Token *tok2 = tok; tok2 != end2; tok2 = tok2->next()) { - // Throw after delete -> Dead pointer - if (tok2->str() == "throw") { - if (printInconclusive) { // For inconclusive checking, throw directly. - deallocThrowError(tok2, tok->str()); - break; - } - throwToken = tok2; - } - - // Variable is assigned -> Bail out - else if (Token::Match(tok2, "%varid% =", varid)) { - if (throwToken) // For non-inconclusive checking, wait until we find an assignment to it. Otherwise we assume it is safe to leave a dead pointer. - deallocThrowError(throwToken, tok2->str()); - break; - } - // Variable passed to function. Assume it becomes assigned -> Bail out - else if (Token::Match(tok2, "[,(] &| %varid% [,)]", varid)) // TODO: No bailout if passed by value or as const reference - break; - } - } - } + if (!mSettings->isEnabled (Settings::WARNING)) + return; + + const bool printInconclusive = mSettings->inconclusive; + const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase (); + + // Deallocate a global/member pointer and then throw exception + // the pointer will be a dead pointer + for (const Scope *scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart->next (); tok != scope->bodyEnd; tok = tok->next ()) { + // only looking for delete now + if (tok->str () != "delete") + continue; + + // Check if this is something similar with: "delete p;" + tok = tok->next (); + if (Token::simpleMatch (tok, "[ ]")) + tok = tok->tokAt (2); + if (!tok || tok == scope->bodyEnd) + break; + if (!Token::Match (tok, "%var% ;")) + continue; + + // we only look for global variables + const Variable *var = tok->variable (); + if (!var || !(var->isGlobal () || var->isStatic ())) + continue; + + const unsigned int varid (tok->varId ()); + + // Token where throw occurs + const Token *throwToken = nullptr; + + // is there a throw after the deallocation? + const Token *const end2 = tok->scope ()->bodyEnd; + for (const Token *tok2 = tok; tok2 != end2; tok2 = tok2->next ()) { + // Throw after delete -> Dead pointer + if (tok2->str () == "throw") { + if (printInconclusive) { // For inconclusive checking, throw directly. + deallocThrowError (tok2, tok->str ()); + break; + } + throwToken = tok2; + } + + // Variable is assigned -> Bail out + else if (Token::Match (tok2, "%varid% =", varid)) { + if (throwToken) // For non-inconclusive checking, wait until we find an assignment to it. Otherwise we assume it is safe to leave a dead pointer. + deallocThrowError (throwToken, tok2->str ()); + break; + } + // Variable passed to function. Assume it becomes assigned -> Bail out + else if (Token::Match (tok2, "[,(] &| %varid% [,)]", varid)) // TODO: No bailout if passed by value or as const reference + break; + } + } + } } //--------------------------------------------------------------------------- @@ -143,99 +144,124 @@ void CheckExceptionSafety::deallocThrow() // throw err; // <- should be just "throw;" // } //--------------------------------------------------------------------------- -void CheckExceptionSafety::checkRethrowCopy() +void CheckExceptionSafety::checkRethrowCopy () { - if (!mSettings->isEnabled(Settings::STYLE)) - return; - - const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); - - for (const Scope &scope : symbolDatabase->scopeList) { - if (scope.type != Scope::eCatch) - continue; - - const unsigned int varid = scope.bodyStart->tokAt(-2)->varId(); - if (varid) { - for (const Token* tok = scope.bodyStart->next(); tok && tok != scope.bodyEnd; tok = tok->next()) { - if (Token::simpleMatch(tok, "catch (") && tok->next()->link() && tok->next()->link()->next()) { // Don't check inner catch - it is handled in another iteration of outer loop. - tok = tok->next()->link()->next()->link(); - if (!tok) - break; - } else if (Token::Match(tok, "%varid% .", varid)) { - const Token *parent = tok->astParent(); - while (Token::simpleMatch(parent->astParent(), ".")) - parent = parent->astParent(); - if (Token::Match(parent->astParent(), "%assign%|++|--|(") && parent == parent->astParent()->astOperand1()) - break; - } else if (Token::Match(tok, "throw %varid% ;", varid)) - rethrowCopyError(tok, tok->strAt(1)); - } - } - } + if (!mSettings->isEnabled (Settings::STYLE)) + return; + + const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase (); + + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eCatch) + continue; + + const unsigned int varid = scope.bodyStart->tokAt (-2)->varId (); + if (varid) { + for (const Token *tok = scope.bodyStart->next (); tok && tok != scope.bodyEnd; tok = tok->next ()) { + if (Token::simpleMatch (tok, "catch (") && tok->next ()->link () && tok->next ()->link ()->next ()) { // Don't check inner catch - it is handled in another iteration of outer loop. + tok = tok->next ()->link ()->next ()->link (); + if (!tok) + break; + } + else if (Token::Match (tok, "%varid% .", varid)) { + const Token *parent = tok->astParent (); + while (Token::simpleMatch (parent->astParent (), ".")) + parent = parent->astParent (); + if (Token::Match (parent->astParent (), "%assign%|++|--|(") && parent == parent->astParent ()->astOperand1 ()) + break; + } + else if (Token::Match (tok, "throw %varid% ;", varid)) + rethrowCopyError (tok, tok->strAt (1)); + } + } + } } //--------------------------------------------------------------------------- // try {} catch (std::exception err) {} <- Should be "std::exception& err" //--------------------------------------------------------------------------- -void CheckExceptionSafety::checkCatchExceptionByValue() +void CheckExceptionSafety::checkCatchExceptionByValue () { - if (!mSettings->isEnabled(Settings::STYLE)) - return; + if (!mSettings->isEnabled (Settings::STYLE)) + return; - const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase (); - for (const Scope &scope : symbolDatabase->scopeList) { - if (scope.type != Scope::eCatch) - continue; + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eCatch) + continue; - // Find a pass-by-value declaration in the catch(), excluding basic types - // e.g. catch (std::exception err) - const Variable *var = scope.bodyStart->tokAt(-2)->variable(); - if (var && var->isClass() && !var->isPointer() && !var->isReference()) - catchExceptionByValueError(scope.classDef); - } + // Find a pass-by-value declaration in the catch(), excluding basic types + // e.g. catch (std::exception err) + const Variable *var = scope.bodyStart->tokAt (-2)->variable (); + if (var && var->isClass () && !var->isPointer () && !var->isReference ()) + catchExceptionByValueError (scope.classDef); + } } +#include -static const Token * functionThrowsRecursive(const Function * function, std::set & recursive) +static const Token *functionThrowsRecursive (const Function *function, std::set &recursive, const CheckExceptionSafety::FunctionImpls *impls) { - // check for recursion and bail if found - if (!recursive.insert(function).second) - return nullptr; - - if (!function->functionScope) - return nullptr; - - for (const Token *tok = function->functionScope->bodyStart->next(); - tok != function->functionScope->bodyEnd; tok = tok->next()) { - if (tok->str() == "try") { - // just bail for now - break; - } - if (tok->str() == "throw") { - return tok; - } else if (tok->function()) { - const Function * called = tok->function(); - // check if called function has an exception specification - if (called->isThrow() && called->throwArg) { - return tok; - } else if (called->isNoExcept() && called->noexceptArg && - called->noexceptArg->str() != "true") { - return tok; - } else if (functionThrowsRecursive(called, recursive)) { - return tok; - } - } - } - - return nullptr; + // check for recursion and bail if found + if (!recursive.insert (function).second) + return nullptr; + + if (!function->functionScope && impls) { + auto name = function->getQualifiedName (); + auto it = impls->find (name); + + if (it != impls->end ()) { + if (name == it->second.first->function->getQualifiedName ()) + function = it->second.first->function; + } + + if (!function || !function->functionScope) + return nullptr; + } + + for (const Token *tok = function->functionScope->bodyStart->next (); + tok != function->functionScope->bodyEnd; tok = tok->next ()) + { + if (tok->str () == "try") { + // just bail for now + break; + } + if (tok->str () == "new") { + return tok; + } + else if (tok->str () == "throw") { + return tok; + } + else if (tok->function ()) { + const Function *called = tok->function (); + // check if called functionhas an exception specification + if (called->isThrow () && called->throwArg) { + return tok; + } + else if (called->isNoExcept () && called->noexceptArg && + called->noexceptArg->str () != "true") { + return tok; + } + else if (!called->isNoExcept ()) { + if (functionThrowsRecursive (called, recursive, impls)) { + return tok; + } + } + } + else if (tok->str () == "throwException") { + return tok; + } + } + + return nullptr; } -static const Token * functionThrows(const Function * function) +static const Token *functionThrows (const Function *function, const CheckExceptionSafety::FunctionImpls *impls) { - std::set recursive; + std::set recursive; - return functionThrowsRecursive(function, recursive); + return functionThrowsRecursive (function, recursive, impls); } //-------------------------------------------------------------------------- @@ -243,68 +269,69 @@ static const Token * functionThrows(const Function * function) // void func() throw() { throw x; } // void func() __attribute__((nothrow)); void func() { throw x; } //-------------------------------------------------------------------------- -void CheckExceptionSafety::nothrowThrows() +void CheckExceptionSafety::nothrowThrows () { - const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); - - for (const Scope * scope : symbolDatabase->functionScopes) { - const Function* function = scope->function; - if (!function) - continue; - - // check noexcept and noexcept(true) functions - if (function->isNoExcept() && - (!function->noexceptArg || function->noexceptArg->str() == "true")) { - const Token *throws = functionThrows(function); - if (throws) - noexceptThrowError(throws); - } - - // check throw() functions - else if (function->isThrow() && !function->throwArg) { - const Token *throws = functionThrows(function); - if (throws) - noexceptThrowError(throws); - } - - // check __attribute__((nothrow)) or __declspec(nothrow) functions - else if (function->isAttributeNothrow()) { - const Token *throws = functionThrows(function); - if (throws) - noexceptThrowError(throws); - } - } + const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase (); + + for (const Scope *scope : symbolDatabase->functionScopes) { + const Function *function = scope->function; + if (!function) + continue; + + // check noexcept and noexcept(true) functions + if (function->isNoExcept () && + (!function->noexceptArg || function->noexceptArg->str () == "true")) { + const Token *throws = functionThrows (function, functionImpls); + if (throws) + noexceptThrowError (throws); + } + + // check throw() functions + else if (function->isThrow () && !function->throwArg) { + const Token *throws = functionThrows (function, functionImpls); + if (throws) + noexceptThrowError (throws); + } + + // check __attribute__((nothrow)) or __declspec(nothrow) functions + else if (function->isAttributeNothrow ()) { + const Token *throws = functionThrows (function, functionImpls); + if (throws) + noexceptThrowError (throws); + } + } } //-------------------------------------------------------------------------- // void func() { functionWithExceptionSpecification(); } //-------------------------------------------------------------------------- -void CheckExceptionSafety::unhandledExceptionSpecification() +void CheckExceptionSafety::unhandledExceptionSpecification () { - if (!mSettings->isEnabled(Settings::STYLE) || !mSettings->inconclusive) - return; - - const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); - - for (const Scope * scope : symbolDatabase->functionScopes) { - // only check functions without exception epecification - if (scope->function && !scope->function->isThrow() && - scope->className != "main" && scope->className != "wmain" && - scope->className != "_tmain" && scope->className != "WinMain") { - for (const Token *tok = scope->function->functionScope->bodyStart->next(); - tok != scope->function->functionScope->bodyEnd; tok = tok->next()) { - if (tok->str() == "try") { - break; - } else if (tok->function()) { - const Function * called = tok->function(); - // check if called function has an exception specification - if (called->isThrow() && called->throwArg) { - unhandledExceptionSpecificationError(tok, called->tokenDef, scope->function->name()); - break; - } - } - } - } - } + if (!mSettings->isEnabled (Settings::STYLE) || !mSettings->inconclusive) + return; + + const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase (); + + for (const Scope *scope : symbolDatabase->functionScopes) { + // only check functions without exception epecification + if (scope->function && !scope->function->isThrow () && + scope->className != "main" && scope->className != "wmain" && + scope->className != "_tmain" && scope->className != "WinMain") { + for (const Token *tok = scope->function->functionScope->bodyStart->next (); + tok != scope->function->functionScope->bodyEnd; tok = tok->next ()) { + if (tok->str () == "try") { + break; + } + else if (tok->function ()) { + const Function *called = tok->function (); + // check if called function has an exception specification + if (called->isThrow () && called->throwArg) { + unhandledExceptionSpecificationError (tok, called->tokenDef, scope->function->name ()); + break; + } + } + } + } + } } diff --git a/lib/checkexceptionsafety.h b/lib/checkexceptionsafety.h index c6db4b6828a..b511762f33f 100644 --- a/lib/checkexceptionsafety.h +++ b/lib/checkexceptionsafety.h @@ -30,6 +30,7 @@ #include #include +class CppCheck; class Settings; class ErrorLogger; @@ -52,26 +53,33 @@ static const struct CWE CWE703(703U); // Improper Check or Handling of Excepti class CPPCHECKLIB CheckExceptionSafety : public Check { public: + typedef std::map> FunctionImpls; + const FunctionImpls *functionImpls; + /** This constructor is used when registering the CheckClass */ - CheckExceptionSafety() : Check(myName()) { + CheckExceptionSafety() : Check(myName(), nullptr, nullptr, nullptr) { } /** This constructor is used when running checks. */ - CheckExceptionSafety(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) { + CheckExceptionSafety(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger, const FunctionImpls *impls) + : Check(myName(), tokenizer, settings, errorLogger), functionImpls (impls) { } - void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { - if (tokenizer->isC()) + void runChecks (const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger, const FunctionImpls *impls) { + if (tokenizer->isC ()) return; - CheckExceptionSafety checkExceptionSafety(tokenizer, settings, errorLogger); - checkExceptionSafety.destructors(); - checkExceptionSafety.deallocThrow(); - checkExceptionSafety.checkRethrowCopy(); - checkExceptionSafety.checkCatchExceptionByValue(); - checkExceptionSafety.nothrowThrows(); - checkExceptionSafety.unhandledExceptionSpecification(); + CheckExceptionSafety checkExceptionSafety (tokenizer, settings, errorLogger, impls); + checkExceptionSafety.destructors (); + checkExceptionSafety.deallocThrow (); + checkExceptionSafety.checkRethrowCopy (); + checkExceptionSafety.checkCatchExceptionByValue (); + checkExceptionSafety.nothrowThrows (); + checkExceptionSafety.unhandledExceptionSpecification (); + } + + void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { + runChecks (tokenizer, settings, errorLogger, nullptr); } /** Don't throw exceptions in destructors */ @@ -137,7 +145,7 @@ class CPPCHECKLIB CheckExceptionSafety : public Check { /** Generate all possible errors (for --errorlist) */ void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { - CheckExceptionSafety c(nullptr, settings, errorLogger); + CheckExceptionSafety c(nullptr, settings, errorLogger, nullptr); c.destructorsError(nullptr, "Class"); c.deallocThrowError(nullptr, "p"); c.rethrowCopyError(nullptr, "varname"); diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index f948e8b783c..9679e67ce85 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -32,6 +32,8 @@ #include "tokenlist.h" #include "version.h" +#include "checkexceptionsafety.h" + #include "exprengine.h" #define PICOJSON_USE_INT64 @@ -58,1521 +60,1588 @@ static const char ExtraVersion[] = ""; static TimerResults s_timerResults; -// CWE ids used -static const CWE CWE398(398U); // Indicator of Poor Code Quality - namespace { - struct AddonInfo { - std::string name; - std::string scriptFile; - std::string args; - std::string python; - - static std::string getFullPath(const std::string &fileName, const std::string &exename) { - if (Path::fileExists(fileName)) - return fileName; - - const std::string exepath = Path::getPathFromFilename(exename); - if (Path::fileExists(exepath + fileName)) - return exepath + fileName; - if (Path::fileExists(exepath + "addons/" + fileName)) - return exepath + "addons/" + fileName; +struct AddonInfo { + std::string name; + std::string scriptFile; + std::string args; + std::string python; + + static std::string getFullPath (const std::string &fileName, const std::string &exename) { + if (Path::fileExists (fileName)) + return fileName; + + const std::string exepath = Path::getPathFromFilename (exename); + if (Path::fileExists (exepath + fileName)) + return exepath + fileName; + if (Path::fileExists (exepath + "addons/" + fileName)) + return exepath + "addons/" + fileName; #ifdef FILESDIR - if (Path::fileExists(FILESDIR + ("/" + fileName))) - return FILESDIR + ("/" + fileName); - if (Path::fileExists(FILESDIR + ("/addons/" + fileName))) - return FILESDIR + ("/addons/" + fileName); + if (Path::fileExists (FILESDIR + ("/" + fileName))) + return FILESDIR + ("/" + fileName); + if (Path::fileExists (FILESDIR + ("/addons/" + fileName))) + return FILESDIR + ("/addons/" + fileName); #endif - return ""; - } - - std::string parseAddonInfo(const picojson::value &json, const std::string &fileName, const std::string &exename) { - std::string json_error = picojson::get_last_error(); - if (!json_error.empty()) { - return "Loading " + fileName + " failed. " + json_error; - } - if (!json.is()) - return "Loading " + fileName + " failed. Bad json."; - picojson::object obj = json.get(); - if (obj.count("args")) { - if (!obj["args"].is()) - return "Loading " + fileName + " failed. args must be array."; - for (const picojson::value &v : obj["args"].get()) - args += " " + v.get(); - } - - if (obj.count("python")) { - // Python was defined in the config file - if (obj["python"].is()) { - return "Loading " + fileName +" failed. python must not be an array."; - } - python = obj["python"].get(); - } else { - python = ""; - } - - return getAddonInfo(obj["script"].get(), exename); - } - - std::string getAddonInfo(const std::string &fileName, const std::string &exename) { - if (fileName[0] == '{') { - std::istringstream in(fileName); - picojson::value json; - in >> json; - return parseAddonInfo(json, fileName, exename); - } - if (fileName.find(".") == std::string::npos) - return getAddonInfo(fileName + ".py", exename); - - if (endsWith(fileName, ".py", 3)) { - scriptFile = getFullPath(fileName, exename); - if (scriptFile.empty()) - return "Did not find addon " + fileName; - - std::string::size_type pos1 = scriptFile.rfind("/"); - if (pos1 == std::string::npos) - pos1 = 0; - else - pos1++; - std::string::size_type pos2 = scriptFile.rfind("."); - if (pos2 < pos1) - pos2 = std::string::npos; - name = scriptFile.substr(pos1, pos2 - pos1); - - return ""; - } - - if (!endsWith(fileName, ".json", 5)) - return "Failed to open addon " + fileName; - - std::ifstream fin(fileName); - if (!fin.is_open()) - return "Failed to open " + fileName; - picojson::value json; - fin >> json; - return parseAddonInfo(json, fileName, exename); - } - }; + return ""; + } + + std::string parseAddonInfo (const picojson::value &json, const std::string &fileName, const std::string &exename) { + std::string json_error = picojson::get_last_error (); + if (!json_error.empty ()) { + return "Loading " + fileName + " failed. " + json_error; + } + if (!json.is ()) + return "Loading " + fileName + " failed. Bad json."; + picojson::object obj = json.get (); + if (obj.count ("args")) { + if (!obj["args"].is ()) + return "Loading " + fileName + " failed. args must be array."; + for (const picojson::value &v : obj["args"].get ()) + args += " " + v.get (); + } + + if (obj.count ("python")) { + // Python was defined in the config file + if (obj["python"].is ()) { + return "Loading " + fileName + " failed. python must not be an array."; + } + python = obj["python"].get (); + } + else { + python = ""; + } + + return getAddonInfo (obj["script"].get (), exename); + } + + std::string getAddonInfo (const std::string &fileName, const std::string &exename) { + if (fileName[0] == '{') { + std::istringstream in (fileName); + picojson::value json; + in >> json; + return parseAddonInfo (json, fileName, exename); + } + if (fileName.find (".") == std::string::npos) + return getAddonInfo (fileName + ".py", exename); + + if (endsWith (fileName, ".py", 3)) { + scriptFile = getFullPath (fileName, exename); + if (scriptFile.empty ()) + return "Did not find addon " + fileName; + + std::string::size_type pos1 = scriptFile.rfind ("/"); + if (pos1 == std::string::npos) + pos1 = 0; + else + pos1++; + std::string::size_type pos2 = scriptFile.rfind ("."); + if (pos2 < pos1) + pos2 = std::string::npos; + name = scriptFile.substr (pos1, pos2 - pos1); + + return ""; + } + + if (!endsWith (fileName, ".json", 5)) + return "Failed to open addon " + fileName; + + std::ifstream fin (fileName); + if (!fin.is_open ()) + return "Failed to open " + fileName; + picojson::value json; + fin >> json; + return parseAddonInfo (json, fileName, exename); + } +}; } -static std::string cmdFileName(std::string f) +static std::string cmdFileName (std::string f) { - f = Path::toNativeSeparators(f); - if (f.find(" ") != std::string::npos) - return "\"" + f + "\""; - return f; + f = Path::toNativeSeparators (f); + if (f.find (" ") != std::string::npos) + return "\"" + f + "\""; + return f; } -static std::vector split(const std::string &str, const std::string &sep=" ") +static std::vector split (const std::string &str, const std::string &sep = " ") { - std::vector ret; - for (std::string::size_type startPos = 0U; startPos < str.size();) { - std::string::size_type endPos; - if (str[startPos] == '\"') { - endPos = str.find("\"", startPos + 1); - if (endPos < str.size()) - endPos++; - } else { - endPos = str.find(sep, startPos + 1); - } - if (endPos == std::string::npos) { - ret.push_back(str.substr(startPos)); - break; - } - ret.push_back(str.substr(startPos, endPos - startPos)); - startPos = str.find_first_not_of(sep, endPos); - } - return ret; + std::vector ret; + for (std::string::size_type startPos = 0U; startPos < str.size ();) { + std::string::size_type endPos; + if (str[startPos] == '\"') { + endPos = str.find ("\"", startPos + 1); + if (endPos < str.size ()) + endPos++; + } + else { + endPos = str.find (sep, startPos + 1); + } + if (endPos == std::string::npos) { + ret.push_back (str.substr (startPos)); + break; + } + ret.push_back (str.substr (startPos, endPos - startPos)); + startPos = str.find_first_not_of (sep, endPos); + } + return ret; } -static std::string executeAddon(const AddonInfo &addonInfo, - const std::string &defaultPythonExe, - const std::string &dumpFile, - std::function,std::string,std::string*)> executeCommand) +static std::string executeAddon (const AddonInfo &addonInfo, + const std::string &defaultPythonExe, + const std::string &dumpFile, + std::function, std::string, std::string *)> executeCommand) { - const std::string redirect = "2>&1"; + const std::string redirect = "2>&1"; - std::string pythonExe; + std::string pythonExe; - if (!addonInfo.python.empty()) - pythonExe = cmdFileName(addonInfo.python); - else if (!defaultPythonExe.empty()) - pythonExe = cmdFileName(defaultPythonExe); - else { + if (!addonInfo.python.empty ()) + pythonExe = cmdFileName (addonInfo.python); + else if (!defaultPythonExe.empty ()) + pythonExe = cmdFileName (defaultPythonExe); + else { #ifdef _WIN32 - const char *p[] = { "python3.exe", "python.exe" }; + const char *p[] = { "python3.exe", "python.exe" }; #else - const char *p[] = { "python3", "python" }; + const char *p[] = { "python3", "python" }; #endif - for (int i = 0; i < 2; ++i) { - std::string out; - if (executeCommand(p[i], split("--version"), redirect, &out) && out.compare(0, 7, "Python ") == 0 && std::isdigit(out[7])) { - pythonExe = p[i]; - break; - } - } - if (pythonExe.empty()) - throw InternalError(nullptr, "Failed to auto detect python"); - } - - const std::string args = cmdFileName(addonInfo.scriptFile) + " --cli" + addonInfo.args + " " + cmdFileName(dumpFile); - std::string result; - if (!executeCommand(pythonExe, split(args), redirect, &result)) - throw InternalError(nullptr, "Failed to execute addon (command: '" + pythonExe + " " + args + "')"); - - // Validate output.. - std::istringstream istr(result); - std::string line; - while (std::getline(istr, line)) { - if (line.compare(0,9,"Checking ", 0, 9) != 0 && !line.empty() && line[0] != '{') - throw InternalError(nullptr, "Failed to execute '" + pythonExe + " " + args + "'. " + result); - } - - // Valid results - return result; + for (int i = 0; i < 2; ++i) { + std::string out; + if (executeCommand (p[i], split ("--version"), redirect, &out) && out.compare (0, 7, "Python ") == 0 && std::isdigit (out[7])) { + pythonExe = p[i]; + break; + } + } + if (pythonExe.empty ()) + throw InternalError (nullptr, "Failed to auto detect python"); + } + + const std::string args = cmdFileName (addonInfo.scriptFile) + " --cli" + addonInfo.args + " " + cmdFileName (dumpFile); + std::string result; + if (!executeCommand (pythonExe, split (args), redirect, &result)) + throw InternalError (nullptr, "Failed to execute addon (command: '" + pythonExe + " " + args + "')"); + + // Validate output.. + std::istringstream istr (result); + std::string line; + while (std::getline (istr, line)) { + if (line.compare (0, 9, "Checking ", 0, 9) != 0 && !line.empty () && line[0] != '{') + throw InternalError (nullptr, "Failed to execute '" + pythonExe + " " + args + "'. " + result); + } + + // Valid results + return result; } -static std::string getDefinesFlags(const std::string &semicolonSeparatedString) +static std::string getDefinesFlags (const std::string &semicolonSeparatedString) { - std::string flags; - for (const std::string &d: split(semicolonSeparatedString, ";")) - flags += "-D" + d + " "; - return flags; + std::string flags; + for (const std::string &d : split (semicolonSeparatedString, ";")) + flags += "-D" + d + " "; + return flags; } -CppCheck::CppCheck(ErrorLogger &errorLogger, - bool useGlobalSuppressions, - std::function,std::string,std::string*)> executeCommand) - : mErrorLogger(errorLogger) - , mExitCode(0) - , mSuppressInternalErrorFound(false) - , mUseGlobalSuppressions(useGlobalSuppressions) - , mTooManyConfigs(false) - , mSimplify(true) - , mExecuteCommand(executeCommand) +CppCheck::CppCheck (CppCheck *parent, ErrorLogger &errorLogger, + bool useGlobalSuppressions, + std::function, std::string, std::string *)> executeCommand) + : mErrorLogger (errorLogger) + , mExitCode (0) + , mSuppressInternalErrorFound (false) + , mUseGlobalSuppressions (useGlobalSuppressions) + , mTooManyConfigs (false) + , mSimplify (true) + , mExecuteCommand (executeCommand) + , mParent (parent) { } -CppCheck::~CppCheck() +CppCheck::~CppCheck () { - while (!mFileInfo.empty()) { - delete mFileInfo.back(); - mFileInfo.pop_back(); - } - s_timerResults.showResults(mSettings.showtime); + while (!mFileInfo.empty ()) { + delete mFileInfo.back (); + mFileInfo.pop_back (); + } + s_timerResults.showResults (mSettings.showtime); } -const char * CppCheck::version() +const char *CppCheck::version () { - return Version; + return Version; } -const char * CppCheck::extraVersion() +const char *CppCheck::extraVersion () { - return ExtraVersion; + return ExtraVersion; } -static bool reportClangErrors(std::istream &is, std::function reportErr) +static bool reportClangErrors (std::istream &is, std::function reportErr) { - std::string line; - while (std::getline(is, line)) { - if (line.empty() || line[0] == ' ' || line[0] == '`' || line[0] == '-') - continue; - - std::string::size_type pos3 = line.find(": error: "); - if (pos3 == std::string::npos) - pos3 = line.find(": fatal error:"); - if (pos3 == std::string::npos) - continue; - - // file:line:column: error: .... - const std::string::size_type pos2 = line.rfind(":", pos3 - 1); - const std::string::size_type pos1 = line.rfind(":", pos2 - 1); - - if (pos1 >= pos2 || pos2 >= pos3) - continue; - - const std::string filename = line.substr(0, pos1); - const std::string linenr = line.substr(pos1+1, pos2-pos1-1); - const std::string colnr = line.substr(pos2+1, pos3-pos2-1); - const std::string msg = line.substr(line.find(":", pos3+1) + 2); - - std::list locationList; - ErrorMessage::FileLocation loc; - loc.setfile(Path::toNativeSeparators(filename)); - loc.line = std::atoi(linenr.c_str()); - loc.column = std::atoi(colnr.c_str()); - locationList.push_back(loc); - ErrorMessage errmsg(locationList, - loc.getfile(), - Severity::error, - msg, - "syntaxError", - false); - reportErr(errmsg); - - return true; - } - return false; + std::string line; + while (std::getline (is, line)) { + if (line.empty () || line[0] == ' ' || line[0] == '`' || line[0] == '-') + continue; + + std::string::size_type pos3 = line.find (": error: "); + if (pos3 == std::string::npos) + pos3 = line.find (": fatal error:"); + if (pos3 == std::string::npos) + continue; + + // file:line:column: error: .... + const std::string::size_type pos2 = line.rfind (":", pos3 - 1); + const std::string::size_type pos1 = line.rfind (":", pos2 - 1); + + if (pos1 >= pos2 || pos2 >= pos3) + continue; + + const std::string filename = line.substr (0, pos1); + const std::string linenr = line.substr (pos1 + 1, pos2 - pos1 - 1); + const std::string colnr = line.substr (pos2 + 1, pos3 - pos2 - 1); + const std::string msg = line.substr (line.find (":", pos3 + 1) + 2); + + std::list locationList; + ErrorMessage::FileLocation loc; + loc.setfile (Path::toNativeSeparators (filename)); + loc.line = std::atoi (linenr.c_str ()); + loc.column = std::atoi (colnr.c_str ()); + locationList.push_back (loc); + ErrorMessage errmsg (locationList, + loc.getfile (), + Severity::error, + msg, + "syntaxError", + false); + reportErr (errmsg); + + return true; + } + return false; } -unsigned int CppCheck::check(const std::string &path) +unsigned int CppCheck::check (const std::string &path) { - if (mSettings.clang) { - if (!mSettings.quiet) - mErrorLogger.reportOut(std::string("Checking ") + path + "..."); - - const std::string lang = Path::isCPP(path) ? "-x c++" : "-x c"; - const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, ""); - const std::string clangcmd = analyzerInfo + ".clang-cmd"; - const std::string clangStderr = analyzerInfo + ".clang-stderr"; + if (!mParent) { + mChildren.emplace_back ( + std::make_shared (this, mErrorLogger, mUseGlobalSuppressions, mExecuteCommand)); + mChildren.back ()->mSettings = mSettings; + } + + CppCheck &temp = !mParent ? *mChildren.back () : *this; + + if (mSettings.clang) { + if (!mSettings.quiet) + mErrorLogger.reportOut (std::string ("Checking ") + path + "..."); + + const std::string lang = Path::isCPP (path) ? "-x c++" : "-x c"; + const std::string analyzerInfo = mSettings.buildDir.empty () ? std::string () : AnalyzerInformation::getAnalyzerInfoFile (mSettings.buildDir, path, ""); + const std::string clangcmd = analyzerInfo + ".clang-cmd"; + const std::string clangStderr = analyzerInfo + ".clang-stderr"; #ifdef _WIN32 - const std::string exe = "clang.exe"; + const std::string exe = "clang.exe"; #else - const std::string exe = "clang"; + const std::string exe = "clang"; #endif - std::string flags(lang + " "); - if (Path::isCPP(path)) { - if (mSettings.standards.cpp == Standards::CPP14) - flags += "-std=c++14 "; - else if (mSettings.standards.cpp == Standards::CPP17) - flags += "-std=c++17 "; - else if (mSettings.standards.cpp == Standards::CPP20) - flags += "-std=c++20 "; - } - - for (const std::string &i: mSettings.includePaths) - flags += "-I" + i + " "; - - flags += getDefinesFlags(mSettings.userDefines); - - const std::string args2 = "-fsyntax-only -Xclang -ast-dump -fno-color-diagnostics " + flags + path; - const std::string redirect2 = analyzerInfo.empty() ? std::string("2>&1") : ("2> " + clangStderr); - if (!mSettings.buildDir.empty()) { - std::ofstream fout(clangcmd); - fout << exe << " " << args2 << " " << redirect2 << std::endl; - } - - std::string output2; - if (!mExecuteCommand(exe,split(args2),redirect2,&output2) || output2.find("TranslationUnitDecl") == std::string::npos) { - std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "'" << std::endl; - return 0; - } - - // Ensure there are not syntax errors... - if (!mSettings.buildDir.empty()) { - std::ifstream fin(clangStderr); - auto reportError = [this](const ErrorMessage& errorMessage) { - reportErr(errorMessage); - }; - if (reportClangErrors(fin, reportError)) - return 0; - } else { - std::istringstream istr(output2); - auto reportError = [this](const ErrorMessage& errorMessage) { - reportErr(errorMessage); - }; - if (reportClangErrors(istr, reportError)) - return 0; - } - - //std::cout << "Checking Clang ast dump:\n" << result2.second << std::endl; - std::istringstream ast(output2); - Tokenizer tokenizer(&mSettings, this); - tokenizer.list.appendFileIfNew(path); - clangimport::parseClangAstDump(&tokenizer, ast); - ValueFlow::setValues(&tokenizer.list, const_cast(tokenizer.getSymbolDatabase()), this, &mSettings); - if (mSettings.debugnormal) - tokenizer.printDebugOutput(1); - checkNormalTokens(tokenizer); - return mExitCode; - } - - std::ifstream fin(path); - return checkFile(Path::simplifyPath(path), emptyString, fin); + std::string flags (lang + " "); + if (Path::isCPP (path)) { + if (mSettings.standards.cpp == Standards::CPP14) + flags += "-std=c++14 "; + else if (mSettings.standards.cpp == Standards::CPP17) + flags += "-std=c++17 "; + else if (mSettings.standards.cpp == Standards::CPP20) + flags += "-std=c++20 "; + } + + for (const std::string &i : mSettings.includePaths) + flags += "-I" + i + " "; + + flags += getDefinesFlags (mSettings.userDefines); + + const std::string args2 = "-fsyntax-only -Xclang -ast-dump -fno-color-diagnostics " + flags + path; + const std::string redirect2 = analyzerInfo.empty () ? std::string ("2>&1") : ("2> " + clangStderr); + if (!mSettings.buildDir.empty ()) { + std::ofstream fout (clangcmd); + fout << exe << " " << args2 << " " << redirect2 << std::endl; + } + + std::string output2; + if (!mExecuteCommand (exe, split (args2), redirect2, &output2) || output2.find ("TranslationUnitDecl") == std::string::npos) { + std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "'" << std::endl; + return 0; + } + + // Ensure there are not syntax errors... + if (!mSettings.buildDir.empty ()) { + std::ifstream fin (clangStderr); + auto reportError = [this](const ErrorMessage &errorMessage) { + reportErr (errorMessage); + }; + if (reportClangErrors (fin, reportError)) + return 0; + } + else { + std::istringstream istr (output2); + auto reportError = [this](const ErrorMessage &errorMessage) { + reportErr (errorMessage); + }; + if (reportClangErrors (istr, reportError)) + return 0; + } + + //std::cout << "Checking Clang ast dump:\n" << result2.second << std::endl; + std::istringstream ast (output2); + Tokenizer tokenizer (&mSettings, this); + tokenizer.list.appendFileIfNew (path); + clangimport::parseClangAstDump (&tokenizer, ast); + ValueFlow::setValues (&tokenizer.list, const_cast(tokenizer.getSymbolDatabase ()), this, &mSettings); + if (mSettings.debugnormal) + tokenizer.printDebugOutput (1); + checkNormalTokens (tokenizer); + return mExitCode; + } + + std::ifstream fin (path); + return temp.checkFile (Path::simplifyPath (path), emptyString, fin); +} + +unsigned int CppCheck::check (const std::string &path, const std::string &content) +{ + std::istringstream iss (content); + return checkFile (Path::simplifyPath (path), emptyString, iss); } -unsigned int CppCheck::check(const std::string &path, const std::string &content) +void CppCheck::checkExceptionSafety () { - std::istringstream iss(content); - return checkFile(Path::simplifyPath(path), emptyString, iss); + CheckExceptionSafety check; + + if (!Settings::terminated ()) { + if (!mChildren.empty ()) { + for (auto child : mChildren) { + if (child->mTokenizer && child->mTokenizer->getSymbolDatabase ()) { + Timer timerRunChecks ( + check.name () + "::runChecks", child->mSettings.showtime, &s_timerResults); + + check.runChecks (&*child->mTokenizer, &child->mSettings, &*child, &mImplementations); + } + } + } + + else { + Timer timerRunChecks ( + check.name () + "::runChecks", mSettings.showtime, &s_timerResults); + + check.runChecks (&*mTokenizer, &mSettings, this, &mImplementations); + } + } } -unsigned int CppCheck::check(const ImportProject::FileSettings &fs) +unsigned int CppCheck::check (const ImportProject::FileSettings &fs) { - CppCheck temp(mErrorLogger, mUseGlobalSuppressions, mExecuteCommand); - temp.mSettings = mSettings; - if (!temp.mSettings.userDefines.empty()) - temp.mSettings.userDefines += ';'; - if (mSettings.clang) - temp.mSettings.userDefines += fs.defines; - else - temp.mSettings.userDefines += fs.cppcheckDefines(); - temp.mSettings.includePaths = fs.includePaths; - temp.mSettings.userUndefs = fs.undefs; - if (fs.standard == "c++14") - temp.mSettings.standards.cpp = Standards::CPP14; - else if (fs.standard == "c++17") - temp.mSettings.standards.cpp = Standards::CPP17; - else if (fs.standard == "c++20") - temp.mSettings.standards.cpp = Standards::CPP20; - if (fs.platformType != Settings::Unspecified) - temp.mSettings.platform(fs.platformType); - if (mSettings.clang) { - temp.mSettings.includePaths.insert(temp.mSettings.includePaths.end(), fs.systemIncludePaths.cbegin(), fs.systemIncludePaths.cend()); - temp.check(Path::simplifyPath(fs.filename)); - } - std::ifstream fin(fs.filename); - return temp.checkFile(Path::simplifyPath(fs.filename), fs.cfg, fin); + mChildren.emplace_back ( + std::make_shared (this, mErrorLogger, mUseGlobalSuppressions, mExecuteCommand)); + + CppCheck &temp = *mChildren.back (); + temp.mSettings = mSettings; + if (!temp.mSettings.userDefines.empty ()) + temp.mSettings.userDefines += ';'; + if (mSettings.clang) + temp.mSettings.userDefines += fs.defines; + else + temp.mSettings.userDefines += fs.cppcheckDefines (); + temp.mSettings.includePaths = fs.includePaths; + temp.mSettings.userUndefs = fs.undefs; + if (fs.standard == "c++14") + temp.mSettings.standards.cpp = Standards::CPP14; + else if (fs.standard == "c++17") + temp.mSettings.standards.cpp = Standards::CPP17; + else if (fs.standard == "c++20") + temp.mSettings.standards.cpp = Standards::CPP20; + if (fs.platformType != Settings::Unspecified) + temp.mSettings.platform (fs.platformType); + if (mSettings.clang) { + temp.mSettings.includePaths.insert (temp.mSettings.includePaths.end (), fs.systemIncludePaths.cbegin (), fs.systemIncludePaths.cend ()); + temp.check (Path::simplifyPath (fs.filename)); + } + std::ifstream fin (fs.filename); + return temp.checkFile (Path::simplifyPath (fs.filename), fs.cfg, fin); } -unsigned int CppCheck::checkFile(const std::string& filename, const std::string &cfgname, std::istream& fileStream) +unsigned int CppCheck::checkFile (const std::string &filename, const std::string &cfgname, std::istream &fileStream) { - mExitCode = 0; - mSuppressInternalErrorFound = false; - - // only show debug warnings for accepted C/C++ source files - if (!Path::acceptFile(filename)) - mSettings.debugwarnings = false; - - if (Settings::terminated()) - return mExitCode; - - if (!mSettings.quiet) { - std::string fixedpath = Path::simplifyPath(filename); - fixedpath = Path::toNativeSeparators(fixedpath); - mErrorLogger.reportOut(std::string("Checking ") + fixedpath + ' ' + cfgname + std::string("...")); - - if (mSettings.verbose) { - mErrorLogger.reportOut("Defines:" + mSettings.userDefines); - std::string undefs; - for (const std::string& U : mSettings.userUndefs) { - if (!undefs.empty()) - undefs += ';'; - undefs += ' ' + U; - } - mErrorLogger.reportOut("Undefines:" + undefs); - std::string includePaths; - for (const std::string &I : mSettings.includePaths) - includePaths += " -I" + I; - mErrorLogger.reportOut("Includes:" + includePaths); - mErrorLogger.reportOut(std::string("Platform:") + mSettings.platformString()); - } - } - - if (plistFile.is_open()) { - plistFile << ErrorLogger::plistFooter(); - plistFile.close(); - } - - CheckUnusedFunctions checkUnusedFunctions(nullptr, nullptr, nullptr); - - try { - Preprocessor preprocessor(mSettings, this); - std::set configurations; - - simplecpp::OutputList outputList; - std::vector files; - simplecpp::TokenList tokens1(fileStream, files, filename, &outputList); - - // If there is a syntax error, report it and stop - for (const simplecpp::Output &output : outputList) { - bool err; - switch (output.type) { - case simplecpp::Output::ERROR: - case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: - case simplecpp::Output::SYNTAX_ERROR: - case simplecpp::Output::UNHANDLED_CHAR_ERROR: - case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: - err = true; - break; - case simplecpp::Output::WARNING: - case simplecpp::Output::MISSING_HEADER: - case simplecpp::Output::PORTABILITY_BACKSLASH: - err = false; - break; - } - - if (err) { - const ErrorMessage::FileLocation loc1(output.location.file(), output.location.line, output.location.col); - std::list callstack(1, loc1); - - ErrorMessage errmsg(callstack, - "", - Severity::error, - output.msg, - "syntaxError", - false); - reportErr(errmsg); - return mExitCode; - } - } - - if (!preprocessor.loadFiles(tokens1, files)) - return mExitCode; - - if (!mSettings.plistOutput.empty()) { - std::string filename2; - if (filename.find('/') != std::string::npos) - filename2 = filename.substr(filename.rfind('/') + 1); - else - filename2 = filename; - filename2 = mSettings.plistOutput + filename2.substr(0, filename2.find('.')) + ".plist"; - plistFile.open(filename2); - plistFile << ErrorLogger::plistHeader(version(), files); - } - - // write dump file xml prolog - std::ofstream fdump; - std::string dumpFile; - if (mSettings.dump || !mSettings.addons.empty()) { - if (!mSettings.dumpFile.empty()) - dumpFile = mSettings.dumpFile; - else if (!mSettings.dump && !mSettings.buildDir.empty()) - dumpFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, filename, "") + ".dump"; - else - dumpFile = filename + ".dump"; - - fdump.open(dumpFile); - if (fdump.is_open()) { - fdump << "" << std::endl; - fdump << "" << std::endl; - fdump << " \n"; - fdump << " " << std::endl; - for (unsigned int i = 0; i < files.size(); ++i) - fdump << " " << std::endl; - for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) { - fdump << " location.fileIndex << "\" " - << "linenr=\"" << tok->location.line << "\" " - << "column=\"" << tok->location.col << "\" " - << "str=\"" << ErrorLogger::toxml(tok->str()) << "\"" - << "/>" << std::endl; - } - fdump << " " << std::endl; - } - } - - // Parse comments and then remove them - preprocessor.inlineSuppressions(tokens1); - if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) { - mSettings.nomsg.dump(fdump); - } - tokens1.removeComments(); - preprocessor.removeComments(); - - if (!mSettings.buildDir.empty()) { - // Get toolinfo - std::ostringstream toolinfo; - toolinfo << CPPCHECK_VERSION_STRING; - toolinfo << (mSettings.isEnabled(Settings::WARNING) ? 'w' : ' '); - toolinfo << (mSettings.isEnabled(Settings::STYLE) ? 's' : ' '); - toolinfo << (mSettings.isEnabled(Settings::PERFORMANCE) ? 'p' : ' '); - toolinfo << (mSettings.isEnabled(Settings::PORTABILITY) ? 'p' : ' '); - toolinfo << (mSettings.isEnabled(Settings::INFORMATION) ? 'i' : ' '); - toolinfo << mSettings.userDefines; - mSettings.nomsg.dump(toolinfo); - - // Calculate checksum so it can be compared with old checksum / future checksums - const unsigned int checksum = preprocessor.calculateChecksum(tokens1, toolinfo.str()); - std::list errors; - if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, checksum, &errors)) { - while (!errors.empty()) { - reportErr(errors.front()); - errors.pop_front(); - } - return mExitCode; // known results => no need to reanalyze file - } - } - - // Get directives - preprocessor.setDirectives(tokens1); - preprocessor.simplifyPragmaAsm(&tokens1); - - preprocessor.setPlatformInfo(&tokens1); - - // Get configurations.. - if ((mSettings.checkAllConfigurations && mSettings.userDefines.empty()) || mSettings.force) { - Timer t("Preprocessor::getConfigs", mSettings.showtime, &s_timerResults); - configurations = preprocessor.getConfigs(tokens1); - } else { - configurations.insert(mSettings.userDefines); - } - - if (mSettings.checkConfiguration) { - for (const std::string &config : configurations) - (void)preprocessor.getcode(tokens1, config, files, true); - - return 0; - } - - // Run define rules on raw code - for (const Settings::Rule &rule : mSettings.rules) { - if (rule.tokenlist != "define") - continue; - - std::string code; - const std::list &directives = preprocessor.getDirectives(); - for (const Directive &dir : directives) { - if (dir.str.compare(0,8,"#define ") == 0) - code += "#line " + MathLib::toString(dir.linenr) + " \"" + dir.file + "\"\n" + dir.str + '\n'; - } - Tokenizer tokenizer2(&mSettings, this); - std::istringstream istr2(code); - tokenizer2.list.createTokens(istr2); - executeRules("define", tokenizer2); - break; - } - - if (!mSettings.force && configurations.size() > mSettings.maxConfigs) { - if (mSettings.isEnabled(Settings::INFORMATION)) { - tooManyConfigsError(Path::toNativeSeparators(filename),configurations.size()); - } else { - mTooManyConfigs = true; - } - } - - std::set checksums; - unsigned int checkCount = 0; - bool hasValidConfig = false; - std::list configurationError; - for (const std::string &currCfg : configurations) { - // bail out if terminated - if (Settings::terminated()) - break; - - // Check only a few configurations (default 12), after that bail out, unless --force - // was used. - if (!mSettings.force && ++checkCount > mSettings.maxConfigs) - break; - - if (!mSettings.userDefines.empty()) { - mCurrentConfig = mSettings.userDefines; - const std::vector v1(split(mSettings.userDefines, ";")); - for (const std::string &cfg: split(currCfg, ";")) { - if (std::find(v1.begin(), v1.end(), cfg) == v1.end()) { - mCurrentConfig += ";" + cfg; - } - } - } else { - mCurrentConfig = currCfg; - } - - if (mSettings.preprocessOnly) { - Timer t("Preprocessor::getcode", mSettings.showtime, &s_timerResults); - std::string codeWithoutCfg = preprocessor.getcode(tokens1, mCurrentConfig, files, true); - t.stop(); - - if (codeWithoutCfg.compare(0,5,"#file") == 0) - codeWithoutCfg.insert(0U, "//"); - std::string::size_type pos = 0; - while ((pos = codeWithoutCfg.find("\n#file",pos)) != std::string::npos) - codeWithoutCfg.insert(pos+1U, "//"); - pos = 0; - while ((pos = codeWithoutCfg.find("\n#endfile",pos)) != std::string::npos) - codeWithoutCfg.insert(pos+1U, "//"); - pos = 0; - while ((pos = codeWithoutCfg.find(Preprocessor::macroChar,pos)) != std::string::npos) - codeWithoutCfg[pos] = ' '; - reportOut(codeWithoutCfg); - continue; - } - - Tokenizer tokenizer(&mSettings, this); - tokenizer.setPreprocessor(&preprocessor); - if (mSettings.showtime != SHOWTIME_MODES::SHOWTIME_NONE) - tokenizer.setTimerResults(&s_timerResults); - - try { - // Create tokens, skip rest of iteration if failed - { - Timer timer("Tokenizer::createTokens", mSettings.showtime, &s_timerResults); - simplecpp::TokenList tokensP = preprocessor.preprocess(tokens1, mCurrentConfig, files, true); - tokenizer.createTokens(std::move(tokensP)); - } - hasValidConfig = true; - - // If only errors are printed, print filename after the check - if (!mSettings.quiet && (!mCurrentConfig.empty() || checkCount > 1)) { - std::string fixedpath = Path::simplifyPath(filename); - fixedpath = Path::toNativeSeparators(fixedpath); - mErrorLogger.reportOut("Checking " + fixedpath + ": " + mCurrentConfig + "..."); - } - - if (!tokenizer.tokens()) - continue; - - // skip rest of iteration if just checking configuration - if (mSettings.checkConfiguration) - continue; - - // Check raw tokens - checkRawTokens(tokenizer); - - // Simplify tokens into normal form, skip rest of iteration if failed - Timer timer2("Tokenizer::simplifyTokens1", mSettings.showtime, &s_timerResults); - bool result = tokenizer.simplifyTokens1(mCurrentConfig); - timer2.stop(); - if (!result) - continue; - - // dump xml if --dump - if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) { - fdump << "" << std::endl; - fdump << " " << std::endl; - fdump << " " << std::endl; - fdump << " " << std::endl; - fdump << " " << std::endl; - preprocessor.dump(fdump); - tokenizer.dump(fdump); - fdump << "" << std::endl; - } - - // Skip if we already met the same simplified token list - if (mSettings.force || mSettings.maxConfigs > 1) { - const unsigned long long checksum = tokenizer.list.calculateChecksum(); - if (checksums.find(checksum) != checksums.end()) { - if (mSettings.debugwarnings) - purgedConfigurationMessage(filename, mCurrentConfig); - continue; - } - checksums.insert(checksum); - } - - // Check normal tokens - checkNormalTokens(tokenizer); - - // Analyze info.. - if (!mSettings.buildDir.empty()) - checkUnusedFunctions.parseTokens(tokenizer, filename.c_str(), &mSettings); - - // simplify more if required, skip rest of iteration if failed - if (mSimplify && hasRule("simple")) { - std::cout << "Handling of \"simple\" rules is deprecated and will be removed in Cppcheck 2.5." << std::endl; - - // if further simplification fails then skip rest of iteration - Timer timer3("Tokenizer::simplifyTokenList2", mSettings.showtime, &s_timerResults); - result = tokenizer.simplifyTokenList2(); - timer3.stop(); - if (!result) - continue; - - if (!Settings::terminated()) - executeRules("simple", tokenizer); - } - - } catch (const simplecpp::Output &o) { - // #error etc during preprocessing - configurationError.push_back((mCurrentConfig.empty() ? "\'\'" : mCurrentConfig) + " : [" + o.location.file() + ':' + MathLib::toString(o.location.line) + "] " + o.msg); - --checkCount; // don't count invalid configurations - continue; - - } catch (const InternalError &e) { - std::list locationList; - if (e.token) { - ErrorMessage::FileLocation loc(e.token, &tokenizer.list); - locationList.push_back(loc); - } else { - ErrorMessage::FileLocation loc(tokenizer.list.getSourceFilePath(), 0, 0); - ErrorMessage::FileLocation loc2(filename, 0, 0); - locationList.push_back(loc2); - locationList.push_back(loc); - } - ErrorMessage errmsg(locationList, - tokenizer.list.getSourceFilePath(), - Severity::error, - e.errorMessage, - e.id, - false); - - if (errmsg.severity == Severity::error || mSettings.isEnabled(errmsg.severity)) - reportErr(errmsg); - } - } - - if (!hasValidConfig && configurations.size() > 1 && mSettings.isEnabled(Settings::INFORMATION)) { - std::string msg; - msg = "This file is not analyzed. Cppcheck failed to extract a valid configuration. Use -v for more details."; - msg += "\nThis file is not analyzed. Cppcheck failed to extract a valid configuration. The tested configurations have these preprocessor errors:"; - for (const std::string &s : configurationError) - msg += '\n' + s; - - std::list locationList; - ErrorMessage::FileLocation loc; - loc.setfile(Path::toNativeSeparators(filename)); - locationList.push_back(loc); - ErrorMessage errmsg(locationList, - loc.getfile(), - Severity::information, - msg, - "noValidConfiguration", - false); - reportErr(errmsg); - } - - // dumped all configs, close root element now - if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) - fdump << "" << std::endl; - - if (!mSettings.addons.empty()) { - fdump.close(); - - for (const std::string &addon : mSettings.addons) { - struct AddonInfo addonInfo; - const std::string &failedToGetAddonInfo = addonInfo.getAddonInfo(addon, mSettings.exename); - if (!failedToGetAddonInfo.empty()) { - reportOut(failedToGetAddonInfo); - mExitCode = 1; - continue; - } - const std::string results = - executeAddon(addonInfo, mSettings.addonPython, dumpFile, mExecuteCommand); - std::istringstream istr(results); - std::string line; - - while (std::getline(istr, line)) { - if (line.compare(0,1,"{") != 0) - continue; - - picojson::value res; - std::istringstream istr2(line); - istr2 >> res; - if (!res.is()) - continue; - - picojson::object obj = res.get(); - - const std::string fileName = obj["file"].get(); - const int64_t lineNumber = obj["linenr"].get(); - const int64_t column = obj["column"].get(); - - ErrorMessage errmsg; - - errmsg.callStack.emplace_back(ErrorMessage::FileLocation(fileName, lineNumber, column)); - - errmsg.id = obj["addon"].get() + "-" + obj["errorId"].get(); - const std::string text = obj["message"].get(); - errmsg.setmsg(text); - const std::string severity = obj["severity"].get(); - errmsg.severity = Severity::fromString(severity); - if (errmsg.severity == Severity::SeverityType::none) - continue; - errmsg.file0 = fileName; - - reportErr(errmsg); - } - } - std::remove(dumpFile.c_str()); - } - - } catch (const std::runtime_error &e) { - internalError(filename, e.what()); - } catch (const std::bad_alloc &e) { - internalError(filename, e.what()); - } catch (const InternalError &e) { - internalError(filename, e.errorMessage); - mExitCode=1; // e.g. reflect a syntax error - } - - mAnalyzerInformation.setFileInfo("CheckUnusedFunctions", checkUnusedFunctions.analyzerInfo()); - mAnalyzerInformation.close(); - - // In jointSuppressionReport mode, unmatched suppressions are - // collected after all files are processed - if (!mSettings.jointSuppressionReport && (mSettings.isEnabled(Settings::INFORMATION) || mSettings.checkConfiguration)) { - reportUnmatchedSuppressions(mSettings.nomsg.getUnmatchedLocalSuppressions(filename, isUnusedFunctionCheckEnabled())); - } - - mErrorList.clear(); - - return mExitCode; + mExitCode = 0; + mSuppressInternalErrorFound = false; + + // only show debug warnings for accepted C/C++ source files + if (!Path::acceptFile (filename)) + mSettings.debugwarnings = false; + + if (Settings::terminated ()) + return mExitCode; + + if (!mSettings.quiet) { + std::string fixedpath = Path::simplifyPath (filename); + fixedpath = Path::toNativeSeparators (fixedpath); + mErrorLogger.reportOut (std::string ("Checking ") + fixedpath + ' ' + cfgname + std::string ("...")); + + if (mSettings.verbose) { + mErrorLogger.reportOut ("Defines:" + mSettings.userDefines); + std::string undefs; + for (const std::string &U : mSettings.userUndefs) { + if (!undefs.empty ()) + undefs += ';'; + undefs += ' ' + U; + } + mErrorLogger.reportOut ("Undefines:" + undefs); + std::string includePaths; + for (const std::string &I : mSettings.includePaths) + includePaths += " -I" + I; + mErrorLogger.reportOut ("Includes:" + includePaths); + mErrorLogger.reportOut (std::string ("Platform:") + mSettings.platformString ()); + } + } + + if (plistFile.is_open ()) { + plistFile << ErrorLogger::plistFooter (); + plistFile.close (); + } + + CheckUnusedFunctions checkUnusedFunctions (nullptr, nullptr, nullptr); + + try { + Preprocessor preprocessor (mSettings, this); + std::set configurations; + + simplecpp::OutputList outputList; + std::vector files; + simplecpp::TokenList tokens1 (fileStream, files, filename, &outputList); + + // If there is a syntax error, report it and stop + for (const simplecpp::Output &output : outputList) { + bool err; + switch (output.type) { + case simplecpp::Output::ERROR: + case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: + case simplecpp::Output::SYNTAX_ERROR: + case simplecpp::Output::UNHANDLED_CHAR_ERROR: + case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: + err = true; + break; + case simplecpp::Output::WARNING: + case simplecpp::Output::MISSING_HEADER: + case simplecpp::Output::PORTABILITY_BACKSLASH: + err = false; + break; + } + + if (err) { + const ErrorMessage::FileLocation loc1 (output.location.file (), output.location.line, output.location.col); + std::list callstack (1, loc1); + + ErrorMessage errmsg (callstack, + "", + Severity::error, + output.msg, + "syntaxError", + false); + reportErr (errmsg); + return mExitCode; + } + } + + if (!preprocessor.loadFiles (tokens1, files)) + return mExitCode; + + if (!mSettings.plistOutput.empty ()) { + std::string filename2; + if (filename.find ('/') != std::string::npos) + filename2 = filename.substr (filename.rfind ('/') + 1); + else + filename2 = filename; + filename2 = mSettings.plistOutput + filename2.substr (0, filename2.find ('.')) + ".plist"; + plistFile.open (filename2); + plistFile << ErrorLogger::plistHeader (version (), files); + } + + // write dump file xml prolog + std::ofstream fdump; + std::string dumpFile; + if (mSettings.dump || !mSettings.addons.empty ()) { + if (!mSettings.dumpFile.empty ()) + dumpFile = mSettings.dumpFile; + else if (!mSettings.dump && !mSettings.buildDir.empty ()) + dumpFile = AnalyzerInformation::getAnalyzerInfoFile (mSettings.buildDir, filename, "") + ".dump"; + else + dumpFile = filename + ".dump"; + + fdump.open (dumpFile); + if (fdump.is_open ()) { + fdump << "" << std::endl; + fdump << "" << std::endl; + fdump << " \n"; + fdump << " " << std::endl; + for (unsigned int i = 0; i < files.size (); ++i) + fdump << " " << std::endl; + for (const simplecpp::Token *tok = tokens1.cfront (); tok; tok = tok->next) { + fdump << " location.fileIndex << "\" " + << "linenr=\"" << tok->location.line << "\" " + << "column=\"" << tok->location.col << "\" " + << "str=\"" << ErrorLogger::toxml (tok->str ()) << "\"" + << "/>" << std::endl; + } + fdump << " " << std::endl; + } + } + + // Parse comments and then remove them + preprocessor.inlineSuppressions (tokens1); + if ((mSettings.dump || !mSettings.addons.empty ()) && fdump.is_open ()) { + mSettings.nomsg.dump (fdump); + } + tokens1.removeComments (); + preprocessor.removeComments (); + + if (!mSettings.buildDir.empty ()) { + // Get toolinfo + std::ostringstream toolinfo; + toolinfo << CPPCHECK_VERSION_STRING; + toolinfo << (mSettings.isEnabled (Settings::WARNING) ? 'w' : ' '); + toolinfo << (mSettings.isEnabled (Settings::STYLE) ? 's' : ' '); + toolinfo << (mSettings.isEnabled (Settings::PERFORMANCE) ? 'p' : ' '); + toolinfo << (mSettings.isEnabled (Settings::PORTABILITY) ? 'p' : ' '); + toolinfo << (mSettings.isEnabled (Settings::INFORMATION) ? 'i' : ' '); + toolinfo << mSettings.userDefines; + mSettings.nomsg.dump (toolinfo); + + // Calculate checksum so it can be compared with old checksum / future checksums + const unsigned int checksum = preprocessor.calculateChecksum (tokens1, toolinfo.str ()); + std::list errors; + if (!mAnalyzerInformation.analyzeFile (mSettings.buildDir, filename, cfgname, checksum, &errors)) { + while (!errors.empty ()) { + reportErr (errors.front ()); + errors.pop_front (); + } + return mExitCode; // known results => no need to reanalyze file + } + } + + // Get directives + preprocessor.setDirectives (tokens1); + preprocessor.simplifyPragmaAsm (&tokens1); + + preprocessor.setPlatformInfo (&tokens1); + + // Get configurations.. + if ((mSettings.checkAllConfigurations && mSettings.userDefines.empty ()) || mSettings.force) { + Timer t ("Preprocessor::getConfigs", mSettings.showtime, &s_timerResults); + configurations = preprocessor.getConfigs (tokens1); + } + else { + configurations.insert (mSettings.userDefines); + } + + if (mSettings.checkConfiguration) { + for (const std::string &config : configurations) + (void)preprocessor.getcode (tokens1, config, files, true); + + return 0; + } + + // Run define rules on raw code + for (const Settings::Rule &rule : mSettings.rules) { + if (rule.tokenlist != "define") + continue; + + std::string code; + const std::list &directives = preprocessor.getDirectives (); + for (const Directive &dir : directives) { + if (dir.str.compare (0, 8, "#define ") == 0) + code += "#line " + MathLib::toString (dir.linenr) + " \"" + dir.file + "\"\n" + dir.str + '\n'; + } + Tokenizer tokenizer2 (&mSettings, this); + std::istringstream istr2 (code); + tokenizer2.list.createTokens (istr2); + executeRules ("define", tokenizer2); + break; + } + + if (!mSettings.force && configurations.size () > mSettings.maxConfigs) { + if (mSettings.isEnabled (Settings::INFORMATION)) { + tooManyConfigsError (Path::toNativeSeparators (filename), configurations.size ()); + } + else { + mTooManyConfigs = true; + } + } + + std::set checksums; + unsigned int checkCount = 0; + bool hasValidConfig = false; + std::list configurationError; + for (const std::string &currCfg : configurations) { + // bail out if terminated + if (Settings::terminated ()) + break; + + // Check only a few configurations (default 12), after that bail out, unless --force + // was used. + if (!mSettings.force && ++checkCount > mSettings.maxConfigs) + break; + + if (!mSettings.userDefines.empty ()) { + mCurrentConfig = mSettings.userDefines; + const std::vector v1 (split (mSettings.userDefines, ";")); + for (const std::string &cfg : split (currCfg, ";")) { + if (std::find (v1.begin (), v1.end (), cfg) == v1.end ()) { + mCurrentConfig += ";" + cfg; + } + } + } + else { + mCurrentConfig = currCfg; + } + + if (mSettings.preprocessOnly) { + Timer t ("Preprocessor::getcode", mSettings.showtime, &s_timerResults); + std::string codeWithoutCfg = preprocessor.getcode (tokens1, mCurrentConfig, files, true); + t.stop (); + + if (codeWithoutCfg.compare (0, 5, "#file") == 0) + codeWithoutCfg.insert (0U, "//"); + std::string::size_type pos = 0; + while ((pos = codeWithoutCfg.find ("\n#file", pos)) != std::string::npos) + codeWithoutCfg.insert (pos + 1U, "//"); + pos = 0; + while ((pos = codeWithoutCfg.find ("\n#endfile", pos)) != std::string::npos) + codeWithoutCfg.insert (pos + 1U, "//"); + pos = 0; + while ((pos = codeWithoutCfg.find (Preprocessor::macroChar, pos)) != std::string::npos) + codeWithoutCfg[pos] = ' '; + reportOut (codeWithoutCfg); + continue; + } + + mTokenizer = std::unique_ptr (new Tokenizer (&mSettings, this)); + + Tokenizer &tokenizer = *mTokenizer; + tokenizer.setPreprocessor (&preprocessor); + if (mSettings.showtime != SHOWTIME_MODES::SHOWTIME_NONE) + tokenizer.setTimerResults (&s_timerResults); + + try { + // Create tokens, skip rest of iteration if failed + { + Timer timer ("Tokenizer::createTokens", mSettings.showtime, &s_timerResults); + simplecpp::TokenList tokensP = preprocessor.preprocess (tokens1, mCurrentConfig, files, true); + tokenizer.createTokens (std::move (tokensP)); + } + hasValidConfig = true; + + // If only errors are printed, print filename after the check + if (!mSettings.quiet && (!mCurrentConfig.empty () || checkCount > 1)) { + std::string fixedpath = Path::simplifyPath (filename); + fixedpath = Path::toNativeSeparators (fixedpath); + mErrorLogger.reportOut ("Checking " + fixedpath + ": " + mCurrentConfig + "..."); + } + + if (!tokenizer.tokens ()) + continue; + + // skip rest of iteration if just checking configuration + if (mSettings.checkConfiguration) + continue; + + // Check raw tokens + checkRawTokens (tokenizer); + + // Simplify tokens into normal form, skip rest of iteration if failed + Timer timer2 ("Tokenizer::simplifyTokens1", mSettings.showtime, &s_timerResults); + bool result = tokenizer.simplifyTokens1 (mCurrentConfig); + timer2.stop (); + if (!result) + continue; + + // dump xml if --dump + if ((mSettings.dump || !mSettings.addons.empty ()) && fdump.is_open ()) { + fdump << "" << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; + preprocessor.dump (fdump); + tokenizer.dump (fdump); + fdump << "" << std::endl; + } + + // Skip if we already met the same simplified token list + if (mSettings.force || mSettings.maxConfigs > 1) { + const unsigned long long checksum = tokenizer.list.calculateChecksum (); + if (checksums.find (checksum) != checksums.end ()) { + if (mSettings.debugwarnings) + purgedConfigurationMessage (filename, mCurrentConfig); + continue; + } + checksums.insert (checksum); + } + + registerImplementations (); + + // Check normal tokens + checkNormalTokens (tokenizer); + + // Analyze info.. + if (!mSettings.buildDir.empty ()) + checkUnusedFunctions.parseTokens (tokenizer, filename.c_str (), &mSettings); + + // simplify more if required, skip rest of iteration if failed + if (mSimplify && hasRule ("simple")) { + std::cout << "Handling of \"simple\" rules is deprecated and will be removed in Cppcheck 2.5." << std::endl; + + // if further simplification fails then skip rest of iteration + Timer timer3 ("Tokenizer::simplifyTokenList2", mSettings.showtime, &s_timerResults); + result = tokenizer.simplifyTokenList2 (); + timer3.stop (); + if (!result) + continue; + + if (!Settings::terminated ()) + executeRules ("simple", tokenizer); + } + + } + catch (const simplecpp::Output &o) { + // #error etc during preprocessing + configurationError.push_back ((mCurrentConfig.empty () ? "\'\'" : mCurrentConfig) + " : [" + o.location.file () + ':' + MathLib::toString (o.location.line) + "] " + o.msg); + --checkCount; // don't count invalid configurations + continue; + + } + catch (const InternalError &e) { + std::list locationList; + if (e.token) { + ErrorMessage::FileLocation loc (e.token, &tokenizer.list); + locationList.push_back (loc); + } + else { + ErrorMessage::FileLocation loc (tokenizer.list.getSourceFilePath (), 0, 0); + ErrorMessage::FileLocation loc2 (filename, 0, 0); + locationList.push_back (loc2); + locationList.push_back (loc); + } + ErrorMessage errmsg (locationList, + tokenizer.list.getSourceFilePath (), + Severity::error, + e.errorMessage, + e.id, + false); + + if (errmsg.severity == Severity::error || mSettings.isEnabled (errmsg.severity)) + reportErr (errmsg); + } + } + + if (!hasValidConfig && configurations.size () > 1 && mSettings.isEnabled (Settings::INFORMATION)) { + std::string msg; + msg = "This file is not analyzed. Cppcheck failed to extract a valid configuration. Use -v for more details."; + msg += "\nThis file is not analyzed. Cppcheck failed to extract a valid configuration. The tested configurations have these preprocessor errors:"; + for (const std::string &s : configurationError) + msg += '\n' + s; + + std::list locationList; + ErrorMessage::FileLocation loc; + loc.setfile (Path::toNativeSeparators (filename)); + locationList.push_back (loc); + ErrorMessage errmsg (locationList, + loc.getfile (), + Severity::information, + msg, + "noValidConfiguration", + false); + reportErr (errmsg); + } + + // dumped all configs, close root element now + if ((mSettings.dump || !mSettings.addons.empty ()) && fdump.is_open ()) + fdump << "" << std::endl; + + if (!mSettings.addons.empty ()) { + fdump.close (); + + for (const std::string &addon : mSettings.addons) { + struct AddonInfo addonInfo; + const std::string &failedToGetAddonInfo = addonInfo.getAddonInfo (addon, mSettings.exename); + if (!failedToGetAddonInfo.empty ()) { + reportOut (failedToGetAddonInfo); + mExitCode = 1; + continue; + } + const std::string results = + executeAddon (addonInfo, mSettings.addonPython, dumpFile, mExecuteCommand); + std::istringstream istr (results); + std::string line; + + while (std::getline (istr, line)) { + if (line.compare (0, 1, "{") != 0) + continue; + + picojson::value res; + std::istringstream istr2 (line); + istr2 >> res; + if (!res.is ()) + continue; + + picojson::object obj = res.get (); + + const std::string fileName = obj["file"].get (); + const int64_t lineNumber = obj["linenr"].get (); + const int64_t column = obj["column"].get (); + + ErrorMessage errmsg; + + errmsg.callStack.emplace_back (ErrorMessage::FileLocation (fileName, lineNumber, column)); + + errmsg.id = obj["addon"].get () + "-" + obj["errorId"].get (); + const std::string text = obj["message"].get (); + errmsg.setmsg (text); + const std::string severity = obj["severity"].get (); + errmsg.severity = Severity::fromString (severity); + if (errmsg.severity == Severity::SeverityType::none) + continue; + errmsg.file0 = fileName; + + reportErr (errmsg); + } + } + std::remove (dumpFile.c_str ()); + } + + } + catch (const std::runtime_error &e) { + internalError (filename, e.what ()); + } + catch (const std::bad_alloc &e) { + internalError (filename, e.what ()); + } + catch (const InternalError &e) { + internalError (filename, e.errorMessage); + mExitCode = 1; // e.g. reflect a syntax error + } + + mAnalyzerInformation.setFileInfo ("CheckUnusedFunctions", checkUnusedFunctions.analyzerInfo ()); + mAnalyzerInformation.close (); + + // In jointSuppressionReport mode, unmatched suppressions are + // collected after all files are processed + if (!mSettings.jointSuppressionReport && (mSettings.isEnabled (Settings::INFORMATION) || mSettings.checkConfiguration)) { + reportUnmatchedSuppressions (mSettings.nomsg.getUnmatchedLocalSuppressions (filename, isUnusedFunctionCheckEnabled ())); + } + + mErrorList.clear (); + + return mExitCode; } -void CppCheck::internalError(const std::string &filename, const std::string &msg) +void CppCheck::internalError (const std::string &filename, const std::string &msg) { - const std::string fixedpath = Path::toNativeSeparators(filename); - const std::string fullmsg("Bailing out from checking " + fixedpath + " since there was an internal error: " + msg); - - if (mSettings.isEnabled(Settings::INFORMATION)) { - const ErrorMessage::FileLocation loc1(filename, 0, 0); - std::list callstack(1, loc1); - - ErrorMessage errmsg(callstack, - emptyString, - Severity::information, - fullmsg, - "internalError", - false); - - mErrorLogger.reportErr(errmsg); - } else { - // Report on stdout - mErrorLogger.reportOut(fullmsg); - } + const std::string fixedpath = Path::toNativeSeparators (filename); + const std::string fullmsg ("Bailing out from checking " + fixedpath + " since there was an internal error: " + msg); + + if (mSettings.isEnabled (Settings::INFORMATION)) { + const ErrorMessage::FileLocation loc1 (filename, 0, 0); + std::list callstack (1, loc1); + + ErrorMessage errmsg (callstack, + emptyString, + Severity::information, + fullmsg, + "internalError", + false); + + mErrorLogger.reportErr (errmsg); + } + else { + // Report on stdout + mErrorLogger.reportOut (fullmsg); + } } //--------------------------------------------------------------------------- // CppCheck - A function that checks a raw token list //--------------------------------------------------------------------------- -void CppCheck::checkRawTokens(const Tokenizer &tokenizer) +void CppCheck::checkRawTokens (const Tokenizer &tokenizer) { - // Execute rules for "raw" code - executeRules("raw", tokenizer); + // Execute rules for "raw" code + executeRules ("raw", tokenizer); } //--------------------------------------------------------------------------- // CppCheck - A function that checks a normal token list //--------------------------------------------------------------------------- -void CppCheck::checkNormalTokens(const Tokenizer &tokenizer) +void CppCheck::checkNormalTokens (const Tokenizer &tokenizer) { - if (mSettings.bugHunting) - ExprEngine::runChecks(this, &tokenizer, &mSettings); - else { - // call all "runChecks" in all registered Check classes - for (Check *check : Check::instances()) { - if (Settings::terminated()) - return; - - if (Tokenizer::isMaxTime()) - return; - - Timer timerRunChecks(check->name() + "::runChecks", mSettings.showtime, &s_timerResults); - check->runChecks(&tokenizer, &mSettings, this); - } - - if (mSettings.clang) - // TODO: Use CTU for Clang analysis - return; - - // Analyse the tokens.. - - CTU::FileInfo *fi1 = CTU::getFileInfo(&tokenizer); - if (fi1) { - mFileInfo.push_back(fi1); - mAnalyzerInformation.setFileInfo("ctu", fi1->toString()); - } - - for (const Check *check : Check::instances()) { - Check::FileInfo *fi = check->getFileInfo(&tokenizer, &mSettings); - if (fi != nullptr) { - mFileInfo.push_back(fi); - mAnalyzerInformation.setFileInfo(check->name(), fi->toString()); - } - } - - executeRules("normal", tokenizer); - } + if (mSettings.bugHunting) + ExprEngine::runChecks (this, &tokenizer, &mSettings); + else { + // call all "runChecks" in all registered Check classes + for (Check *check : Check::instances ()) { + if (Settings::terminated ()) + return; + + if (Tokenizer::isMaxTime ()) + return; + + Timer timerRunChecks (check->name () + "::runChecks", mSettings.showtime, &s_timerResults); + check->runChecks (&tokenizer, &mSettings, this); + } + + if (mSettings.clang) + // TODO: Use CTU for Clang analysis + return; + + // Analyse the tokens.. + + CTU::FileInfo *fi1 = CTU::getFileInfo (&tokenizer); + if (fi1) { + mFileInfo.push_back (fi1); + mAnalyzerInformation.setFileInfo ("ctu", fi1->toString ()); + } + + for (const Check *check : Check::instances ()) { + Check::FileInfo *fi = check->getFileInfo (&tokenizer, &mSettings); + if (fi != nullptr) { + mFileInfo.push_back (fi); + mAnalyzerInformation.setFileInfo (check->name (), fi->toString ()); + } + } + + executeRules ("normal", tokenizer); + } } //--------------------------------------------------------------------------- -bool CppCheck::hasRule(const std::string &tokenlist) const +bool CppCheck::hasRule (const std::string &tokenlist) const { #ifdef HAVE_RULES - for (const Settings::Rule &rule : mSettings.rules) { - if (rule.tokenlist == tokenlist) - return true; - } + for (const Settings::Rule &rule : mSettings.rules) { + if (rule.tokenlist == tokenlist) + return true; + } #else - (void)tokenlist; + (void)tokenlist; #endif - return false; + return false; } #ifdef HAVE_RULES -static const char * pcreErrorCodeToString(const int pcreExecRet) +static const char *pcreErrorCodeToString (const int pcreExecRet) { - switch (pcreExecRet) { - case PCRE_ERROR_NULL: - return "Either code or subject was passed as NULL, or ovector was NULL " - "and ovecsize was not zero (PCRE_ERROR_NULL)"; - case PCRE_ERROR_BADOPTION: - return "An unrecognized bit was set in the options argument (PCRE_ERROR_BADOPTION)"; - case PCRE_ERROR_BADMAGIC: - return "PCRE stores a 4-byte \"magic number\" at the start of the compiled code, " - "to catch the case when it is passed a junk pointer and to detect when a " - "pattern that was compiled in an environment of one endianness is run in " - "an environment with the other endianness. This is the error that PCRE " - "gives when the magic number is not present (PCRE_ERROR_BADMAGIC)"; - case PCRE_ERROR_UNKNOWN_NODE: - return "While running the pattern match, an unknown item was encountered in the " - "compiled pattern. This error could be caused by a bug in PCRE or by " - "overwriting of the compiled pattern (PCRE_ERROR_UNKNOWN_NODE)"; - case PCRE_ERROR_NOMEMORY: - return "If a pattern contains back references, but the ovector that is passed " - "to pcre_exec() is not big enough to remember the referenced substrings, " - "PCRE gets a block of memory at the start of matching to use for this purpose. " - "If the call via pcre_malloc() fails, this error is given. The memory is " - "automatically freed at the end of matching. This error is also given if " - "pcre_stack_malloc() fails in pcre_exec(). " - "This can happen only when PCRE has been compiled with " - "--disable-stack-for-recursion (PCRE_ERROR_NOMEMORY)"; - case PCRE_ERROR_NOSUBSTRING: - return "This error is used by the pcre_copy_substring(), pcre_get_substring(), " - "and pcre_get_substring_list() functions (see below). " - "It is never returned by pcre_exec() (PCRE_ERROR_NOSUBSTRING)"; - case PCRE_ERROR_MATCHLIMIT: - return "The backtracking limit, as specified by the match_limit field in a pcre_extra " - "structure (or defaulted) was reached. " - "See the description above (PCRE_ERROR_MATCHLIMIT)"; - case PCRE_ERROR_CALLOUT: - return "This error is never generated by pcre_exec() itself. " - "It is provided for use by callout functions that want to yield a distinctive " - "error code. See the pcrecallout documentation for details (PCRE_ERROR_CALLOUT)"; - case PCRE_ERROR_BADUTF8: - return "A string that contains an invalid UTF-8 byte sequence was passed as a subject, " - "and the PCRE_NO_UTF8_CHECK option was not set. If the size of the output vector " - "(ovecsize) is at least 2, the byte offset to the start of the the invalid UTF-8 " - "character is placed in the first element, and a reason code is placed in the " - "second element. The reason codes are listed in the following section. For " - "backward compatibility, if PCRE_PARTIAL_HARD is set and the problem is a truncated " - "UTF-8 character at the end of the subject (reason codes 1 to 5), " - "PCRE_ERROR_SHORTUTF8 is returned instead of PCRE_ERROR_BADUTF8"; - case PCRE_ERROR_BADUTF8_OFFSET: - return "The UTF-8 byte sequence that was passed as a subject was checked and found to " - "be valid (the PCRE_NO_UTF8_CHECK option was not set), but the value of " - "startoffset did not point to the beginning of a UTF-8 character or the end of " - "the subject (PCRE_ERROR_BADUTF8_OFFSET)"; - case PCRE_ERROR_PARTIAL: - return "The subject string did not match, but it did match partially. See the " - "pcrepartial documentation for details of partial matching (PCRE_ERROR_PARTIAL)"; - case PCRE_ERROR_BADPARTIAL: - return "This code is no longer in use. It was formerly returned when the PCRE_PARTIAL " - "option was used with a compiled pattern containing items that were not supported " - "for partial matching. From release 8.00 onwards, there are no restrictions on " - "partial matching (PCRE_ERROR_BADPARTIAL)"; - case PCRE_ERROR_INTERNAL: - return "An unexpected internal error has occurred. This error could be caused by a bug " - "in PCRE or by overwriting of the compiled pattern (PCRE_ERROR_INTERNAL)"; - case PCRE_ERROR_BADCOUNT: - return"This error is given if the value of the ovecsize argument is negative " - "(PCRE_ERROR_BADCOUNT)"; - case PCRE_ERROR_RECURSIONLIMIT : - return "The internal recursion limit, as specified by the match_limit_recursion " - "field in a pcre_extra structure (or defaulted) was reached. " - "See the description above (PCRE_ERROR_RECURSIONLIMIT)"; - case PCRE_ERROR_DFA_UITEM: - return "PCRE_ERROR_DFA_UITEM"; - case PCRE_ERROR_DFA_UCOND: - return "PCRE_ERROR_DFA_UCOND"; - case PCRE_ERROR_DFA_WSSIZE: - return "PCRE_ERROR_DFA_WSSIZE"; - case PCRE_ERROR_DFA_RECURSE: - return "PCRE_ERROR_DFA_RECURSE"; - case PCRE_ERROR_NULLWSLIMIT: - return "PCRE_ERROR_NULLWSLIMIT"; - case PCRE_ERROR_BADNEWLINE: - return "An invalid combination of PCRE_NEWLINE_xxx options was " - "given (PCRE_ERROR_BADNEWLINE)"; - case PCRE_ERROR_BADOFFSET: - return "The value of startoffset was negative or greater than the length " - "of the subject, that is, the value in length (PCRE_ERROR_BADOFFSET)"; - case PCRE_ERROR_SHORTUTF8: - return "This error is returned instead of PCRE_ERROR_BADUTF8 when the subject " - "string ends with a truncated UTF-8 character and the PCRE_PARTIAL_HARD option is set. " - "Information about the failure is returned as for PCRE_ERROR_BADUTF8. " - "It is in fact sufficient to detect this case, but this special error code for " - "PCRE_PARTIAL_HARD precedes the implementation of returned information; " - "it is retained for backwards compatibility (PCRE_ERROR_SHORTUTF8)"; - case PCRE_ERROR_RECURSELOOP: - return "This error is returned when pcre_exec() detects a recursion loop " - "within the pattern. Specifically, it means that either the whole pattern " - "or a subpattern has been called recursively for the second time at the same " - "position in the subject string. Some simple patterns that might do this " - "are detected and faulted at compile time, but more complicated cases, " - "in particular mutual recursions between two different subpatterns, " - "cannot be detected until run time (PCRE_ERROR_RECURSELOOP)"; - case PCRE_ERROR_JIT_STACKLIMIT: - return "This error is returned when a pattern that was successfully studied " - "using a JIT compile option is being matched, but the memory available " - "for the just-in-time processing stack is not large enough. See the pcrejit " - "documentation for more details (PCRE_ERROR_JIT_STACKLIMIT)"; - case PCRE_ERROR_BADMODE: - return "This error is given if a pattern that was compiled by the 8-bit library " - "is passed to a 16-bit or 32-bit library function, or vice versa (PCRE_ERROR_BADMODE)"; - case PCRE_ERROR_BADENDIANNESS: - return "This error is given if a pattern that was compiled and saved is reloaded on a " - "host with different endianness. The utility function pcre_pattern_to_host_byte_order() " - "can be used to convert such a pattern so that it runs on the new host (PCRE_ERROR_BADENDIANNESS)"; - case PCRE_ERROR_DFA_BADRESTART: - return "PCRE_ERROR_DFA_BADRESTART"; + switch (pcreExecRet) { + case PCRE_ERROR_NULL: + return "Either code or subject was passed as NULL, or ovector was NULL " + "and ovecsize was not zero (PCRE_ERROR_NULL)"; + case PCRE_ERROR_BADOPTION: + return "An unrecognized bit was set in the options argument (PCRE_ERROR_BADOPTION)"; + case PCRE_ERROR_BADMAGIC: + return "PCRE stores a 4-byte \"magic number\" at the start of the compiled code, " + "to catch the case when it is passed a junk pointer and to detect when a " + "pattern that was compiled in an environment of one endianness is run in " + "an environment with the other endianness. This is the error that PCRE " + "gives when the magic number is not present (PCRE_ERROR_BADMAGIC)"; + case PCRE_ERROR_UNKNOWN_NODE: + return "While running the pattern match, an unknown item was encountered in the " + "compiled pattern. This error could be caused by a bug in PCRE or by " + "overwriting of the compiled pattern (PCRE_ERROR_UNKNOWN_NODE)"; + case PCRE_ERROR_NOMEMORY: + return "If a pattern contains back references, but the ovector that is passed " + "to pcre_exec() is not big enough to remember the referenced substrings, " + "PCRE gets a block of memory at the start of matching to use for this purpose. " + "If the call via pcre_malloc() fails, this error is given. The memory is " + "automatically freed at the end of matching. This error is also given if " + "pcre_stack_malloc() fails in pcre_exec(). " + "This can happen only when PCRE has been compiled with " + "--disable-stack-for-recursion (PCRE_ERROR_NOMEMORY)"; + case PCRE_ERROR_NOSUBSTRING: + return "This error is used by the pcre_copy_substring(), pcre_get_substring(), " + "and pcre_get_substring_list() functions (see below). " + "It is never returned by pcre_exec() (PCRE_ERROR_NOSUBSTRING)"; + case PCRE_ERROR_MATCHLIMIT: + return "The backtracking limit, as specified by the match_limit field in a pcre_extra " + "structure (or defaulted) was reached. " + "See the description above (PCRE_ERROR_MATCHLIMIT)"; + case PCRE_ERROR_CALLOUT: + return "This error is never generated by pcre_exec() itself. " + "It is provided for use by callout functions that want to yield a distinctive " + "error code. See the pcrecallout documentation for details (PCRE_ERROR_CALLOUT)"; + case PCRE_ERROR_BADUTF8: + return "A string that contains an invalid UTF-8 byte sequence was passed as a subject, " + "and the PCRE_NO_UTF8_CHECK option was not set. If the size of the output vector " + "(ovecsize) is at least 2, the byte offset to the start of the the invalid UTF-8 " + "character is placed in the first element, and a reason code is placed in the " + "second element. The reason codes are listed in the following section. For " + "backward compatibility, if PCRE_PARTIAL_HARD is set and the problem is a truncated " + "UTF-8 character at the end of the subject (reason codes 1 to 5), " + "PCRE_ERROR_SHORTUTF8 is returned instead of PCRE_ERROR_BADUTF8"; + case PCRE_ERROR_BADUTF8_OFFSET: + return "The UTF-8 byte sequence that was passed as a subject was checked and found to " + "be valid (the PCRE_NO_UTF8_CHECK option was not set), but the value of " + "startoffset did not point to the beginning of a UTF-8 character or the end of " + "the subject (PCRE_ERROR_BADUTF8_OFFSET)"; + case PCRE_ERROR_PARTIAL: + return "The subject string did not match, but it did match partially. See the " + "pcrepartial documentation for details of partial matching (PCRE_ERROR_PARTIAL)"; + case PCRE_ERROR_BADPARTIAL: + return "This code is no longer in use. It was formerly returned when the PCRE_PARTIAL " + "option was used with a compiled pattern containing items that were not supported " + "for partial matching. From release 8.00 onwards, there are no restrictions on " + "partial matching (PCRE_ERROR_BADPARTIAL)"; + case PCRE_ERROR_INTERNAL: + return "An unexpected internal error has occurred. This error could be caused by a bug " + "in PCRE or by overwriting of the compiled pattern (PCRE_ERROR_INTERNAL)"; + case PCRE_ERROR_BADCOUNT: + return"This error is given if the value of the ovecsize argument is negative " + "(PCRE_ERROR_BADCOUNT)"; + case PCRE_ERROR_RECURSIONLIMIT: + return "The internal recursion limit, as specified by the match_limit_recursion " + "field in a pcre_extra structure (or defaulted) was reached. " + "See the description above (PCRE_ERROR_RECURSIONLIMIT)"; + case PCRE_ERROR_DFA_UITEM: + return "PCRE_ERROR_DFA_UITEM"; + case PCRE_ERROR_DFA_UCOND: + return "PCRE_ERROR_DFA_UCOND"; + case PCRE_ERROR_DFA_WSSIZE: + return "PCRE_ERROR_DFA_WSSIZE"; + case PCRE_ERROR_DFA_RECURSE: + return "PCRE_ERROR_DFA_RECURSE"; + case PCRE_ERROR_NULLWSLIMIT: + return "PCRE_ERROR_NULLWSLIMIT"; + case PCRE_ERROR_BADNEWLINE: + return "An invalid combination of PCRE_NEWLINE_xxx options was " + "given (PCRE_ERROR_BADNEWLINE)"; + case PCRE_ERROR_BADOFFSET: + return "The value of startoffset was negative or greater than the length " + "of the subject, that is, the value in length (PCRE_ERROR_BADOFFSET)"; + case PCRE_ERROR_SHORTUTF8: + return "This error is returned instead of PCRE_ERROR_BADUTF8 when the subject " + "string ends with a truncated UTF-8 character and the PCRE_PARTIAL_HARD option is set. " + "Information about the failure is returned as for PCRE_ERROR_BADUTF8. " + "It is in fact sufficient to detect this case, but this special error code for " + "PCRE_PARTIAL_HARD precedes the implementation of returned information; " + "it is retained for backwards compatibility (PCRE_ERROR_SHORTUTF8)"; + case PCRE_ERROR_RECURSELOOP: + return "This error is returned when pcre_exec() detects a recursion loop " + "within the pattern. Specifically, it means that either the whole pattern " + "or a subpattern has been called recursively for the second time at the same " + "position in the subject string. Some simple patterns that might do this " + "are detected and faulted at compile time, but more complicated cases, " + "in particular mutual recursions between two different subpatterns, " + "cannot be detected until run time (PCRE_ERROR_RECURSELOOP)"; + case PCRE_ERROR_JIT_STACKLIMIT: + return "This error is returned when a pattern that was successfully studied " + "using a JIT compile option is being matched, but the memory available " + "for the just-in-time processing stack is not large enough. See the pcrejit " + "documentation for more details (PCRE_ERROR_JIT_STACKLIMIT)"; + case PCRE_ERROR_BADMODE: + return "This error is given if a pattern that was compiled by the 8-bit library " + "is passed to a 16-bit or 32-bit library function, or vice versa (PCRE_ERROR_BADMODE)"; + case PCRE_ERROR_BADENDIANNESS: + return "This error is given if a pattern that was compiled and saved is reloaded on a " + "host with different endianness. The utility function pcre_pattern_to_host_byte_order() " + "can be used to convert such a pattern so that it runs on the new host (PCRE_ERROR_BADENDIANNESS)"; + case PCRE_ERROR_DFA_BADRESTART: + return "PCRE_ERROR_DFA_BADRESTART"; #if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32 - case PCRE_ERROR_BADLENGTH: - return "This error is given if pcre_exec() is called with a negative value for the length argument (PCRE_ERROR_BADLENGTH)"; - case PCRE_ERROR_JIT_BADOPTION: - return "This error is returned when a pattern that was successfully studied using a JIT compile " - "option is being matched, but the matching mode (partial or complete match) does not correspond " - "to any JIT compilation mode. When the JIT fast path function is used, this error may be " - "also given for invalid options. See the pcrejit documentation for more details (PCRE_ERROR_JIT_BADOPTION)"; + case PCRE_ERROR_BADLENGTH: + return "This error is given if pcre_exec() is called with a negative value for the length argument (PCRE_ERROR_BADLENGTH)"; + case PCRE_ERROR_JIT_BADOPTION: + return "This error is returned when a pattern that was successfully studied using a JIT compile " + "option is being matched, but the matching mode (partial or complete match) does not correspond " + "to any JIT compilation mode. When the JIT fast path function is used, this error may be " + "also given for invalid options. See the pcrejit documentation for more details (PCRE_ERROR_JIT_BADOPTION)"; #endif - } - return ""; + } + return ""; } #endif // HAVE_RULES -void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &tokenizer) +void CppCheck::executeRules (const std::string &tokenlist, const Tokenizer &tokenizer) { - (void)tokenlist; - (void)tokenizer; + (void)tokenlist; + (void)tokenizer; #ifdef HAVE_RULES - // There is no rule to execute - if (!hasRule(tokenlist)) - return; - - // Write all tokens in a string that can be parsed by pcre - std::ostringstream ostr; - for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) - ostr << " " << tok->str(); - const std::string str(ostr.str()); - - for (const Settings::Rule &rule : mSettings.rules) { - if (rule.pattern.empty() || rule.id.empty() || rule.severity == Severity::none || rule.tokenlist != tokenlist) - continue; - - const char *pcreCompileErrorStr = nullptr; - int erroffset = 0; - pcre * const re = pcre_compile(rule.pattern.c_str(),0,&pcreCompileErrorStr,&erroffset,nullptr); - if (!re) { - if (pcreCompileErrorStr) { - const std::string msg = "pcre_compile failed: " + std::string(pcreCompileErrorStr); - const ErrorMessage errmsg(std::list(), - emptyString, - Severity::error, - msg, - "pcre_compile", - false); - - reportErr(errmsg); - } - continue; - } - - // Optimize the regex, but only if PCRE_CONFIG_JIT is available + // There is no rule to execute + if (!hasRule (tokenlist)) + return; + + // Write all tokens in a string that can be parsed by pcre + std::ostringstream ostr; + for (const Token *tok = tokenizer.tokens (); tok; tok = tok->next ()) + ostr << " " << tok->str (); + const std::string str (ostr.str ()); + + for (const Settings::Rule &rule : mSettings.rules) { + if (rule.pattern.empty () || rule.id.empty () || rule.severity == Severity::none || rule.tokenlist != tokenlist) + continue; + + const char *pcreCompileErrorStr = nullptr; + int erroffset = 0; + pcre *const re = pcre_compile (rule.pattern.c_str (), 0, &pcreCompileErrorStr, &erroffset, nullptr); + if (!re) { + if (pcreCompileErrorStr) { + const std::string msg = "pcre_compile failed: " + std::string (pcreCompileErrorStr); + const ErrorMessage errmsg (std::list (), + emptyString, + Severity::error, + msg, + "pcre_compile", + false); + + reportErr (errmsg); + } + continue; + } + + // Optimize the regex, but only if PCRE_CONFIG_JIT is available #ifdef PCRE_CONFIG_JIT - const char *pcreStudyErrorStr = nullptr; - pcre_extra * const pcreExtra = pcre_study(re, PCRE_STUDY_JIT_COMPILE, &pcreStudyErrorStr); - // pcre_study() returns NULL for both errors and when it can not optimize the regex. - // The last argument is how one checks for errors. - // It is NULL if everything works, and points to an error string otherwise. - if (pcreStudyErrorStr) { - const std::string msg = "pcre_study failed: " + std::string(pcreStudyErrorStr); - const ErrorMessage errmsg(std::list(), - emptyString, - Severity::error, - msg, - "pcre_study", - false); - - reportErr(errmsg); - // pcre_compile() worked, but pcre_study() returned an error. Free the resources allocated by pcre_compile(). - pcre_free(re); - continue; - } + const char *pcreStudyErrorStr = nullptr; + pcre_extra *const pcreExtra = pcre_study (re, PCRE_STUDY_JIT_COMPILE, &pcreStudyErrorStr); + // pcre_study() returns NULL for both errors and when it can not optimize the regex. + // The last argument is how one checks for errors. + // It is NULL if everything works, and points to an error string otherwise. + if (pcreStudyErrorStr) { + const std::string msg = "pcre_study failed: " + std::string (pcreStudyErrorStr); + const ErrorMessage errmsg (std::list (), + emptyString, + Severity::error, + msg, + "pcre_study", + false); + + reportErr (errmsg); + // pcre_compile() worked, but pcre_study() returned an error. Free the resources allocated by pcre_compile(). + pcre_free (re); + continue; + } #else - const pcre_extra * const pcreExtra = nullptr; + const pcre_extra *const pcreExtra = nullptr; #endif - int pos = 0; - int ovector[30]= {0}; - while (pos < (int)str.size()) { - const int pcreExecRet = pcre_exec(re, pcreExtra, str.c_str(), (int)str.size(), pos, 0, ovector, 30); - if (pcreExecRet < 0) { - const std::string errorMessage = pcreErrorCodeToString(pcreExecRet); - if (!errorMessage.empty()) { - const ErrorMessage errmsg(std::list(), - emptyString, - Severity::error, - std::string("pcre_exec failed: ") + errorMessage, - "pcre_exec", - false); - - reportErr(errmsg); - } - break; - } - const unsigned int pos1 = (unsigned int)ovector[0]; - const unsigned int pos2 = (unsigned int)ovector[1]; - - // jump to the end of the match for the next pcre_exec - pos = (int)pos2; - - // determine location.. - ErrorMessage::FileLocation loc; - loc.setfile(tokenizer.list.getSourceFilePath()); - loc.line = 0; - - std::size_t len = 0; - for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { - len = len + 1U + tok->str().size(); - if (len > pos1) { - loc.setfile(tokenizer.list.getFiles().at(tok->fileIndex())); - loc.line = tok->linenr(); - break; - } - } - - const std::list callStack(1, loc); - - // Create error message - std::string summary; - if (rule.summary.empty()) - summary = "found '" + str.substr(pos1, pos2 - pos1) + "'"; - else - summary = rule.summary; - const ErrorMessage errmsg(callStack, tokenizer.list.getSourceFilePath(), rule.severity, summary, rule.id, false); - - // Report error - reportErr(errmsg); - } - - pcre_free(re); + int pos = 0; + int ovector[30] = { 0 }; + while (pos < (int)str.size ()) { + const int pcreExecRet = pcre_exec (re, pcreExtra, str.c_str (), (int)str.size (), pos, 0, ovector, 30); + if (pcreExecRet < 0) { + const std::string errorMessage = pcreErrorCodeToString (pcreExecRet); + if (!errorMessage.empty ()) { + const ErrorMessage errmsg (std::list (), + emptyString, + Severity::error, + std::string ("pcre_exec failed: ") + errorMessage, + "pcre_exec", + false); + + reportErr (errmsg); + } + break; + } + const unsigned int pos1 = (unsigned int)ovector[0]; + const unsigned int pos2 = (unsigned int)ovector[1]; + + // jump to the end of the match for the next pcre_exec + pos = (int)pos2; + + // determine location.. + ErrorMessage::FileLocation loc; + loc.setfile (tokenizer.list.getSourceFilePath ()); + loc.line = 0; + + std::size_t len = 0; + for (const Token *tok = tokenizer.tokens (); tok; tok = tok->next ()) { + len = len + 1U + tok->str ().size (); + if (len > pos1) { + loc.setfile (tokenizer.list.getFiles ().at (tok->fileIndex ())); + loc.line = tok->linenr (); + break; + } + } + + const std::list callStack (1, loc); + + // Create error message + std::string summary; + if (rule.summary.empty ()) + summary = "found '" + str.substr (pos1, pos2 - pos1) + "'"; + else + summary = rule.summary; + const ErrorMessage errmsg (callStack, tokenizer.list.getSourceFilePath (), rule.severity, summary, rule.id, false); + + // Report error + reportErr (errmsg); + } + + pcre_free (re); #ifdef PCRE_CONFIG_JIT - // Free up the EXTRA PCRE value (may be NULL at this point) - if (pcreExtra) { - pcre_free_study(pcreExtra); - } + // Free up the EXTRA PCRE value (may be NULL at this point) + if (pcreExtra) { + pcre_free_study (pcreExtra); + } #endif - } + } #endif } -Settings &CppCheck::settings() +Settings &CppCheck::settings () { - return mSettings; + return mSettings; } -void CppCheck::tooManyConfigsError(const std::string &file, const std::size_t numberOfConfigurations) +void CppCheck::tooManyConfigsError (const std::string &file, const std::size_t numberOfConfigurations) { - if (!mSettings.isEnabled(Settings::INFORMATION) && !mTooManyConfigs) - return; - - mTooManyConfigs = false; - - if (mSettings.isEnabled(Settings::INFORMATION) && file.empty()) - return; - - std::list loclist; - if (!file.empty()) { - ErrorMessage::FileLocation location; - location.setfile(file); - loclist.push_back(location); - } - - std::ostringstream msg; - msg << "Too many #ifdef configurations - cppcheck only checks " << mSettings.maxConfigs; - if (numberOfConfigurations > mSettings.maxConfigs) - msg << " of " << numberOfConfigurations << " configurations. Use --force to check all configurations.\n"; - if (file.empty()) - msg << " configurations. Use --force to check all configurations. For more details, use --enable=information.\n"; - msg << "The checking of the file will be interrupted because there are too many " - "#ifdef configurations. Checking of all #ifdef configurations can be forced " - "by --force command line option or from GUI preferences. However that may " - "increase the checking time."; - if (file.empty()) - msg << " For more details, use --enable=information."; - - - ErrorMessage errmsg(loclist, - emptyString, - Severity::information, - msg.str(), - "toomanyconfigs", CWE398, - false); - - reportErr(errmsg); + if (!mSettings.isEnabled (Settings::INFORMATION) && !mTooManyConfigs) + return; + + mTooManyConfigs = false; + + if (mSettings.isEnabled (Settings::INFORMATION) && file.empty ()) + return; + + std::list loclist; + if (!file.empty ()) { + ErrorMessage::FileLocation location; + location.setfile (file); + loclist.push_back (location); + } + + std::ostringstream msg; + msg << "Too many #ifdef configurations - cppcheck only checks " << mSettings.maxConfigs; + if (numberOfConfigurations > mSettings.maxConfigs) + msg << " of " << numberOfConfigurations << " configurations. Use --force to check all configurations.\n"; + if (file.empty ()) + msg << " configurations. Use --force to check all configurations. For more details, use --enable=information.\n"; + msg << "The checking of the file will be interrupted because there are too many " + "#ifdef configurations. Checking of all #ifdef configurations can be forced " + "by --force command line option or from GUI preferences. However that may " + "increase the checking time."; + if (file.empty ()) + msg << " For more details, use --enable=information."; + + + ErrorMessage errmsg (loclist, + emptyString, + Severity::information, + msg.str (), + "toomanyconfigs", CWE398, + false); + + reportErr (errmsg); } -void CppCheck::purgedConfigurationMessage(const std::string &file, const std::string& configuration) +void CppCheck::purgedConfigurationMessage (const std::string &file, const std::string &configuration) { - mTooManyConfigs = false; - - if (mSettings.isEnabled(Settings::INFORMATION) && file.empty()) - return; - - std::list loclist; - if (!file.empty()) { - ErrorMessage::FileLocation location; - location.setfile(file); - loclist.push_back(location); - } - - ErrorMessage errmsg(loclist, - emptyString, - Severity::information, - "The configuration '" + configuration + "' was not checked because its code equals another one.", - "purgedConfiguration", - false); - - reportErr(errmsg); + mTooManyConfigs = false; + + if (mSettings.isEnabled (Settings::INFORMATION) && file.empty ()) + return; + + std::list loclist; + if (!file.empty ()) { + ErrorMessage::FileLocation location; + location.setfile (file); + loclist.push_back (location); + } + + ErrorMessage errmsg (loclist, + emptyString, + Severity::information, + "The configuration '" + configuration + "' was not checked because its code equals another one.", + "purgedConfiguration", + false); + + reportErr (errmsg); } //--------------------------------------------------------------------------- -void CppCheck::reportErr(const ErrorMessage &msg) +void CppCheck::reportErr (const ErrorMessage &msg) { - mSuppressInternalErrorFound = false; - - if (!mSettings.library.reportErrors(msg.file0)) - return; - - const std::string errmsg = msg.toString(mSettings.verbose); - if (errmsg.empty()) - return; - - // Alert only about unique errors - if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) != mErrorList.end()) - return; - - const Suppressions::ErrorMessage errorMessage = msg.toSuppressionsErrorMessage(); - - if (mUseGlobalSuppressions) { - if (mSettings.nomsg.isSuppressed(errorMessage)) { - mSuppressInternalErrorFound = true; - return; - } - } else { - if (mSettings.nomsg.isSuppressedLocal(errorMessage)) { - mSuppressInternalErrorFound = true; - return; - } - } - - if (!mSettings.nofail.isSuppressed(errorMessage) && !mSettings.nomsg.isSuppressed(errorMessage)) { - mExitCode = 1; - } - - mErrorList.push_back(errmsg); - - mErrorLogger.reportErr(msg); - mAnalyzerInformation.reportErr(msg, mSettings.verbose); - if (!mSettings.plistOutput.empty() && plistFile.is_open()) { - plistFile << ErrorLogger::plistData(msg); - } + mSuppressInternalErrorFound = false; + + if (!mSettings.library.reportErrors (msg.file0)) + return; + + const std::string errmsg = msg.toString (mSettings.verbose); + if (errmsg.empty ()) + return; + + // Alert only about unique errors + if (std::find (mErrorList.begin (), mErrorList.end (), errmsg) != mErrorList.end ()) + return; + + const Suppressions::ErrorMessage errorMessage = msg.toSuppressionsErrorMessage (); + + if (mUseGlobalSuppressions) { + if (mSettings.nomsg.isSuppressed (errorMessage)) { + mSuppressInternalErrorFound = true; + return; + } + } + else { + if (mSettings.nomsg.isSuppressedLocal (errorMessage)) { + mSuppressInternalErrorFound = true; + return; + } + } + + if (!mSettings.nofail.isSuppressed (errorMessage) && !mSettings.nomsg.isSuppressed (errorMessage)) { + mExitCode = 1; + } + + mErrorList.push_back (errmsg); + + mErrorLogger.reportErr (msg); + mAnalyzerInformation.reportErr (msg, mSettings.verbose); + if (!mSettings.plistOutput.empty () && plistFile.is_open ()) { + plistFile << ErrorLogger::plistData (msg); + } } -void CppCheck::reportOut(const std::string &outmsg) +void CppCheck::reportOut (const std::string &outmsg) { - mErrorLogger.reportOut(outmsg); + mErrorLogger.reportOut (outmsg); } -void CppCheck::reportProgress(const std::string &filename, const char stage[], const std::size_t value) +void CppCheck::reportProgress (const std::string &filename, const char stage[], const std::size_t value) { - mErrorLogger.reportProgress(filename, stage, value); + mErrorLogger.reportProgress (filename, stage, value); } -void CppCheck::reportInfo(const ErrorMessage &msg) +void CppCheck::reportInfo (const ErrorMessage &msg) { - const Suppressions::ErrorMessage &errorMessage = msg.toSuppressionsErrorMessage(); - if (!mSettings.nomsg.isSuppressed(errorMessage)) - mErrorLogger.reportInfo(msg); + const Suppressions::ErrorMessage &errorMessage = msg.toSuppressionsErrorMessage (); + if (!mSettings.nomsg.isSuppressed (errorMessage)) + mErrorLogger.reportInfo (msg); } -void CppCheck::reportStatus(unsigned int /*fileindex*/, unsigned int /*filecount*/, std::size_t /*sizedone*/, std::size_t /*sizetotal*/) +void CppCheck::reportStatus (unsigned int /*fileindex*/, unsigned int /*filecount*/, std::size_t /*sizedone*/, std::size_t /*sizetotal*/) { } -void CppCheck::bughuntingReport(const std::string &str) +void CppCheck::bughuntingReport (const std::string &str) { - mErrorLogger.bughuntingReport(str); + mErrorLogger.bughuntingReport (str); } -void CppCheck::getErrorMessages() +void CppCheck::getErrorMessages () { - Settings s(mSettings); - s.addEnabled("warning"); - s.addEnabled("style"); - s.addEnabled("portability"); - s.addEnabled("performance"); - s.addEnabled("information"); + Settings s (mSettings); + s.addEnabled ("warning"); + s.addEnabled ("style"); + s.addEnabled ("portability"); + s.addEnabled ("performance"); + s.addEnabled ("information"); - purgedConfigurationMessage("",""); + purgedConfigurationMessage ("", ""); - mTooManyConfigs = true; - tooManyConfigsError("",0U); + mTooManyConfigs = true; + tooManyConfigsError ("", 0U); - // call all "getErrorMessages" in all registered Check classes - for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) - (*it)->getErrorMessages(this, &s); + // call all "getErrorMessages" in all registered Check classes + for (std::list::const_iterator it = Check::instances ().begin (); it != Check::instances ().end (); ++it) + (*it)->getErrorMessages (this, &s); - Preprocessor::getErrorMessages(this, &s); + Preprocessor::getErrorMessages (this, &s); } -void CppCheck::analyseClangTidy(const ImportProject::FileSettings &fileSettings) +void CppCheck::analyseClangTidy (const ImportProject::FileSettings &fileSettings) { - std::string allIncludes = ""; - for (const std::string &inc : fileSettings.includePaths) { - allIncludes = allIncludes + "-I\"" + inc + "\" "; - } + std::string allIncludes = ""; + for (const std::string &inc : fileSettings.includePaths) { + allIncludes = allIncludes + "-I\"" + inc + "\" "; + } - const std::string allDefines = getDefinesFlags(fileSettings.defines); + const std::string allDefines = getDefinesFlags (fileSettings.defines); #ifdef _WIN32 - const char exe[] = "clang-tidy.exe"; + const char exe[] = "clang-tidy.exe"; #else - const char exe[] = "clang-tidy"; + const char exe[] = "clang-tidy"; #endif - const std::string args = "-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines; - std::string output; - if (!mExecuteCommand(exe, split(args), "", &output)) { - std::cerr << "Failed to execute '" << exe << "'" << std::endl; - return; - } - - // parse output and create error messages - std::istringstream istr(output); - std::string line; - - if (!mSettings.buildDir.empty()) { - const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename, ""); - std::ofstream fcmd(analyzerInfoFile + ".clang-tidy-cmd"); - fcmd << istr.str(); - } - - while (std::getline(istr, line)) { - if (line.find("error") == std::string::npos && line.find("warning") == std::string::npos) - continue; - - std::size_t endColumnPos = line.find(": error:"); - if (endColumnPos == std::string::npos) { - endColumnPos = line.find(": warning:"); - } - - const std::size_t endLinePos = line.rfind(":", endColumnPos-1); - const std::size_t endNamePos = line.rfind(":", endLinePos - 1); - const std::size_t endMsgTypePos = line.find(':', endColumnPos + 2); - const std::size_t endErrorPos = line.rfind('[', std::string::npos); - if (endLinePos==std::string::npos || endNamePos==std::string::npos || endMsgTypePos==std::string::npos || endErrorPos==std::string::npos) - continue; - - const std::string lineNumString = line.substr(endNamePos + 1, endLinePos - endNamePos - 1); - const std::string columnNumString = line.substr(endLinePos + 1, endColumnPos - endLinePos - 1); - const std::string errorTypeString = line.substr(endColumnPos + 1, endMsgTypePos - endColumnPos - 1); - const std::string messageString = line.substr(endMsgTypePos + 1, endErrorPos - endMsgTypePos - 1); - const std::string errorString = line.substr(endErrorPos, line.length()); - - std::string fixedpath = Path::simplifyPath(line.substr(0, endNamePos)); - const int64_t lineNumber = std::atol(lineNumString.c_str()); - const int64_t column = std::atol(columnNumString.c_str()); - fixedpath = Path::toNativeSeparators(fixedpath); - - ErrorMessage errmsg; - errmsg.callStack.emplace_back(ErrorMessage::FileLocation(fixedpath, lineNumber, column)); - - errmsg.id = "clang-tidy-" + errorString.substr(1, errorString.length() - 2); - if (errmsg.id.find("performance") != std::string::npos) - errmsg.severity = Severity::SeverityType::performance; - else if (errmsg.id.find("portability") != std::string::npos) - errmsg.severity = Severity::SeverityType::portability; - else if (errmsg.id.find("cert") != std::string::npos || errmsg.id.find("misc") != std::string::npos || errmsg.id.find("unused") != std::string::npos) - errmsg.severity = Severity::SeverityType::warning; - else - errmsg.severity = Severity::SeverityType::style; - - errmsg.file0 = fixedpath; - errmsg.setmsg(messageString); - reportErr(errmsg); - } + const std::string args = "-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines; + std::string output; + if (!mExecuteCommand (exe, split (args), "", &output)) { + std::cerr << "Failed to execute '" << exe << "'" << std::endl; + return; + } + + // parse output and create error messages + std::istringstream istr (output); + std::string line; + + if (!mSettings.buildDir.empty ()) { + const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile (mSettings.buildDir, fileSettings.filename, ""); + std::ofstream fcmd (analyzerInfoFile + ".clang-tidy-cmd"); + fcmd << istr.str (); + } + + while (std::getline (istr, line)) { + if (line.find ("error") == std::string::npos && line.find ("warning") == std::string::npos) + continue; + + std::size_t endColumnPos = line.find (": error:"); + if (endColumnPos == std::string::npos) { + endColumnPos = line.find (": warning:"); + } + + const std::size_t endLinePos = line.rfind (":", endColumnPos - 1); + const std::size_t endNamePos = line.rfind (":", endLinePos - 1); + const std::size_t endMsgTypePos = line.find (':', endColumnPos + 2); + const std::size_t endErrorPos = line.rfind ('[', std::string::npos); + if (endLinePos == std::string::npos || endNamePos == std::string::npos || endMsgTypePos == std::string::npos || endErrorPos == std::string::npos) + continue; + + const std::string lineNumString = line.substr (endNamePos + 1, endLinePos - endNamePos - 1); + const std::string columnNumString = line.substr (endLinePos + 1, endColumnPos - endLinePos - 1); + const std::string errorTypeString = line.substr (endColumnPos + 1, endMsgTypePos - endColumnPos - 1); + const std::string messageString = line.substr (endMsgTypePos + 1, endErrorPos - endMsgTypePos - 1); + const std::string errorString = line.substr (endErrorPos, line.length ()); + + std::string fixedpath = Path::simplifyPath (line.substr (0, endNamePos)); + const int64_t lineNumber = std::atol (lineNumString.c_str ()); + const int64_t column = std::atol (columnNumString.c_str ()); + fixedpath = Path::toNativeSeparators (fixedpath); + + ErrorMessage errmsg; + errmsg.callStack.emplace_back (ErrorMessage::FileLocation (fixedpath, lineNumber, column)); + + errmsg.id = "clang-tidy-" + errorString.substr (1, errorString.length () - 2); + if (errmsg.id.find ("performance") != std::string::npos) + errmsg.severity = Severity::SeverityType::performance; + else if (errmsg.id.find ("portability") != std::string::npos) + errmsg.severity = Severity::SeverityType::portability; + else if (errmsg.id.find ("cert") != std::string::npos || errmsg.id.find ("misc") != std::string::npos || errmsg.id.find ("unused") != std::string::npos) + errmsg.severity = Severity::SeverityType::warning; + else + errmsg.severity = Severity::SeverityType::style; + + errmsg.file0 = fixedpath; + errmsg.setmsg (messageString); + reportErr (errmsg); + } } -bool CppCheck::analyseWholeProgram() +bool CppCheck::analyseWholeProgram () { - bool errors = false; - // Init CTU - CTU::maxCtuDepth = mSettings.maxCtuDepth; - // Analyse the tokens - CTU::FileInfo ctu; - for (const Check::FileInfo *fi : mFileInfo) { - const CTU::FileInfo *fi2 = dynamic_cast(fi); - if (fi2) { - ctu.functionCalls.insert(ctu.functionCalls.end(), fi2->functionCalls.begin(), fi2->functionCalls.end()); - ctu.nestedCalls.insert(ctu.nestedCalls.end(), fi2->nestedCalls.begin(), fi2->nestedCalls.end()); - } - } - for (Check *check : Check::instances()) - errors |= check->analyseWholeProgram(&ctu, mFileInfo, mSettings, *this); // TODO: ctu - return errors && (mExitCode > 0); + bool errors = false; + // Init CTU + CTU::maxCtuDepth = mSettings.maxCtuDepth; + // Analyse the tokens + CTU::FileInfo ctu; + for (const Check::FileInfo *fi : mFileInfo) { + const CTU::FileInfo *fi2 = dynamic_cast(fi); + if (fi2) { + ctu.functionCalls.insert (ctu.functionCalls.end (), fi2->functionCalls.begin (), fi2->functionCalls.end ()); + ctu.nestedCalls.insert (ctu.nestedCalls.end (), fi2->nestedCalls.begin (), fi2->nestedCalls.end ()); + } + } + for (Check *check : Check::instances ()) + errors |= check->analyseWholeProgram (&ctu, mFileInfo, mSettings, *this); // TODO: ctu + return errors && (mExitCode > 0); } -void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map &files) +void CppCheck::analyseWholeProgram (const std::string &buildDir, const std::map &files) { - (void)files; - if (buildDir.empty()) - return; - if (mSettings.isEnabled(Settings::UNUSED_FUNCTION)) - CheckUnusedFunctions::analyseWholeProgram(this, buildDir); - std::list fileInfoList; - CTU::FileInfo ctuFileInfo; - - // Load all analyzer info data.. - const std::string filesTxt(buildDir + "/files.txt"); - std::ifstream fin(filesTxt); - std::string filesTxtLine; - while (std::getline(fin, filesTxtLine)) { - const std::string::size_type firstColon = filesTxtLine.find(':'); - if (firstColon == std::string::npos) - continue; - const std::string::size_type lastColon = filesTxtLine.rfind(':'); - if (firstColon == lastColon) - continue; - const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon); - //const std::string sourcefile = filesTxtLine.substr(lastColon+1); - - tinyxml2::XMLDocument doc; - const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str()); - if (error != tinyxml2::XML_SUCCESS) - continue; - - const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); - if (rootNode == nullptr) - continue; - - for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { - if (std::strcmp(e->Name(), "FileInfo") != 0) - continue; - const char *checkClassAttr = e->Attribute("check"); - if (!checkClassAttr) - continue; - if (std::strcmp(checkClassAttr, "ctu") == 0) { - ctuFileInfo.loadFromXml(e); - continue; - } - for (Check *check : Check::instances()) { - if (checkClassAttr == check->name()) - fileInfoList.push_back(check->loadFileInfoFromXml(e)); - } - } - } - - // Set CTU max depth - CTU::maxCtuDepth = mSettings.maxCtuDepth; - - // Analyse the tokens - for (Check *check : Check::instances()) - check->analyseWholeProgram(&ctuFileInfo, fileInfoList, mSettings, *this); - - for (Check::FileInfo *fi : fileInfoList) - delete fi; + (void)files; + if (buildDir.empty ()) + return; + if (mSettings.isEnabled (Settings::UNUSED_FUNCTION)) + CheckUnusedFunctions::analyseWholeProgram (this, buildDir); + std::list fileInfoList; + CTU::FileInfo ctuFileInfo; + + // Load all analyzer info data.. + const std::string filesTxt (buildDir + "/files.txt"); + std::ifstream fin (filesTxt); + std::string filesTxtLine; + while (std::getline (fin, filesTxtLine)) { + const std::string::size_type firstColon = filesTxtLine.find (':'); + if (firstColon == std::string::npos) + continue; + const std::string::size_type lastColon = filesTxtLine.rfind (':'); + if (firstColon == lastColon) + continue; + const std::string xmlfile = buildDir + '/' + filesTxtLine.substr (0, firstColon); + //const std::string sourcefile = filesTxtLine.substr(lastColon+1); + + tinyxml2::XMLDocument doc; + const tinyxml2::XMLError error = doc.LoadFile (xmlfile.c_str ()); + if (error != tinyxml2::XML_SUCCESS) + continue; + + const tinyxml2::XMLElement *const rootNode = doc.FirstChildElement (); + if (rootNode == nullptr) + continue; + + for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement (); e; e = e->NextSiblingElement ()) { + if (std::strcmp (e->Name (), "FileInfo") != 0) + continue; + const char *checkClassAttr = e->Attribute ("check"); + if (!checkClassAttr) + continue; + if (std::strcmp (checkClassAttr, "ctu") == 0) { + ctuFileInfo.loadFromXml (e); + continue; + } + for (Check *check : Check::instances ()) { + if (checkClassAttr == check->name ()) + fileInfoList.push_back (check->loadFileInfoFromXml (e)); + } + } + } + + // Set CTU max depth + CTU::maxCtuDepth = mSettings.maxCtuDepth; + + // Analyse the tokens + for (Check *check : Check::instances ()) + check->analyseWholeProgram (&ctuFileInfo, fileInfoList, mSettings, *this); + + for (Check::FileInfo *fi : fileInfoList) + delete fi; } -bool CppCheck::isUnusedFunctionCheckEnabled() const +bool CppCheck::isUnusedFunctionCheckEnabled () const { - return (mSettings.jobs == 1 && mSettings.isEnabled(Settings::UNUSED_FUNCTION)); + return (mSettings.jobs == 1 && mSettings.isEnabled (Settings::UNUSED_FUNCTION)); } + +void CppCheck::registerImplementations () { + const SymbolDatabase *symbols = mTokenizer->getSymbolDatabase (); + + for (const Scope *scope : symbols->functionScopes) { + // fileIndex > 0 means function definition lives in header file + // and potentially accesible for other CUs + if (scope->function && scope->function->tokenDef->fileIndex () > 0) { + CheckExceptionSafety::FunctionImpls &impls = + mParent ? mParent->mImplementations : mImplementations; + + impls[scope->function->getQualifiedName ()] = std::make_pair (scope, this); + } + } +} \ No newline at end of file diff --git a/lib/cppcheck.h b/lib/cppcheck.h index fbdd95b1810..b0a945aa846 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -36,9 +36,13 @@ #include class Tokenizer; +class Scope; /// @addtogroup Core /// @{ +class CppCheck; +typedef std::shared_ptr PCppCheck; +typedef std::shared_ptr CPCppCheck; /** * @brief This is the base class which will use other classes to do @@ -51,7 +55,7 @@ class CPPCHECKLIB CppCheck : ErrorLogger { /** * @brief Constructor. */ - CppCheck(ErrorLogger &errorLogger, + CppCheck(CppCheck *parent, ErrorLogger &errorLogger, bool useGlobalSuppressions, std::function,std::string,std::string*)> executeCommand); @@ -77,6 +81,8 @@ class CPPCHECKLIB CppCheck : ErrorLogger { unsigned int check(const std::string &path); unsigned int check(const ImportProject::FileSettings &fs); + void checkExceptionSafety (); + /** * @brief Check the file. * This function checks one "virtual" file. The file is not read from @@ -203,6 +209,8 @@ class CPPCHECKLIB CppCheck : ErrorLogger { void bughuntingReport(const std::string &str) OVERRIDE; + void registerImplementations (); + std::list mErrorList; Settings mSettings; @@ -237,6 +245,11 @@ class CPPCHECKLIB CppCheck : ErrorLogger { /** Callback for executing a shell command (exe, args, output) */ std::function,std::string,std::string*)> mExecuteCommand; + + std::map> mImplementations; + std::unique_ptr mTokenizer; + std::vector mChildren; + CppCheck *mParent; }; /// @} diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 07af526d96f..b3ef7280194 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -2474,6 +2474,59 @@ const Token * Function::constructorMemberInitialization() const return nullptr; } +std::string Function::getQualifiedName () const +{ + std::string function_name; + + if (tokenDef) { + if (const Scope *parentScope = nestedIn) { + std::stack fqpn; + + do { + switch (parentScope->type) { + case Scope::eClass: case Scope::eStruct: { + fqpn.push (parentScope->className); + if (::Type *type = parentScope->definedType) + parentScope = type->enclosingScope; + else + parentScope = nullptr; + break; + } + + case Scope::eNamespace: { + fqpn.push (parentScope->className); + parentScope = parentScope->nestedIn; + break; + } + + case Scope::eGlobal: { + parentScope = nullptr; + break; + } + } + } while (parentScope); + + while (!fqpn.empty ()) { + function_name += fqpn.top () + " :: "; + fqpn.pop (); + } + + if (Function::eDestructor == type) { + function_name += '~'; + } + } + + if (const Token *def = tokenDef) { + do { + function_name += def->str () + ' '; + def = def->next (); + } while (def && def->str () != ";" && def->str () != "{" && def->str () != "override"); + } + } + + return function_name; +} + bool Function::isSafe(const Settings *settings) const { if (settings->safeChecks.externalFunctions) { diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 0d4ba92ff7f..1f1bcf3169f 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -912,6 +912,8 @@ class CPPCHECKLIB Function { */ const Token * constructorMemberInitialization() const; + std::string getQualifiedName () const; + private: /** Recursively determine if this function overrides a virtual function in a base class */ const Function * getOverriddenFunctionRecursive(const ::Type* baseType, bool *foundAllBaseClasses) const; diff --git a/stdc++headers/algorithm b/stdc++headers/algorithm new file mode 100644 index 00000000000..4c845e0928e --- /dev/null +++ b/stdc++headers/algorithm @@ -0,0 +1,13 @@ +#pragma once +#include + +namespace std { + +template +inline void swap(T& lhs, T& rhs) noexcept { + T tmp = std::move(lhs); + lhs = std::move(rhs); + rhs = std::move(tmp); +} + +} \ No newline at end of file diff --git a/stdc++headers/boost/enable_shared_from_this.hpp b/stdc++headers/boost/enable_shared_from_this.hpp new file mode 100644 index 00000000000..7745cdf0226 --- /dev/null +++ b/stdc++headers/boost/enable_shared_from_this.hpp @@ -0,0 +1,2 @@ +#pragma once +#include \ No newline at end of file diff --git a/stdc++headers/boost/make_shared.hpp b/stdc++headers/boost/make_shared.hpp new file mode 100644 index 00000000000..0bc92fd6b99 --- /dev/null +++ b/stdc++headers/boost/make_shared.hpp @@ -0,0 +1,2 @@ +#pragma once +#include diff --git a/stdc++headers/boost/shared_ptr.hpp b/stdc++headers/boost/shared_ptr.hpp new file mode 100644 index 00000000000..b96154b6b13 --- /dev/null +++ b/stdc++headers/boost/shared_ptr.hpp @@ -0,0 +1,2 @@ +#pragma once +#include diff --git a/stdc++headers/boost/smart_ptr.hpp b/stdc++headers/boost/smart_ptr.hpp new file mode 100644 index 00000000000..832f0472c6b --- /dev/null +++ b/stdc++headers/boost/smart_ptr.hpp @@ -0,0 +1,5 @@ +#pragma once +#include +#include +#include +#include \ No newline at end of file diff --git a/stdc++headers/boost/smart_ptr/enable_shared_from_this.hpp b/stdc++headers/boost/smart_ptr/enable_shared_from_this.hpp new file mode 100644 index 00000000000..c352937e7e1 --- /dev/null +++ b/stdc++headers/boost/smart_ptr/enable_shared_from_this.hpp @@ -0,0 +1,29 @@ +#pragma once +#include +#include + +namespace boost { + + template class enable_shared_from_this { + private: + + // exposition only + weak_ptr weak_this_; + + protected: + + enable_shared_from_this() = default; + ~enable_shared_from_this() = default; + + enable_shared_from_this(const enable_shared_from_this&) noexcept; + enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept; + + public: + + shared_ptr shared_from_this() noexcept(false); + shared_ptr shared_from_this() const noexcept(false); + + weak_ptr weak_from_this() noexcept; + weak_ptr weak_from_this() const noexcept; + } +} \ No newline at end of file diff --git a/stdc++headers/boost/smart_ptr/make_shared.hpp b/stdc++headers/boost/smart_ptr/make_shared.hpp new file mode 100644 index 00000000000..930aed04797 --- /dev/null +++ b/stdc++headers/boost/smart_ptr/make_shared.hpp @@ -0,0 +1,46 @@ +#pragma once +#include + +namespace boost { + // T is not an array + template + shared_ptr make_shared(Args&&... args) noexcept(false); + template + shared_ptr allocate_shared(const A& a, Args&&... args); + + // T is an array of unknown bounds + template + shared_ptr make_shared(std::size_t n) noexcept(false); + template + shared_ptr allocate_shared(const A& a, std::size_t n); + + // T is an array of known bounds + template + shared_ptr make_shared() noexcept(false); + template + shared_ptr allocate_shared(const A& a); + + // T is an array of unknown bounds + template shared_ptr + make_shared(std::size_t n, const std::remove_extent_t& v) noexcept(false); + template shared_ptr + allocate_shared(const A& a, std::size_t n, const std::remove_extent_t& v); + + // T is an array of known bounds + template + shared_ptr make_shared(const std::remove_extent_t& v) noexcept(false); + template + shared_ptr allocate_shared(const A& a, const std::remove_extent_t& v); + + // T is not an array of unknown bounds + template + shared_ptr make_shared_noinit() noexcept(false); + template + shared_ptr allocate_shared_noinit(const A& a); + + // T is an array of unknown bounds + template + shared_ptr make_shared_noinit(std::size_t n) noexcept(false); + template + shared_ptr allocate_shared_noinit(const A& a, std::size_t n); +} \ No newline at end of file diff --git a/stdc++headers/boost/smart_ptr/shared_ptr.hpp b/stdc++headers/boost/smart_ptr/shared_ptr.hpp new file mode 100644 index 00000000000..73d07509539 --- /dev/null +++ b/stdc++headers/boost/smart_ptr/shared_ptr.hpp @@ -0,0 +1,84 @@ +#pragma once +#include + +namespace boost +{ + template class shared_ptr; + template class weak_ptr; + + template class shared_ptr { + public: + using element_type = std::remove_extent_t; + using weak_type = weak_ptr; + + // constructors + constexpr shared_ptr() noexcept; + constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { } + template + explicit shared_ptr(Y* p); + template + shared_ptr(Y* p, D d); + template + shared_ptr(Y* p, D d, A a); + template + shared_ptr(nullptr_t p, D d); + template + shared_ptr(nullptr_t p, D d, A a); + template + shared_ptr(const shared_ptr& r, element_type* p) noexcept; + template + shared_ptr(shared_ptr&& r, element_type* p) noexcept; + shared_ptr(const shared_ptr& r) noexcept; + template + shared_ptr(const shared_ptr& r) noexcept; + shared_ptr(shared_ptr&& r) noexcept; + template + shared_ptr(shared_ptr&& r) noexcept; + template + explicit shared_ptr(const weak_ptr& r); + template + shared_ptr(unique_ptr&& r); + + // destructor + ~shared_ptr(); + + // assignment + shared_ptr& operator=(const shared_ptr& r) noexcept; + template + shared_ptr& operator=(const shared_ptr& r) noexcept; + shared_ptr& operator=(shared_ptr&& r) noexcept; + template + shared_ptr& operator=(shared_ptr&& r) noexcept; + template + shared_ptr& operator=(unique_ptr&& r); + + // modifiers + void swap(shared_ptr& r) noexcept; + void reset() noexcept; + template + void reset(Y* p); + template + void reset(Y* p, D d); + template + void reset(Y* p, D d, A a); + + // observers + element_type* get() const noexcept; + T& operator*() const noexcept; + T* operator->() const noexcept; + element_type& operator[](ptrdiff_t i) const; + long use_count() const noexcept; + explicit operator bool() const noexcept; + template + bool owner_before(const shared_ptr& b) const noexcept; + template + bool owner_before(const weak_ptr& b) const noexcept; + }; + + template + shared_ptr(weak_ptr) -> shared_ptr; + template + shared_ptr(unique_ptr) -> shared_ptr; + + +} // namespace boost diff --git a/stdc++headers/boost/smart_ptr/weak_ptr.hpp b/stdc++headers/boost/smart_ptr/weak_ptr.hpp new file mode 100644 index 00000000000..d15a47550a3 --- /dev/null +++ b/stdc++headers/boost/smart_ptr/weak_ptr.hpp @@ -0,0 +1,52 @@ +#pragma once +#include + +namespace boost { + + + template class weak_ptr { + public: + using element_type = std::remove_extent_t; + + // constructors + constexpr weak_ptr() noexcept; + template + weak_ptr(const shared_ptr& r) noexcept; + weak_ptr(const weak_ptr& r) noexcept; + template + weak_ptr(const weak_ptr& r) noexcept; + weak_ptr(weak_ptr&& r) noexcept; + template + weak_ptr(weak_ptr&& r) noexcept; + + // destructor + ~weak_ptr(); + + // assignment + weak_ptr& operator=(const weak_ptr& r) noexcept; + template + weak_ptr& operator=(const weak_ptr& r) noexcept; + template + weak_ptr& operator=(const shared_ptr& r) noexcept; + weak_ptr& operator=(weak_ptr&& r) noexcept; + template + weak_ptr& operator=(weak_ptr&& r) noexcept; + + // modifiers + void swap(weak_ptr& r) noexcept; + void reset() noexcept; + + // observers + long use_count() const noexcept; + bool expired() const noexcept; + shared_ptr lock() const noexcept; + template + bool owner_before(const shared_ptr& b) const noexcept; + template + bool owner_before(const weak_ptr& b) const noexcept; + }; + + template + weak_ptr(shared_ptr) -> weak_ptr; + +} \ No newline at end of file diff --git a/stdc++headers/boost/thread/tss.hpp b/stdc++headers/boost/thread/tss.hpp new file mode 100644 index 00000000000..4fdeb07d5fd --- /dev/null +++ b/stdc++headers/boost/thread/tss.hpp @@ -0,0 +1,86 @@ +#pragma once + +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// (C) Copyright 2007-8 Anthony Williams + +namespace boost +{ + namespace detail + { + namespace thread + { + typedef void(*cleanup_func_t)(void*); + typedef void(*cleanup_caller_t)(cleanup_func_t, void*); + } + + void set_tss_data(void const* key,detail::thread::cleanup_caller_t caller,detail::thread::cleanup_func_t func,void* tss_data,bool cleanup_existing); + void* get_tss_data(void const* key); + } + + template + class thread_specific_ptr + { + private: + thread_specific_ptr(thread_specific_ptr&); + thread_specific_ptr& operator=(thread_specific_ptr&); + + typedef void(*original_cleanup_func_t)(T*); + + static void default_deleter(T* data) + { + delete data; + } + + static void cleanup_caller(detail::thread::cleanup_func_t cleanup_function,void* data) + { + reinterpret_cast(cleanup_function)(static_cast(data)); + } + + + detail::thread::cleanup_func_t cleanup; + + public: + typedef T element_type; + + thread_specific_ptr(): + cleanup(reinterpret_cast(&default_deleter)) + {} + explicit thread_specific_ptr(void (*func_)(T*)) + : cleanup(reinterpret_cast(func_)) + {} + ~thread_specific_ptr() + { + detail::set_tss_data(this,0,0,0,true); + } + + T* get() const + { + return static_cast(detail::get_tss_data(this)); + } + T* operator->() const + { + return get(); + } + typename add_reference::type operator*() const + { + return *get(); + } + T* release() + { + T* const temp=get(); + detail::set_tss_data(this,0,0,0,false); + return temp; + } + void reset(T* new_value=0) + { + T* const current_value=get(); + if(current_value!=new_value) + { + detail::set_tss_data(this,&cleanup_caller,cleanup,new_value,true); + } + } + }; +} + diff --git a/stdc++headers/boost/weak_ptr.hpp b/stdc++headers/boost/weak_ptr.hpp new file mode 100644 index 00000000000..2bca6e915e9 --- /dev/null +++ b/stdc++headers/boost/weak_ptr.hpp @@ -0,0 +1,2 @@ +#pragma once +#include diff --git a/stdc++headers/cassert b/stdc++headers/cassert new file mode 100644 index 00000000000..5150916abb7 --- /dev/null +++ b/stdc++headers/cassert @@ -0,0 +1,3 @@ +#pragma once + +#define assert(expression) ((void)0) \ No newline at end of file diff --git a/stdc++headers/cstddef b/stdc++headers/cstddef new file mode 100644 index 00000000000..4056c6cc8cd --- /dev/null +++ b/stdc++headers/cstddef @@ -0,0 +1,4 @@ +#pragma once +#define NULL nullptr + +typedef decltype(nullptr) nullptr_t; \ No newline at end of file diff --git a/stdc++headers/memory b/stdc++headers/memory new file mode 100644 index 00000000000..53276d38252 --- /dev/null +++ b/stdc++headers/memory @@ -0,0 +1,619 @@ +#pragma once +#include + +namespace std { + // pointer conversion + template + constexpr T* to_address(T* p) noexcept; + template + auto to_address(const Ptr& p) noexcept; + + // pointer safety + enum class pointer_safety { relaxed, preferred, strict }; + void declare_reachable(void* p); + template + T* undeclare_reachable(T* p); + void declare_no_pointers(char* p, size_t n); + void undeclare_no_pointers(char* p, size_t n); + pointer_safety get_pointer_safety() noexcept; + + // pointer alignment + void* align(size_t alignment, size_t size, void*& ptr, size_t& space); + template + [[nodiscard]] constexpr T* assume_aligned(T* ptr); + + // allocator argument tag + struct allocator_arg_t { explicit allocator_arg_t() = default; }; + inline constexpr allocator_arg_t allocator_arg{}; + + // uses_allocator + template struct uses_allocator; + + // uses_allocator + template + inline constexpr bool uses_allocator_v = uses_allocator::value; + + // uses-allocator construction + template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, Args&&... args) + noexcept -> /* see definition */; + template + constexpr auto uses_allocator_construction_args( + const Alloc& alloc, piecewise_construct_t, Tuple1&& x, Tuple2&& y) + noexcept -> /* see definition */; + template + constexpr auto uses_allocator_construction_args(const Alloc& alloc) + noexcept -> /* see definition */; + template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, U&& u, V&& v) + noexcept -> /* see definition */; + template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, + const pair& pr) + noexcept -> /* see definition */; + template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, pair&& pr) + noexcept -> /* see definition */; + template + constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args); + template + T* uninitialized_construct_using_allocator(T* p, const Alloc& alloc, Args&&... args); + + // allocator traits + template struct allocator_traits; + + // the default allocator + template class allocator; + template + bool operator==(const allocator&, const allocator&) noexcept; + template + bool operator!=(const allocator&, const allocator&) noexcept; + + template + constexpr T* addressof(T& r) noexcept; + template + const T* addressof(const T&&) = delete; + template + void uninitialized_default_construct(ForwardIt first, ForwardIt last); + template + void uninitialized_default_construct(ExecutionPolicy&& exec, + ForwardIt first, ForwardIt last); + template + ForwardIt uninitialized_default_construct_n(ForwardIt first, Size n); + template + ForwardIt uninitialized_default_construct_n(ExecutionPolicy&& exec, + ForwardIt first, Size n); + + template + void uninitialized_value_construct(ForwardIt first, ForwardIt last); + template + void uninitialized_value_construct(ExecutionPolicy&& exec, + ForwardIt first, ForwardIt last); + template + ForwardIt uninitialized_value_construct_n(ForwardIt first, Size n); + template + ForwardIt uninitialized_value_construct_n(ExecutionPolicy&& exec, + ForwardIt first, Size n); + + template + ForwardIt uninitialized_copy(InputIt first, InputIt last, ForwardIt result); + template + ForwardIt uninitialized_copy(ExecutionPolicy&& exec, + InputIt first, InputIt last, ForwardIt result); + template + ForwardIt uninitialized_copy_n(InputIt first, Size n, ForwardIt result); + template + ForwardIt uninitialized_copy_n(ExecutionPolicy&& exec, + InputIt first, Size n, ForwardIt result); + + template + ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt result); + template + ForwardIt uninitialized_move(ExecutionPolicy&& exec, + InputIt first, InputIt last, ForwardIt result); + template + pair uninitialized_move_n(InputIt first, Size n, + ForwardIt result); + template + pair uninitialized_move_n(ExecutionPolicy&& exec, + InputIt first, Size n, + ForwardIt result); + + template + void uninitialized_fill(ForwardIt first, ForwardIt last, const T& x); + template + void uninitialized_fill(ExecutionPolicy&& exec, + ForwardIt first, ForwardIt last, const T& x); + template + ForwardIt uninitialized_fill_n(ForwardIt first, Size n, const T& x); + template + ForwardIt uninitialized_fill_n(ExecutionPolicy&& exec, + ForwardIt first, Size n, const T& x); + + template + void destroy_at(T* location); + template + void destroy(ForwardIt first, ForwardIt last); + template + void destroy(ExecutionPolicy&& exec, + ForwardIt first, ForwardIt last); + template + ForwardIt destroy_n(ForwardIt first, Size n); + template + ForwardIt destroy_n(ExecutionPolicy&& exec, + ForwardIt first, Size n); + + // class template unique_­ptr + template struct default_delete; + template struct default_delete; + template> class unique_ptr; + template class unique_ptr; + + template + unique_ptr make_unique(Args&&... args) noexcept(false); + template + unique_ptr make_unique(size_t n) noexcept(false); + template + /* unspecified */ make_unique(Args&&...) = delete; // T is U[N] + + template + unique_ptr make_unique_for_overwrite() noexcept(false); + template + unique_ptr make_unique_for_overwrite(size_t n) noexcept(false); + template + /* unspecified */ make_unique_for_overwrite(Args&&...) = delete; // T is U[N] + + template + void swap(unique_ptr& x, unique_ptr& y) noexcept; + + template + bool operator==(const unique_ptr& x, const unique_ptr& y); + template + bool operator!=(const unique_ptr& x, const unique_ptr& y); + template + bool operator<(const unique_ptr& x, const unique_ptr& y); + template + bool operator>(const unique_ptr& x, const unique_ptr& y); + template + bool operator<=(const unique_ptr& x, const unique_ptr& y); + template + bool operator>=(const unique_ptr& x, const unique_ptr& y); + + template + bool operator==(const unique_ptr& x, nullptr_t) noexcept; + template + bool operator==(nullptr_t, const unique_ptr& y) noexcept; + template + bool operator!=(const unique_ptr& x, nullptr_t) noexcept; + template + bool operator!=(nullptr_t, const unique_ptr& y) noexcept; + template + bool operator<(const unique_ptr& x, nullptr_t); + template + bool operator<(nullptr_t, const unique_ptr& y); + template + bool operator>(const unique_ptr& x, nullptr_t); + template + bool operator>(nullptr_t, const unique_ptr& y); + template + bool operator<=(const unique_ptr& x, nullptr_t); + template + bool operator<=(nullptr_t, const unique_ptr& y); + template + bool operator>=(const unique_ptr& x, nullptr_t); + template + bool operator>=(nullptr_t, const unique_ptr& y); + + template + basic_ostream& operator<<(basic_ostream& os, const unique_ptr& p); + + // class bad_weak_ptr + class bad_weak_ptr; + + // class template shared_ptr + template class shared_ptr; + + // shared_ptr creation + template + shared_ptr make_shared(Args&&... args) noexcept(false); + template + shared_ptr allocate_shared(const A& a, Args&&... args) noexcept(false); + + template + shared_ptr make_shared(size_t N) noexcept(false); + template + shared_ptr allocate_shared(const A& a, size_t N) noexcept(false); + + template + shared_ptr make_shared() noexcept(false); + template + shared_ptr allocate_shared(const A& a) noexcept(false); + + template + shared_ptr make_shared(size_t N, const remove_extent_t& u) noexcept(false); + template + shared_ptr allocate_shared(const A& a, size_t N, + const remove_extent_t& u) noexcept(false); + + template + shared_ptr make_shared(const remove_extent_t& u) noexcept(false); + template + shared_ptr allocate_shared(const A& a, const remove_extent_t& u) noexcept(false); + + template + shared_ptr make_shared_for_overwrite() noexcept(false); + template + shared_ptr allocate_shared_for_overwrite(const A& a) noexcept(false); + + template + shared_ptr make_shared_for_overwrite(size_t N) noexcept(false); + template + shared_ptr allocate_shared_for_overwrite(const A& a, size_t N) noexcept(false); + + // shared_ptr comparisons + template + bool operator==(const shared_ptr& a, const shared_ptr& b) noexcept; + template + bool operator!=(const shared_ptr& a, const shared_ptr& b) noexcept; + template + bool operator<(const shared_ptr& a, const shared_ptr& b) noexcept; + template + bool operator>(const shared_ptr& a, const shared_ptr& b) noexcept; + template + bool operator<=(const shared_ptr& a, const shared_ptr& b) noexcept; + template + bool operator>=(const shared_ptr& a, const shared_ptr& b) noexcept; + + template + bool operator==(const shared_ptr& x, nullptr_t) noexcept; + template + bool operator==(nullptr_t, const shared_ptr& y) noexcept; + template + bool operator!=(const shared_ptr& x, nullptr_t) noexcept; + template + bool operator!=(nullptr_t, const shared_ptr& y) noexcept; + template + bool operator<(const shared_ptr& x, nullptr_t) noexcept; + template + bool operator<(nullptr_t, const shared_ptr& y) noexcept; + template + bool operator>(const shared_ptr& x, nullptr_t) noexcept; + template + bool operator>(nullptr_t, const shared_ptr& y) noexcept; + template + bool operator<=(const shared_ptr& x, nullptr_t) noexcept; + template + bool operator<=(nullptr_t, const shared_ptr& y) noexcept; + template + bool operator>=(const shared_ptr& x, nullptr_t) noexcept; + template + bool operator>=(nullptr_t, const shared_ptr& y) noexcept; + + // shared_ptr specialized algorithms + template + void swap(shared_ptr& a, shared_ptr& b) noexcept; + + // shared_ptr casts + template + shared_ptr static_pointer_cast(const shared_ptr& r) noexcept; + template + shared_ptr static_pointer_cast(shared_ptr&& r) noexcept; + template + shared_ptr dynamic_pointer_cast(const shared_ptr& r) noexcept; + template + shared_ptr dynamic_pointer_cast(shared_ptr&& r) noexcept; + template + shared_ptr const_pointer_cast(const shared_ptr& r) noexcept; + template + shared_ptr const_pointer_cast(shared_ptr&& r) noexcept; + template + shared_ptr reinterpret_pointer_cast(const shared_ptr& r) noexcept; + template + shared_ptr reinterpret_pointer_cast(shared_ptr&& r) noexcept; + + // shared_ptr get_deleter + template + D* get_deleter(const shared_ptr& p) noexcept; + + // shared_ptr I/O + template + basic_ostream& operator<<(basic_ostream& os, const shared_ptr& p); + + // class template weak_ptr + template class weak_ptr; + + // weak_ptr specialized algorithms + template void swap(weak_ptr& a, weak_ptr& b) noexcept; + + // class template owner_less + template struct owner_less; + + // class template enable_shared_from_this + template class enable_shared_from_this; + + // hash support + template struct hash; + template struct hash>; + template struct hash>; + + // atomic smart pointers + template struct atomic; + template struct atomic>; + template struct atomic>; + + // shared_ptr atomic access + template + bool atomic_is_lock_free(const shared_ptr* p); + template + shared_ptr atomic_load(const shared_ptr* p); + template + shared_ptr atomic_load_explicit(const shared_ptr* p, memory_order mo); + template + void atomic_store(shared_ptr* p, shared_ptr r); + template + void atomic_store_explicit(shared_ptr* p, shared_ptr r, memory_order mo); + template + shared_ptr atomic_exchange(shared_ptr* p, shared_ptr r); + template + shared_ptr atomic_exchange_explicit(shared_ptr* p, shared_ptr r, + memory_order mo); + template + bool atomic_compare_exchange_weak(shared_ptr* p, shared_ptr* v, shared_ptr w); + template + bool atomic_compare_exchange_strong(shared_ptr* p, shared_ptr* v, shared_ptr w); + template + bool atomic_compare_exchange_weak_explicit(shared_ptr* p, shared_ptr* v, + shared_ptr w, memory_order success, + memory_order failure); + template + bool atomic_compare_exchange_strong_explicit(shared_ptr* p, shared_ptr* v, + shared_ptr w, memory_order success, + memory_order failure); +} + +namespace std { + template struct default_delete { + constexpr default_delete() noexcept = default; + template default_delete(const default_delete&) noexcept; + void operator()(T*) const; + }; + + template struct default_delete { + constexpr default_delete() noexcept = default; + template default_delete(const default_delete&) noexcept; + template void operator()(U* ptr) const; + }; +} + +namespace std { + template> class unique_ptr { + public: + using pointer = T*; + using element_type = T; + using deleter_type = D; + + // constructors + constexpr unique_ptr() noexcept; + explicit unique_ptr(T* p) noexcept; + unique_ptr(unique_ptr&& u) noexcept; + constexpr unique_ptr(nullptr_t) noexcept; + template + unique_ptr(unique_ptr&& u) noexcept; + + // destructor + ~unique_ptr() noexcept; + + // assignment + unique_ptr& operator=(unique_ptr&& u) noexcept; + template + unique_ptr& operator=(unique_ptr&& u) noexcept; + unique_ptr& operator=(nullptr_t) noexcept; + + // observers + T* operator*() const; + T* operator->() const noexcept; + T* get() const noexcept; + deleter_type& get_deleter() noexcept; + const deleter_type& get_deleter() const noexcept; + explicit operator bool() const noexcept; + + // modifiers + T* release() noexcept; + void reset(T* p = pointer()) noexcept; + void swap(unique_ptr& u) noexcept; + + // disable copy from lvalue + unique_ptr(const unique_ptr&) = delete; + unique_ptr& operator=(const unique_ptr&) = delete; + }; + + template class unique_ptr { + public: + using pointer = T*; + using element_type = T; + using deleter_type = D; + + // constructors + constexpr unique_ptr() noexcept; + template explicit unique_ptr(U p) noexcept; + unique_ptr(unique_ptr&& u) noexcept; + template + unique_ptr(unique_ptr&& u) noexcept; + constexpr unique_ptr(nullptr_t) noexcept; + + // destructor + ~unique_ptr(); + + // assignment + unique_ptr& operator=(unique_ptr&& u) noexcept; + template + unique_ptr& operator=(unique_ptr&& u) noexcept; + unique_ptr& operator=(nullptr_t) noexcept; + + // observers + T& operator[](size_t i) const; + T* get() const noexcept; + deleter_type& get_deleter() noexcept; + const deleter_type& get_deleter() const noexcept; + explicit operator bool() const noexcept; + + // modifiers + T* release() noexcept; + template void reset(U p) noexcept; + void reset(nullptr_t = nullptr) noexcept; + void swap(unique_ptr& u) noexcept; + + // disable copy from lvalue + unique_ptr(const unique_ptr&) = delete; + unique_ptr& operator=(const unique_ptr&) = delete; + }; +} + +namespace std { + class bad_weak_ptr : public exception { + public: + bad_weak_ptr() noexcept; + }; +} + +namespace std { + template class shared_ptr { + public: + using element_type = remove_extent_t; + using weak_type = weak_ptr; + + // constructors + constexpr shared_ptr() noexcept; + constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { } + template + explicit shared_ptr(Y* p); + template + shared_ptr(Y* p, D d); + template + shared_ptr(Y* p, D d, A a); + template + shared_ptr(nullptr_t p, D d); + template + shared_ptr(nullptr_t p, D d, A a); + template + shared_ptr(const shared_ptr& r, element_type* p) noexcept; + template + shared_ptr(shared_ptr&& r, element_type* p) noexcept; + shared_ptr(const shared_ptr& r) noexcept; + template + shared_ptr(const shared_ptr& r) noexcept; + shared_ptr(shared_ptr&& r) noexcept; + template + shared_ptr(shared_ptr&& r) noexcept; + template + explicit shared_ptr(const weak_ptr& r); + template + shared_ptr(unique_ptr&& r); + + // destructor + ~shared_ptr(); + + // assignment + shared_ptr& operator=(const shared_ptr& r) noexcept; + template + shared_ptr& operator=(const shared_ptr& r) noexcept; + shared_ptr& operator=(shared_ptr&& r) noexcept; + template + shared_ptr& operator=(shared_ptr&& r) noexcept; + template + shared_ptr& operator=(unique_ptr&& r); + + // modifiers + void swap(shared_ptr& r) noexcept; + void reset() noexcept; + template + void reset(Y* p); + template + void reset(Y* p, D d); + template + void reset(Y* p, D d, A a); + + // observers + element_type* get() const noexcept; + T& operator*() const noexcept; + T* operator->() const noexcept; + element_type& operator[](ptrdiff_t i) const; + long use_count() const noexcept; + explicit operator bool() const noexcept; + template + bool owner_before(const shared_ptr& b) const noexcept; + template + bool owner_before(const weak_ptr& b) const noexcept; + }; + + template + shared_ptr(weak_ptr) -> shared_ptr; + template + shared_ptr(unique_ptr) -> shared_ptr; +} + +namespace std { + template class weak_ptr { + public: + using element_type = remove_extent_t; + + // constructors + constexpr weak_ptr() noexcept; + template + weak_ptr(const shared_ptr& r) noexcept; + weak_ptr(const weak_ptr& r) noexcept; + template + weak_ptr(const weak_ptr& r) noexcept; + weak_ptr(weak_ptr&& r) noexcept; + template + weak_ptr(weak_ptr&& r) noexcept; + + // destructor + ~weak_ptr(); + + // assignment + weak_ptr& operator=(const weak_ptr& r) noexcept; + template + weak_ptr& operator=(const weak_ptr& r) noexcept; + template + weak_ptr& operator=(const shared_ptr& r) noexcept; + weak_ptr& operator=(weak_ptr&& r) noexcept; + template + weak_ptr& operator=(weak_ptr&& r) noexcept; + + // modifiers + void swap(weak_ptr& r) noexcept; + void reset() noexcept; + + // observers + long use_count() const noexcept; + bool expired() const noexcept; + shared_ptr lock() const noexcept; + template + bool owner_before(const shared_ptr& b) const noexcept; + template + bool owner_before(const weak_ptr& b) const noexcept; + }; + + template + weak_ptr(shared_ptr) -> weak_ptr; +} + +namespace std { + template class enable_shared_from_this { + protected: + constexpr enable_shared_from_this() noexcept; + enable_shared_from_this(const enable_shared_from_this&) noexcept; + enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept; + ~enable_shared_from_this(); + + public: + shared_ptr shared_from_this(); + shared_ptr shared_from_this() const; + weak_ptr weak_from_this() noexcept; + weak_ptr weak_from_this() const noexcept; + + private: + mutable weak_ptr weak_this; // exposition only + }; +} \ No newline at end of file diff --git a/stdc++headers/type_traits b/stdc++headers/type_traits new file mode 100644 index 00000000000..51e4cfb726c --- /dev/null +++ b/stdc++headers/type_traits @@ -0,0 +1,39 @@ +#pragma once + +namespace std { + +template +struct remove_reference { + using type = T; +}; + +template +struct remove_reference { + using type = T; +}; + +template +struct remove_reference { + using type = T; +}; + +template +using remove_reference_t = typename remove_reference::type; + +} + +namespace std { + +template +struct remove_extent { typedef T type; }; + +template +struct remove_extent { typedef T type; }; + +template +struct remove_extent { typedef T type; }; + +template< class T > +using remove_extent_t = typename remove_extent::type; + +} \ No newline at end of file diff --git a/stdc++headers/utility b/stdc++headers/utility new file mode 100644 index 00000000000..d77afb976ac --- /dev/null +++ b/stdc++headers/utility @@ -0,0 +1,11 @@ +#pragma once +#include + +namespace std { + +template +constexpr remove_reference_t&& move(T&& _Arg) noexcept { + return static_cast&&>(_Arg); +} + +} \ No newline at end of file