diff --git a/.github/workflows/corpus.yml b/.github/workflows/corpus.yml new file mode 100644 index 00000000000..0c1aeda94de --- /dev/null +++ b/.github/workflows/corpus.yml @@ -0,0 +1,58 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: corpus + +on: + schedule: + - cron: '0 0 * * 0' + workflow_dispatch: + +permissions: + contents: read + +jobs: + corpus: + runs-on: ubuntu-22.04 + if: ${{ github.repository_owner == 'cppcheck-opensource' }} + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ github.workflow }}-${{ runner.os }} + + - name: Install missing software on ubuntu + run: | + sudo apt-get update + sudo apt-get install -y fdupes + + - name: build testrunner + run: | + store_dir=$(pwd)/store + mkdir $store_dir + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + make -j$(nproc) CXXOPTS="-Werror" CPPOPTS="-DSTORE_INPUT_DIR=\"\\\"$store_dir\\\"\"" testrunner + + - name: run testrunner + run: | + ./testrunner -q + + - name: de-duplicate files + run: | + set -x + ls -l ./store | wc -l + echo "removing duplicates" + fdupes -qdN ./store > /dev/null + ls -l ./store | wc -l + # print largest size + ls -l ./store | cut -d' ' -f5 | sort -u -n -r | head -n1 + + - uses: actions/upload-artifact@v4 + if: success() + with: + name: corpus + path: ./store diff --git a/AUTHORS b/AUTHORS index 40391c83d7d..04cff0891a9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -141,6 +141,7 @@ Frank Winklmeier Frank Zingsheim Frederik Schwarzer fu7mu4 +Gaƫl Bonithon Galimov Albert Garrett Bodily Gary Leutheuser diff --git a/Makefile b/Makefile index bb006c36fea..5a348e27001 100644 --- a/Makefile +++ b/Makefile @@ -228,6 +228,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \ $(libcppdir)/checknullpointer.o \ $(libcppdir)/checkother.o \ $(libcppdir)/checkpostfixoperator.o \ + $(libcppdir)/checks.o \ $(libcppdir)/checksizeof.o \ $(libcppdir)/checkstl.o \ $(libcppdir)/checkstring.o \ @@ -560,6 +561,9 @@ $(libcppdir)/checkother.o: lib/checkother.cpp lib/addoninfo.h lib/astutils.h lib $(libcppdir)/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checkpostfixoperator.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/regex.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkpostfixoperator.cpp +$(libcppdir)/checks.o: lib/checks.cpp lib/check.h lib/check64bit.h lib/checkassert.h lib/checkautovariables.h lib/checkbool.h lib/checkbufferoverrun.h lib/checkclass.h lib/checkcondition.h lib/checkexceptionsafety.h lib/checkfunctions.h lib/checkinternal.h lib/checkio.h lib/checkleakautovar.h lib/checkmemoryleak.h lib/checknullpointer.h lib/checkother.h lib/checkpostfixoperator.h lib/checks.h lib/checksizeof.h lib/checkstl.h lib/checkstring.h lib/checktype.h lib/checkuninitvar.h lib/checkunusedvar.h lib/checkvaarg.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/standards.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checks.cpp + $(libcppdir)/checksizeof.o: lib/checksizeof.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checksizeof.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/regex.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checksizeof.cpp @@ -590,7 +594,7 @@ $(libcppdir)/clangimport.o: lib/clangimport.cpp lib/clangimport.h lib/config.h l $(libcppdir)/color.o: lib/color.cpp lib/color.h lib/config.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/color.cpp -$(libcppdir)/cppcheck.o: lib/cppcheck.cpp externals/picojson/picojson.h externals/simplecpp/simplecpp.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/checkunusedfunctions.h lib/clangimport.h lib/color.h lib/config.h lib/cppcheck.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/json.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/regex.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/version.h lib/vfvalue.h +$(libcppdir)/cppcheck.o: lib/cppcheck.cpp externals/picojson/picojson.h externals/simplecpp/simplecpp.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/checks.h lib/checkunusedfunctions.h lib/clangimport.h lib/color.h lib/config.h lib/cppcheck.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/json.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/regex.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/version.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/cppcheck.cpp $(libcppdir)/ctu.o: lib/ctu.cpp externals/tinyxml2/tinyxml2.h lib/astutils.h lib/check.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h @@ -695,7 +699,7 @@ $(libcppdir)/vfvalue.o: lib/vfvalue.cpp lib/config.h lib/errortypes.h lib/mathli frontend/frontend.o: frontend/frontend.cpp frontend/frontend.h lib/addoninfo.h lib/checkers.h lib/config.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/regex.h lib/settings.h lib/standards.h lib/utils.h $(CXX) ${INCLUDE_FOR_FE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ frontend/frontend.cpp -cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlinelogger.h cli/cmdlineparser.h cli/filelister.h externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/regex.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h lib/xml.h +cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlinelogger.h cli/cmdlineparser.h cli/filelister.h externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/check.h lib/checkers.h lib/checks.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/regex.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h lib/xml.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cmdlineparser.cpp cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlinelogger.h cli/cmdlineparser.h cli/cppcheckexecutor.h cli/executor.h cli/processexecutor.h cli/sehwrapper.h cli/signalhandler.h cli/singleexecutor.h cli/threadexecutor.h externals/picojson/picojson.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/checkersreport.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/json.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/regex.h lib/sarifreport.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h @@ -764,7 +768,7 @@ test/testbufferoverrun.o: test/testbufferoverrun.cpp lib/addoninfo.h lib/check.h test/testcharvar.o: test/testcharvar.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checkother.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/regex.h lib/settings.h lib/standards.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h $(CXX) ${INCLUDE_FOR_TEST} ${CFLAGS_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testcharvar.cpp -test/testcheck.o: test/testcheck.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/regex.h lib/settings.h lib/standards.h lib/utils.h test/fixture.h +test/testcheck.o: test/testcheck.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checks.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/regex.h lib/settings.h lib/standards.h lib/utils.h test/fixture.h $(CXX) ${INCLUDE_FOR_TEST} ${CFLAGS_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testcheck.cpp test/testcheckersreport.o: test/testcheckersreport.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checkersreport.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/regex.h lib/settings.h lib/standards.h lib/utils.h test/fixture.h @@ -812,7 +816,7 @@ test/testfrontend.o: test/testfrontend.cpp lib/addoninfo.h lib/check.h lib/check test/testfunctions.o: test/testfunctions.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checkfunctions.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/regex.h lib/settings.h lib/standards.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h $(CXX) ${INCLUDE_FOR_TEST} ${CFLAGS_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testfunctions.cpp -test/testgarbage.o: test/testgarbage.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/regex.h lib/settings.h lib/smallvector.h lib/standards.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h +test/testgarbage.o: test/testgarbage.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checks.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/regex.h lib/settings.h lib/smallvector.h lib/standards.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h $(CXX) ${INCLUDE_FOR_TEST} ${CFLAGS_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testgarbage.cpp test/testimportproject.o: test/testimportproject.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/regex.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h lib/xml.h test/fixture.h test/redirect.h diff --git a/cfg/gtk.cfg b/cfg/gtk.cfg index c4ab8972155..b561aad91b2 100644 --- a/cfg/gtk.cfg +++ b/cfg/gtk.cfg @@ -10,10 +10,24 @@ + + + + + + + + + + + + + + @@ -234,6 +248,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + g_thread_new g_thread_try_new @@ -22972,6 +23066,10 @@ + + + + diff --git a/cfg/std.cfg b/cfg/std.cfg index e282dd99995..ec5fbb72548 100644 --- a/cfg/std.cfg +++ b/cfg/std.cfg @@ -4411,7 +4411,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + @@ -4444,7 +4444,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + @@ -4861,7 +4861,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun false - + @@ -5044,7 +5044,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun false - + @@ -5433,7 +5433,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun false - + diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 43361c6e5bc..27cfe08d871 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -20,6 +20,7 @@ #include "addoninfo.h" #include "check.h" +#include "checks.h" #include "checkers.h" #include "color.h" #include "config.h" @@ -359,9 +360,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a if (std::strcmp(argv[i], "--doc") == 0) { std::ostringstream doc; // Get documentation.. - for (const Check * it : Check::instances()) { - const std::string& name(it->name()); - const std::string info(it->classInfo()); + for (const Check * const c : CheckInstances::get()) { + const std::string& name(c->name()); + const std::string info(c->classInfo()); if (!name.empty() && !info.empty()) doc << "## " << name << " ##\n" << info << "\n"; diff --git a/cli/processexecutor.cpp b/cli/processexecutor.cpp index a410f58d5ea..e77a75ba39e 100644 --- a/cli/processexecutor.cpp +++ b/cli/processexecutor.cpp @@ -515,7 +515,7 @@ void ProcessExecutor::reportInternalChildErr(const std::string &childname, const "cppcheckError", Certainty::normal); - if (!mSuppressions.nomsg.isSuppressed(errmsg, {})) + if (hasToLog(errmsg)) mErrorLogger.reportErr(errmsg); } diff --git a/lib/check.cpp b/lib/check.cpp index 812c8b02fcd..4033378e927 100644 --- a/lib/check.cpp +++ b/lib/check.cpp @@ -26,34 +26,15 @@ #include "tokenize.h" #include "vfvalue.h" -#include #include #include -#include #include //--------------------------------------------------------------------------- -Check::Check(const std::string &aname) - : mName(aname) -{ - { - const auto it = std::find_if(instances().begin(), instances().end(), [&](const Check *i) { - return i->name() == aname; - }); - if (it != instances().end()) - throw std::runtime_error("'" + aname + "' instance already exists"); - } - - // make sure the instances are sorted - const auto it = std::find_if(instances().begin(), instances().end(), [&](const Check* i) { - return i->name() > aname; - }); - if (it == instances().end()) - instances().push_back(this); - else - instances().insert(it, this); -} +Check::Check(std::string aname) + : mName(std::move(aname)) +{} void Check::writeToErrorList(const ErrorMessage &errmsg) { @@ -88,19 +69,6 @@ bool Check::wrongData(const Token *tok, const char *str) return true; } -std::list &Check::instances() -{ -#ifdef __SVR4 - // Under Solaris, destructors are called in wrong order which causes a segmentation fault. - // This fix ensures pointer remains valid and reachable until program terminates. - static std::list *_instances= new std::list; - return *_instances; -#else - static std::list _instances; - return _instances; -#endif -} - std::string Check::getMessageId(const ValueFlow::Value &value, const char id[]) { if (value.condition != nullptr) diff --git a/lib/check.h b/lib/check.h index 4e3c5b9a4e3..9a50e64baa1 100644 --- a/lib/check.h +++ b/lib/check.h @@ -59,25 +59,22 @@ class Tokenizer; class CPPCHECKLIB Check { public: /** This constructor is used when registering the CheckClass */ - explicit Check(const std::string &aname); + explicit Check(std::string aname); protected: /** This constructor is used when running checks. */ Check(std::string aname, const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : mTokenizer(tokenizer), mSettings(settings), mErrorLogger(errorLogger), mName(std::move(aname)) {} +private: + static std::list &instances_internal(); + public: - virtual ~Check() { - if (!mTokenizer) - instances().remove(this); - } + virtual ~Check() = default; Check(const Check &) = delete; Check& operator=(const Check &) = delete; - /** List of registered check classes. This is used by Cppcheck to run checks and generate documentation */ - static std::list &instances(); - /** run checks, the token list is not simplified */ virtual void runChecks(const Tokenizer &, ErrorLogger *) = 0; diff --git a/lib/check64bit.cpp b/lib/check64bit.cpp index d203eb5f279..b33ab5bdbcb 100644 --- a/lib/check64bit.cpp +++ b/lib/check64bit.cpp @@ -36,11 +36,6 @@ // CWE ids used static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior -// Register this check class (by creating a static instance of it) -namespace { - Check64BitPortability instance; -} - static bool is32BitIntegerReturn(const Function* func, const Settings* settings) { if (settings->platform.sizeof_pointer != 8) diff --git a/lib/checkassert.cpp b/lib/checkassert.cpp index 3ab55967ede..ce2d7c62169 100644 --- a/lib/checkassert.cpp +++ b/lib/checkassert.cpp @@ -39,11 +39,6 @@ // CWE ids used static const CWE CWE398(398U); // Indicator of Poor Code Quality -// Register this check class (by creating a static instance of it) -namespace { - CheckAssert instance; -} - void CheckAssert::assertWithSideEffects() { if (!mSettings->severity.isEnabled(Severity::warning)) diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp index e778f3d5959..fdc5deceefa 100644 --- a/lib/checkautovariables.cpp +++ b/lib/checkautovariables.cpp @@ -39,12 +39,6 @@ //--------------------------------------------------------------------------- - -// Register this check class into cppcheck by creating a static instance of it.. -namespace { - CheckAutoVariables instance; -} - static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE562(562U); // Return of Stack Variable Address static const CWE CWE590(590U); // Free of Memory not on the Heap diff --git a/lib/checkbool.cpp b/lib/checkbool.cpp index 93e501416aa..c9a66876317 100644 --- a/lib/checkbool.cpp +++ b/lib/checkbool.cpp @@ -32,11 +32,6 @@ #include //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckBool instance; -} - static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE571(571U); // Expression is Always True static const CWE CWE587(587U); // Assignment of a Fixed Address to a Pointer diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 642e40bce76..3ba41882316 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -50,13 +50,6 @@ //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckBufferOverrun instance; -} - -//--------------------------------------------------------------------------- - // CWE ids used: static const CWE CWE131(131U); // Incorrect Calculation of Buffer Size static const CWE CWE170(170U); // Improper Null Termination diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 9fc9bdc7863..3313fb636ed 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -43,11 +43,6 @@ //--------------------------------------------------------------------------- -// Register CheckClass.. -namespace { - CheckClass instance; -} - static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE404(404U); // Improper Resource Shutdown or Release static const CWE CWE665(665U); // Improper Initialization @@ -351,6 +346,8 @@ void CheckClass::constructors() // Variables with default initializers bool hasAnyDefaultInit = false; + bool hasAnySelfInit = false; + const bool cpp14OrLater = mSettings->standards.cpp >= Standards::CPP14; for (Usage& usage : usageList) { const Variable& var = *usage.var; @@ -358,9 +355,11 @@ void CheckClass::constructors() if (var.hasDefault()) { usage.init = true; hasAnyDefaultInit = true; + } else if (cpp14OrLater && !hasAnySelfInit && isInitialized(usage, FunctionType::eConstructor)) { + hasAnySelfInit = true; } } - if (!hasAnyDefaultInit) + if (!hasAnyDefaultInit && !hasAnySelfInit) continue; handleUnionMembers(usageList); @@ -371,8 +370,11 @@ void CheckClass::constructors() continue; const Variable& var = *usage.var; + if (var.typeScope() && var.typeScope()->numConstructors > 0) + continue; + if (diagVars.count(&var) == 0) - uninitVarError(scope->bodyStart, false, FunctionType::eConstructor, var.scope()->className, var.name(), false, false, true); + uninitVarError(var.nameToken(), false, FunctionType::eConstructor, var.scope()->className, var.name(), false, false, true); } } } diff --git a/lib/checkcondition.cpp b/lib/checkcondition.cpp index d0913963cca..cec3293ad27 100644 --- a/lib/checkcondition.cpp +++ b/lib/checkcondition.cpp @@ -52,11 +52,6 @@ static const CWE CWE571(571U); // Expression is Always True //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckCondition instance; -} - bool CheckCondition::diag(const Token* tok, bool insert) { if (!tok) diff --git a/lib/checkexceptionsafety.cpp b/lib/checkexceptionsafety.cpp index 34303cc0f4a..6da5281e80a 100644 --- a/lib/checkexceptionsafety.cpp +++ b/lib/checkexceptionsafety.cpp @@ -34,11 +34,6 @@ //--------------------------------------------------------------------------- -// Register CheckExceptionSafety.. -namespace { - CheckExceptionSafety instance; -} - static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE703(703U); // Improper Check or Handling of Exceptional Conditions static const CWE CWE480(480U); // Use of Incorrect Operator diff --git a/lib/checkfunctions.cpp b/lib/checkfunctions.cpp index 7611ea7eee4..733b868814e 100644 --- a/lib/checkfunctions.cpp +++ b/lib/checkfunctions.cpp @@ -44,12 +44,6 @@ //--------------------------------------------------------------------------- - -// Register this check class (by creating a static instance of it) -namespace { - CheckFunctions instance; -} - static const CWE CWE252(252U); // Unchecked Return Value static const CWE CWE477(477U); // Use of Obsolete Functions static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior diff --git a/lib/checkinternal.cpp b/lib/checkinternal.cpp index f289044b102..ed39b4db96a 100644 --- a/lib/checkinternal.cpp +++ b/lib/checkinternal.cpp @@ -31,12 +31,6 @@ #include #include -// Register this check class (by creating a static instance of it). -// Disabled in release builds -namespace { - CheckInternal instance; -} - void CheckInternal::checkTokenMatchPatterns() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); diff --git a/lib/checkinternal.h b/lib/checkinternal.h index 1fc97bcb247..d62d9698b8d 100644 --- a/lib/checkinternal.h +++ b/lib/checkinternal.h @@ -22,6 +22,8 @@ #define checkinternalH //--------------------------------------------------------------------------- +#ifdef CHECK_INTERNAL + #include "check.h" #include "config.h" @@ -93,4 +95,7 @@ class CPPCHECKLIB CheckInternal : public Check { }; /// @} //--------------------------------------------------------------------------- + +#endif // CHECK_INTERNAL + #endif // checkinternalH diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 8b3c835caf3..4161b9b224d 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -45,11 +45,6 @@ //--------------------------------------------------------------------------- -// Register CheckIO.. -namespace { - CheckIO instance; -} - // CVE ID used: static const CWE CWE119(119U); // Improper Restriction of Operations within the Bounds of a Memory Buffer static const CWE CWE398(398U); // Indicator of Poor Code Quality @@ -245,6 +240,28 @@ void CheckIO::checkFileUsage() } else if (tok->str() == "fclose") { fileTok = tok->tokAt(2); operation = Filepointer::Operation::CLOSE; + + // #1473 Check if fclose is in a while loop condition + if (fileTok && fileTok->isVariable()) { + const Token* loopTok = tok->astTop()->previous(); + + if (loopTok && loopTok->str() == "while") { + const Token* bodyEnd = nullptr; + const Token* bodyStart = nullptr; + + if (Token::simpleMatch(loopTok->previous(), "}") && loopTok->previous()->scope()->type == ScopeType::eDo) { // Handle do-while loops + bodyEnd = loopTok->previous(); + bodyStart = bodyEnd->link(); + } else { + bodyStart = loopTok->linkAt(1)->next(); + bodyEnd = bodyStart->link(); + } + + // Do not trigger a warning if the loop always exits or if the file is opened again in the loop. + if (!isReturnScope(bodyEnd, mSettings->library) && Token::findmatch(bodyStart, "%var% =", bodyEnd, fileTok->varId()) == nullptr) + fcloseInLoopConditionError(tok, fileTok->str()); + } + } } else if (whitelist.find(tok->str()) != whitelist.end()) { fileTok = tok->tokAt(2); if ((tok->str() == "ungetc" || tok->str() == "ungetwc") && fileTok) @@ -392,6 +409,15 @@ void CheckIO::useClosedFileError(const Token *tok) "useClosedFile", "Used file that is not opened.", CWE910, Certainty::normal); } +void CheckIO::fcloseInLoopConditionError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::warning, + "fcloseInLoopCondition", + "fclose() used as loop condition may skip loop body or double-close file handle.\n" + "fclose() closes '" + varname + "' each time it is evaluated. On success the loop body might never execute, on failure fclose() might be called again on the already-closed file handle.", + CWE910, Certainty::normal); +} + void CheckIO::seekOnAppendedFileError(const Token *tok) { reportError(tok, Severity::warning, @@ -2043,6 +2069,7 @@ void CheckIO::getErrorMessages(ErrorLogger *errorLogger, const Settings *setting c.readWriteOnlyFileError(nullptr); c.writeReadOnlyFileError(nullptr); c.useClosedFileError(nullptr); + c.fcloseInLoopConditionError(nullptr, "fp"); c.seekOnAppendedFileError(nullptr); c.incompatibleFileOpenError(nullptr, "tmp"); c.invalidScanfError(nullptr); diff --git a/lib/checkio.h b/lib/checkio.h index e37a942770b..b3c86da3577 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -105,6 +105,7 @@ class CPPCHECKLIB CheckIO : public Check { void readWriteOnlyFileError(const Token *tok); void writeReadOnlyFileError(const Token *tok); void useClosedFileError(const Token *tok); + void fcloseInLoopConditionError(const Token *tok, const std::string &varname); void seekOnAppendedFileError(const Token *tok); void incompatibleFileOpenError(const Token *tok, const std::string &filename); void invalidScanfError(const Token *tok); diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 803ea9967a3..1c9a7283161 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -45,11 +45,6 @@ //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckLeakAutoVar instance; -} - static const CWE CWE672(672U); static const CWE CWE415(415U); diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 1a6ef787eb6..b2f97693cd6 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -37,14 +37,6 @@ //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckMemoryLeakInFunction instance1; - CheckMemoryLeakInClass instance2; - CheckMemoryLeakStructMember instance3; - CheckMemoryLeakNoVar instance4; -} - // CWE ID used: static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE401(401U); // Improper Release of Memory Before Removing Last Reference ('Memory Leak') diff --git a/lib/checknullpointer.cpp b/lib/checknullpointer.cpp index beb9d5d77a3..5bd602488b4 100644 --- a/lib/checknullpointer.cpp +++ b/lib/checknullpointer.cpp @@ -47,11 +47,6 @@ static const CWE CWE_NULL_POINTER_DEREFERENCE(476U); static const CWE CWE_INCORRECT_CALCULATION(682U); -// Register this check class (by creating a static instance of it) -namespace { - CheckNullPointer instance; -} - //--------------------------------------------------------------------------- static bool checkNullpointerFunctionCallPlausibility(const Function* func, unsigned int arg) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index fb75d80fca7..b454c4410cf 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -49,11 +49,6 @@ //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckOther instance; -} - static const CWE CWE128(128U); // Wrap-around Error static const CWE CWE131(131U); // Incorrect Calculation of Buffer Size static const CWE CWE197(197U); // Numeric Truncation Error @@ -1941,7 +1936,9 @@ void CheckOther::checkConstPointer() continue; if (deref != NONE) { const Token* gparent = parent->astParent(); - while (Token::simpleMatch(gparent, "[") && parent != gparent->astOperand2() && parent->str() == gparent->str()) + while (Token::simpleMatch(gparent, "[") && parent != gparent->astOperand2()) + gparent = gparent->astParent(); + while (Token::Match(gparent, "[?:]")) gparent = gparent->astParent(); if (deref == MEMBER) { if (!gparent) diff --git a/lib/checkpostfixoperator.cpp b/lib/checkpostfixoperator.cpp index c6c9ccba77b..d771177b2d2 100644 --- a/lib/checkpostfixoperator.cpp +++ b/lib/checkpostfixoperator.cpp @@ -34,12 +34,6 @@ //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckPostfixOperator instance; -} - - // CWE ids used static const CWE CWE398(398U); // Indicator of Poor Code Quality diff --git a/lib/checks.cpp b/lib/checks.cpp new file mode 100644 index 00000000000..169832c2745 --- /dev/null +++ b/lib/checks.cpp @@ -0,0 +1,123 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2026 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "checks.h" + +#include "check64bit.h" +#include "checkassert.h" +#include "checkautovariables.h" +#include "checkbool.h" +#include "checkbufferoverrun.h" +#include "checkclass.h" +#include "checkcondition.h" +#include "checkexceptionsafety.h" +#include "checkfunctions.h" +#include "checkinternal.h" +#include "checkio.h" +#include "checkleakautovar.h" +#include "checkmemoryleak.h" +#include "checknullpointer.h" +#include "checkother.h" +#include "checkpostfixoperator.h" +#include "checksizeof.h" +#include "checkstl.h" +#include "checkstring.h" +#include "checktype.h" +#include "checkuninitvar.h" +#include "checkunusedvar.h" +#include "checkvaarg.h" + +class CheckInstancesImpl +{ +private: +/* *INDENT-OFF* */ +#define UPI(c) std::unique_ptr m##c{new c} +/* *INDENT-ON* */ + UPI(Check64BitPortability); + UPI(CheckAssert); + UPI(CheckAutoVariables); + UPI(CheckBool); + UPI(CheckBufferOverrun); + UPI(CheckClass); + UPI(CheckCondition); + UPI(CheckExceptionSafety); + UPI(CheckFunctions); +#ifdef CHECK_INTERNAL + UPI(CheckInternal); +#endif + UPI(CheckIO); + UPI(CheckLeakAutoVar); + UPI(CheckMemoryLeakInFunction); + UPI(CheckMemoryLeakInClass); + UPI(CheckMemoryLeakStructMember); + UPI(CheckMemoryLeakNoVar); + UPI(CheckNullPointer); + UPI(CheckOther); + UPI(CheckPostfixOperator); + UPI(CheckSizeof); + UPI(CheckStl); + UPI(CheckString); + UPI(CheckType); + UPI(CheckUninitVar); + UPI(CheckUnusedVar); + UPI(CheckVaarg); +#undef UPI + +public: + const std::list& get() const + { + static std::list s_checks{ + mCheck64BitPortability.get(), + mCheckAssert.get(), + mCheckAutoVariables.get(), + mCheckBool.get(), + mCheckBufferOverrun.get(), + mCheckClass.get(), + mCheckCondition.get(), + mCheckExceptionSafety.get(), + mCheckFunctions.get(), + #ifdef CHECK_INTERNAL + mCheckInternal.get(), + #endif + mCheckIO.get(), + mCheckLeakAutoVar.get(), + mCheckMemoryLeakInFunction.get(), + mCheckMemoryLeakInClass.get(), + mCheckMemoryLeakStructMember.get(), + mCheckMemoryLeakNoVar.get(), + mCheckNullPointer.get(), + mCheckOther.get(), + mCheckPostfixOperator.get(), + mCheckSizeof.get(), + mCheckStl.get(), + mCheckString.get(), + mCheckType.get(), + mCheckUninitVar.get(), + mCheckUnusedVar.get(), + mCheckVaarg.get() + }; + + return s_checks; + } +}; + +const std::list& CheckInstances::get() +{ + static const CheckInstancesImpl s_impl; + return s_impl.get(); +} diff --git a/lib/checks.h b/lib/checks.h new file mode 100644 index 00000000000..ec4a78c2008 --- /dev/null +++ b/lib/checks.h @@ -0,0 +1,34 @@ +/* -*- C++ -*- + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2026 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef checksH +#define checksH + +#include "config.h" + +#include + +class Check; + +namespace CheckInstances +{ + /** List of registered check classes. This is used by Cppcheck to run checks and generate documentation */ + CPPCHECKLIB const std::list& get(); +}; + +#endif // checksH diff --git a/lib/checksizeof.cpp b/lib/checksizeof.cpp index f7c0d44d399..134cca92b7f 100644 --- a/lib/checksizeof.cpp +++ b/lib/checksizeof.cpp @@ -34,11 +34,6 @@ //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckSizeof instance; -} - // CWE IDs used: static const CWE CWE467(467U); // Use of sizeof() on a Pointer Type static const CWE CWE682(682U); // Incorrect Calculation diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index faa6f25911d..b1543f4af37 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -46,11 +46,6 @@ #include #include -// Register this check class (by creating a static instance of it) -namespace { - CheckStl instance; -} - // CWE IDs used: static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE597(597U); // Use of Wrong Operator in String Comparison diff --git a/lib/checkstring.cpp b/lib/checkstring.cpp index 64b575df72d..580597bb10d 100644 --- a/lib/checkstring.cpp +++ b/lib/checkstring.cpp @@ -36,11 +36,6 @@ //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckString instance; -} - // CWE ids used: static const CWE CWE570(570U); // Expression is Always False static const CWE CWE571(571U); // Expression is Always True diff --git a/lib/checktype.cpp b/lib/checktype.cpp index 3f42bf075ef..eb222fbe6f5 100644 --- a/lib/checktype.cpp +++ b/lib/checktype.cpp @@ -44,11 +44,6 @@ //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckType instance; -} - //--------------------------------------------------------------------------- // Checking for shift by too many bits //--------------------------------------------------------------------------- diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index a7a4d8b1bf3..b88debda6f8 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -50,11 +50,6 @@ // CWE ids used: static const CWE CWE_USE_OF_UNINITIALIZED_VARIABLE(457U); -// Register this check class (by creating a static instance of it) -namespace { - CheckUninitVar instance; -} - //--------------------------------------------------------------------------- // get ast parent, skip possible address-of and casts diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 6dd98de078e..9531237a208 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -40,11 +40,6 @@ #include //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckUnusedVar instance; -} - static const CWE CWE563(563U); // Assignment to Variable without Use ('Unused Variable') static const CWE CWE665(665U); // Improper Initialization diff --git a/lib/checkvaarg.cpp b/lib/checkvaarg.cpp index 8151957a7d7..e6efc8f5ed9 100644 --- a/lib/checkvaarg.cpp +++ b/lib/checkvaarg.cpp @@ -30,14 +30,6 @@ #include #include -//--------------------------------------------------------------------------- - -// Register this check class (by creating a static instance of it) -namespace { - CheckVaarg instance; -} - - //--------------------------------------------------------------------------- // Ensure that correct parameter is passed to va_start() //--------------------------------------------------------------------------- diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 3ef1e7c83e3..f39e64a938f 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -21,6 +21,7 @@ #include "addoninfo.h" #include "analyzerinfo.h" #include "check.h" +#include "checks.h" #include "checkunusedfunctions.h" #include "clangimport.h" #include "color.h" @@ -1338,8 +1339,7 @@ void CppCheck::checkNormalTokens(const Tokenizer &tokenizer, AnalyzerInformation const std::time_t maxTime = mSettings.checksMaxTime > 0 ? std::time(nullptr) + mSettings.checksMaxTime : 0; // call all "runChecks" in all registered Check classes - // cppcheck-suppress shadowFunction - TODO: fix this - for (Check *check : Check::instances()) { + for (Check * const c : CheckInstances::get()) { if (Settings::terminated()) return; @@ -1357,8 +1357,8 @@ void CppCheck::checkNormalTokens(const Tokenizer &tokenizer, AnalyzerInformation return; } - Timer::run(check->name() + "::runChecks", mTimerResults, [&]() { - check->runChecks(tokenizer, &mErrorLogger); + Timer::run(c->name() + "::runChecks", mTimerResults, [&]() { + c->runChecks(tokenizer, &mErrorLogger); }); } } @@ -1388,11 +1388,10 @@ void CppCheck::checkNormalTokens(const Tokenizer &tokenizer, AnalyzerInformation } if (!doUnusedFunctionOnly) { - // cppcheck-suppress shadowFunction - TODO: fix this - for (const Check *check : Check::instances()) { - if (Check::FileInfo * const fi = check->getFileInfo(tokenizer, mSettings, currentConfig)) { + for (const Check * const c : CheckInstances::get()) { + if (Check::FileInfo * const fi = c->getFileInfo(tokenizer, mSettings, currentConfig)) { if (analyzerInformation) - analyzerInformation->setFileInfo(check->name(), fi->toString()); + analyzerInformation->setFileInfo(c->name(), fi->toString()); if (mSettings.useSingleJob()) mFileInfo.push_back(fi); else @@ -1714,8 +1713,8 @@ void CppCheck::getErrorMessages(ErrorLogger &errorlogger) s.addEnabled("all"); // call all "getErrorMessages" in all registered Check classes - for (auto it = Check::instances().cbegin(); it != Check::instances().cend(); ++it) - (*it)->getErrorMessages(&errorlogger, &s); + for (const Check * const c : CheckInstances::get()) + c->getErrorMessages(&errorlogger, &s); CheckUnusedFunctions::getErrorMessages(errorlogger); Preprocessor::getErrorMessages(errorlogger, s); @@ -1832,9 +1831,8 @@ bool CppCheck::analyseWholeProgram() } } - // cppcheck-suppress shadowFunction - TODO: fix this - for (Check *check : Check::instances()) - errors |= check->analyseWholeProgram(ctu, mFileInfo, mSettings, mErrorLogger); // TODO: ctu + for (Check * const c : CheckInstances::get()) + errors |= c->analyseWholeProgram(ctu, mFileInfo, mSettings, mErrorLogger); // TODO: ctu } if (mUnusedFunctionsCheck) @@ -1864,7 +1862,7 @@ unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const st ctuFileInfo.loadFromXml(e); return; } - for (const Check *check : Check::instances()) { + for (const Check *check : CheckInstances::get()) { if (checkattr == check->name()) { if (Check::FileInfo* fi = check->loadFileInfoFromXml(e)) { fi->file0 = filesTxtInfo.sourceFile; @@ -1881,9 +1879,8 @@ unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const st } else { // Analyse the tokens - // cppcheck-suppress shadowFunction - TODO: fix this - for (Check *check : Check::instances()) - check->analyseWholeProgram(ctuFileInfo, fileInfoList, mSettings, mErrorLogger); + for (Check * const c : CheckInstances::get()) + c->analyseWholeProgram(ctuFileInfo, fileInfoList, mSettings, mErrorLogger); } for (Check::FileInfo *fi : fileInfoList) diff --git a/lib/cppcheck.vcxproj b/lib/cppcheck.vcxproj index b0e52a814fe..12bd4898d4a 100644 --- a/lib/cppcheck.vcxproj +++ b/lib/cppcheck.vcxproj @@ -57,6 +57,7 @@ + @@ -130,6 +131,7 @@ + diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index f155a75014a..a345332961e 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -333,8 +333,26 @@ bool TokenList::createTokensFromBuffer(const char* data, size_t size) //--------------------------------------------------------------------------- +#ifdef STORE_INPUT_DIR +#include +#include + +static void storeInput(const char* data, size_t size) +{ + static std::atomic_uint64_t num(0); + { + std::ofstream out(STORE_INPUT_DIR "/" + std::to_string(num++)); + out.write(data, size); + } +} +#endif + bool TokenList::createTokensFromBufferInternal(const char* data, size_t size, const std::string& file0) { +#ifdef STORE_INPUT_DIR + storeInput(data, size); +#endif + simplecpp::OutputList outputList; simplecpp::TokenList tokens({data, size}, mFiles, file0, &outputList); diff --git a/man/checkers/fcloseInLoopCondition.md b/man/checkers/fcloseInLoopCondition.md new file mode 100644 index 00000000000..db7f0a584c6 --- /dev/null +++ b/man/checkers/fcloseInLoopCondition.md @@ -0,0 +1,40 @@ +# fcloseInLoopCondition + +**Message**: fclose() used as loop condition may skip loop body or double-close file handle.
+**Category**: Resource Management
+**Severity**: Warning
+**Language**: C and C++ + +## Description + +Using `fclose()` as a loop condition leads to two unwanted outcomes: + +- On **success**, the condition is false and the loop body never executes. The intent was likely to process the file inside the loop, but it is already closed. +- On **failure**, the condition is true and the loop body executes but `fclose()` is called again on the already-closed file handle on the next iteration, which is undefined behaviour. + +This pattern is almost always a misunderstanding of what `fclose()` returns, or confusion with a function that reads/processes data incrementally (like `fgets` or `fread`). Unlike those functions, `fclose()` is a one-shot teardown operation and has no meaningful retry or loop-until-done semantic. + +## How to fix + +Call `fclose()` outside the loop condition. If you need to check whether the close succeeded, store the return value and test it separately. + +Before: +```c +FILE *fp = fopen("data.txt", "r"); +while (fclose(fp)) { + /* process file */ +} +``` + +After: +```c +FILE *fp = fopen("data.txt", "r"); +/* process file */ +if (fclose(fp) != 0) { + /* handle close error */ +} +``` + +## Related checkers + +- `useClosedFile` - for using a file handle that has already been closed diff --git a/oss-fuzz/Makefile b/oss-fuzz/Makefile index b2a42dba2a7..c93b8694065 100644 --- a/oss-fuzz/Makefile +++ b/oss-fuzz/Makefile @@ -65,6 +65,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \ $(libcppdir)/checknullpointer.o \ $(libcppdir)/checkother.o \ $(libcppdir)/checkpostfixoperator.o \ + $(libcppdir)/checks.o \ $(libcppdir)/checksizeof.o \ $(libcppdir)/checkstl.o \ $(libcppdir)/checkstring.o \ @@ -230,6 +231,9 @@ $(libcppdir)/checkother.o: ../lib/checkother.cpp ../lib/addoninfo.h ../lib/astut $(libcppdir)/checkpostfixoperator.o: ../lib/checkpostfixoperator.cpp ../lib/addoninfo.h ../lib/check.h ../lib/checkers.h ../lib/checkpostfixoperator.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/regex.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkpostfixoperator.cpp +$(libcppdir)/checks.o: ../lib/checks.cpp ../lib/check.h ../lib/check64bit.h ../lib/checkassert.h ../lib/checkautovariables.h ../lib/checkbool.h ../lib/checkbufferoverrun.h ../lib/checkclass.h ../lib/checkcondition.h ../lib/checkexceptionsafety.h ../lib/checkfunctions.h ../lib/checkinternal.h ../lib/checkio.h ../lib/checkleakautovar.h ../lib/checkmemoryleak.h ../lib/checknullpointer.h ../lib/checkother.h ../lib/checkpostfixoperator.h ../lib/checks.h ../lib/checksizeof.h ../lib/checkstl.h ../lib/checkstring.h ../lib/checktype.h ../lib/checkuninitvar.h ../lib/checkunusedvar.h ../lib/checkvaarg.h ../lib/config.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/standards.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checks.cpp + $(libcppdir)/checksizeof.o: ../lib/checksizeof.cpp ../lib/addoninfo.h ../lib/check.h ../lib/checkers.h ../lib/checksizeof.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/regex.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checksizeof.cpp @@ -260,7 +264,7 @@ $(libcppdir)/clangimport.o: ../lib/clangimport.cpp ../lib/clangimport.h ../lib/c $(libcppdir)/color.o: ../lib/color.cpp ../lib/color.h ../lib/config.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/color.cpp -$(libcppdir)/cppcheck.o: ../lib/cppcheck.cpp ../externals/picojson/picojson.h ../externals/simplecpp/simplecpp.h ../lib/addoninfo.h ../lib/analyzerinfo.h ../lib/check.h ../lib/checkers.h ../lib/checkunusedfunctions.h ../lib/clangimport.h ../lib/color.h ../lib/config.h ../lib/cppcheck.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/filesettings.h ../lib/json.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/preprocessor.h ../lib/regex.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/version.h ../lib/vfvalue.h +$(libcppdir)/cppcheck.o: ../lib/cppcheck.cpp ../externals/picojson/picojson.h ../externals/simplecpp/simplecpp.h ../lib/addoninfo.h ../lib/analyzerinfo.h ../lib/check.h ../lib/checkers.h ../lib/checks.h ../lib/checkunusedfunctions.h ../lib/clangimport.h ../lib/color.h ../lib/config.h ../lib/cppcheck.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/filesettings.h ../lib/json.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/preprocessor.h ../lib/regex.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/version.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/cppcheck.cpp $(libcppdir)/ctu.o: ../lib/ctu.cpp ../externals/tinyxml2/tinyxml2.h ../lib/astutils.h ../lib/check.h ../lib/config.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h diff --git a/releasenotes.txt b/releasenotes.txt index ec8c6032da9..5d94da2ebc3 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -8,6 +8,7 @@ New checks: - MISRA C 2012 rule 10.3 now warns on assigning integer literals 0 and 1 to bool in C99 and later while preserving the existing C89 behavior. - funcArgNamesDifferentUnnamed warns on function declarations/definitions where a parameter in either location is unnamed - uninitMemberVarNoCtor warns on user-defined types where some but not all members requiring initialization have in-class initializers. +- fcloseInLoopCondition warns when fclose() is used as a while loop condition, which may skip the loop body or double-close the file handle. C/C++ support: - diff --git a/test/cfg/std.c b/test/cfg/std.c index 928d52bf116..997f8204f73 100644 --- a/test/cfg/std.c +++ b/test/cfg/std.c @@ -3519,6 +3519,13 @@ void invalidFunctionArg_strchr(const char *cs, int c) (void)strchr(cs, 256); } +void constParameterPointer_strchr(char *str) // #14453 +{ + char *sep = strchr(str, ':'); + if (sep) + *sep = '\0'; +} + void invalidFunctionArg_log10(float f, double d, const long double ld) { // cppcheck-suppress invalidFunctionArg diff --git a/test/fixture.h b/test/fixture.h index fe767a3a16a..b927dc5de65 100644 --- a/test/fixture.h +++ b/test/fixture.h @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -135,21 +134,13 @@ class TestFixture : public ErrorLogger { void processOptions(const options& args); - template - static T& getCheck() + static Check& getCheck(Check& check) { - for (Check *check : Check::instances()) { - //cppcheck-suppress useStlAlgorithm - if (T* c = dynamic_cast(check)) - return *c; - } - throw std::runtime_error("instance not found"); + return check; } - template - static void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) + static void runChecks(Check& check, const Tokenizer &tokenizer, ErrorLogger *errorLogger) { - Check& check = getCheck(); check.runChecks(tokenizer, errorLogger); } diff --git a/test/testassert.cpp b/test/testassert.cpp index 412a7cb750b..e14825e46b2 100644 --- a/test/testassert.cpp +++ b/test/testassert.cpp @@ -39,8 +39,8 @@ class TestAssert : public TestFixture { SimpleTokenizer tokenizer(settings, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check.. - runChecks(tokenizer, this); + CheckAssert check; + runChecks(check, tokenizer, this); } void run() override { diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index 008dc645419..2474c2fe6a5 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -47,7 +47,8 @@ class TestAutoVariables : public TestFixture { SimpleTokenizer tokenizer(settings1, *this, options.cpp); ASSERT_LOC(tokenizer.tokenize(code), file, line); - runChecks(tokenizer, this); + CheckAutoVariables check; + runChecks(check, tokenizer, this); } void run() override { diff --git a/test/testbool.cpp b/test/testbool.cpp index 61bc1b36d9e..ccf4d7860c4 100644 --- a/test/testbool.cpp +++ b/test/testbool.cpp @@ -88,8 +88,8 @@ class TestBool : public TestFixture { SimpleTokenizer tokenizer(settings, *this, options.cpp); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check... - runChecks(tokenizer, this); + CheckBool check; + runChecks(check, tokenizer, this); } diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index 8a7c1d311b0..5d545606387 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -56,8 +56,8 @@ class TestBufferOverrun : public TestFixture { SimpleTokenizer tokenizer(settings, *this, cpp); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check for buffer overruns.. - runChecks(tokenizer, this); + CheckBufferOverrun check; + runChecks(check, tokenizer, this); } // TODO: get rid of this @@ -66,8 +66,8 @@ class TestBufferOverrun : public TestFixture { SimpleTokenizer tokenizer(settings0_i, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check for buffer overruns.. - runChecks(tokenizer, this); + CheckBufferOverrun check; + runChecks(check, tokenizer, this); } #define checkP(...) checkP_(__FILE__, __LINE__, __VA_ARGS__) @@ -79,8 +79,8 @@ class TestBufferOverrun : public TestFixture { // Tokenizer.. ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); - // Check for buffer overruns.. - runChecks(tokenizer, this); + CheckBufferOverrun check; + runChecks(check, tokenizer, this); } void run() override { @@ -5169,7 +5169,8 @@ class TestBufferOverrun : public TestFixture { void getErrorMessages() { // Ticket #2292: segmentation fault when using --errorlist - const Check& c = getCheck(); + CheckBufferOverrun check; + const Check& c = getCheck(check); c.getErrorMessages(this, nullptr); // we are not interested in the output - just consume it ignore_errout(); @@ -5362,9 +5363,9 @@ class TestBufferOverrun : public TestFixture { CTU::FileInfo *ctu = CTU::getFileInfo(tokenizer); - // Check code.. std::list fileInfo; - Check& c = getCheck(); + CheckBufferOverrun check; + Check& c = getCheck(check); fileInfo.push_back(c.getFileInfo(tokenizer, settings0, "")); c.analyseWholeProgram(*ctu, fileInfo, settings0, *this); // TODO: check result while (!fileInfo.empty()) { diff --git a/test/testcheck.cpp b/test/testcheck.cpp index 8ea4cef38d8..3ae2cad3d8f 100644 --- a/test/testcheck.cpp +++ b/test/testcheck.cpp @@ -17,6 +17,7 @@ */ #include "check.h" +#include "checks.h" #include "fixture.h" #include @@ -28,23 +29,12 @@ class TestCheck : public TestFixture { private: void run() override { - TEST_CASE(instancesSorted); TEST_CASE(classInfoFormat); } - void instancesSorted() const { - for (auto i = Check::instances().cbegin(); i != Check::instances().cend(); ++i) { - auto j = i; - ++j; - if (j != Check::instances().cend()) { - ASSERT_EQUALS(true, (*i)->name() < (*j)->name()); - } - } - } - void classInfoFormat() const { - for (auto i = Check::instances().cbegin(); i != Check::instances().cend(); ++i) { - const std::string info = (*i)->classInfo(); + for (const Check * const c : CheckInstances::get()) { + const std::string info = c->classInfo(); if (!info.empty()) { ASSERT('\n' != info[0]); // No \n in the beginning ASSERT('\n' == info.back()); // \n at end diff --git a/test/testclass.cpp b/test/testclass.cpp index 2cecdfe80f6..c1b801c208b 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -9278,7 +9278,8 @@ class TestClass : public TestFixture { void ctu(const std::vector &code) { - Check &check = getCheck(); + CheckClass checkClass; + Check &check = getCheck(checkClass); // getFileInfo std::list fileInfo; @@ -9330,8 +9331,8 @@ class TestClass : public TestFixture { SimpleTokenizer tokenizer(settings1, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check.. - const Check& c = getCheck(); + CheckClass check; + const Check& c = getCheck(check); Check::FileInfo * fileInfo = (c.getFileInfo)(tokenizer, settings1, ""); delete fileInfo; diff --git a/test/testcondition.cpp b/test/testcondition.cpp index f0764dd2bd6..9a15349aff0 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -149,8 +149,8 @@ class TestCondition : public TestFixture { // Tokenizer.. ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); - // Run checks.. - runChecks(tokenizer, this); + CheckCondition check; + runChecks(check, tokenizer, this); } #define checkP(...) checkP_(__FILE__, __LINE__, __VA_ARGS__) @@ -162,8 +162,8 @@ class TestCondition : public TestFixture { // Tokenizer.. ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); - // Run checks.. - runChecks(tokenizer, this); + CheckCondition check; + runChecks(check, tokenizer, this); } void assignAndCompare() { @@ -527,7 +527,8 @@ class TestCondition : public TestFixture { SimpleTokenizer tokenizer(settings1, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - runChecks(tokenizer, this); + CheckCondition check; + runChecks(check, tokenizer, this); } void multicompare() { diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index 61916e1d0bb..0eb91ba8cca 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -763,12 +763,40 @@ class TestConstructors : public TestFixture { check("struct S {\n" // #14546 " int a = 0, b;\n" "};\n"); - ASSERT_EQUALS("[test.cpp:1:10]: (warning) Member variable 'S::b' has no initializer. [uninitMemberVarNoCtor]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:2:16]: (warning) Member variable 'S::b' has no initializer. [uninitMemberVarNoCtor]\n", errout_str()); check("struct S {\n" " int a, b;\n" "};\n"); ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " explicit S(int);\n" + " S(const S&);\n" + " int i;\n" + "};\n" + "struct T {\n" + " S s;\n" + " int j{};\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + const char code[] = "struct S { int i = 0; };\n" // #14697 + "struct T {\n" + " S s;\n" + " int j;\n" + "};\n" + "struct U {\n" + " std::string a;\n" + " int k;\n" + "};\n"; + const Settings s = settingsBuilder(settings).cpp(Standards::CPP11).build(); + check(code, s); + ASSERT_EQUALS("", errout_str()); + check(code); + ASSERT_EQUALS("[test.cpp:4:9]: (warning) Member variable 'T::j' has no initializer. [uninitMemberVarNoCtor]\n" + "[test.cpp:8:9]: (warning) Member variable 'U::k' has no initializer. [uninitMemberVarNoCtor]\n", + errout_str()); } // ticket #4290 "False Positive: style (noConstructor): The class 'foo' does not have a constructor." diff --git a/test/testexceptionsafety.cpp b/test/testexceptionsafety.cpp index 58b26ecbc27..46c995c231b 100644 --- a/test/testexceptionsafety.cpp +++ b/test/testexceptionsafety.cpp @@ -75,8 +75,8 @@ class TestExceptionSafety : public TestFixture { SimpleTokenizer tokenizer(settings1, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check char variable usage.. - runChecks(tokenizer, this); + CheckExceptionSafety check; + runChecks(check, tokenizer, this); } void destructors() { diff --git a/test/testfunctions.cpp b/test/testfunctions.cpp index 8bbd3d92c6b..9ddb4c1f43c 100644 --- a/test/testfunctions.cpp +++ b/test/testfunctions.cpp @@ -132,7 +132,8 @@ class TestFunctions : public TestFixture { SimpleTokenizer tokenizer(s, *this, cpp); ASSERT_LOC(tokenizer.tokenize(code), file, line); - runChecks(tokenizer, this); + CheckFunctions check; + runChecks(check, tokenizer, this); } void prohibitedFunctions_posix() { diff --git a/test/testgarbage.cpp b/test/testgarbage.cpp index 44f163a53c1..289b22b284e 100644 --- a/test/testgarbage.cpp +++ b/test/testgarbage.cpp @@ -17,6 +17,7 @@ */ #include "check.h" +#include "checks.h" #include "errortypes.h" #include "fixture.h" #include "helpers.h" @@ -285,8 +286,8 @@ class TestGarbage : public TestFixture { ASSERT_LOC(tokenizer.tokenize(code), file, line); // call all "runChecks" in all registered Check classes - for (auto it = Check::instances().cbegin(); it != Check::instances().cend(); ++it) { - (*it)->runChecks(tokenizer, this); + for (Check * const c : CheckInstances::get()) { + c->runChecks(tokenizer, this); } return tokenizer.tokens()->stringifyList(false, false, false, true, false, nullptr, nullptr); diff --git a/test/testinternal.cpp b/test/testinternal.cpp index 009129c5dc1..10431adcd38 100644 --- a/test/testinternal.cpp +++ b/test/testinternal.cpp @@ -56,8 +56,8 @@ class TestInternal : public TestFixture { SimpleTokenizer tokenizer(settings, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check.. - runChecks(tokenizer, this); + CheckInternal check; + runChecks(check, tokenizer, this); } void simplePatternInTokenMatch() { diff --git a/test/testio.cpp b/test/testio.cpp index 02180f12376..d3344f76b8d 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -113,7 +113,8 @@ class TestIO : public TestFixture { checkIO.checkWrongPrintfScanfArguments(); return; } - runChecks(tokenizer, this); + CheckIO check; + runChecks(check, tokenizer, this); } void coutCerrMisusage() { @@ -544,7 +545,29 @@ class TestIO : public TestFixture { " FILE *a = fopen(\"aa\", \"r\");\n" " while (fclose(a)) {}\n" "}"); - TODO_ASSERT_EQUALS("[test.cpp:3:5]: (error) Used file that is not opened. [useClosedFile]\n", "", errout_str()); + ASSERT_EQUALS("[test.cpp:3:12]: (warning) fclose() used as loop condition may skip loop body or double-close file handle. [fcloseInLoopCondition]\n", errout_str()); + + check("void foo() {\n" + " FILE *a = fopen(\"aa\", \"r\");\n" + " while (fclose(a)) {\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " FILE *a = fopen(\"aa\", \"r\");\n" + " while (fclose(a)) {\n" + " a = fopen(\"aa\", \"r\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " FILE *a = fopen(\"aa\", \"r\");\n" + " do {} while (fclose(a));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3:18]: (warning) fclose() used as loop condition may skip loop body or double-close file handle. [fcloseInLoopCondition]\n", errout_str()); // #6823 check("void foo() {\n" diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index b038270dcb7..265e31bf3c2 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -226,23 +226,17 @@ class TestLeakAutoVar : public TestFixture { template void check_(const char* file, int line, const char (&code)[size], const CheckOptions& options = make_default_obj()) { const Settings& settings1 = options.s ? *options.s : settings; - - // Tokenize.. - SimpleTokenizer tokenizer(settings1, *this, options.cpp); - ASSERT_LOC(tokenizer.tokenize(code), file, line); - - // Check for leaks.. - runChecks(tokenizer, this); + check_(file, line, code, settings1, options.cpp); } template - void check_(const char* file, int line, const char (&code)[size], const Settings & s) { + void check_(const char* file, int line, const char (&code)[size], const Settings & s, bool cpp = true) { // Tokenize.. - SimpleTokenizer tokenizer(s, *this); + SimpleTokenizer tokenizer(s, *this, cpp); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check for leaks.. - runChecks(tokenizer, this); + CheckLeakAutoVar check; + runChecks(check, tokenizer, this); } void assign1() { @@ -3272,8 +3266,8 @@ class TestLeakAutoVarRecursiveCountLimit : public TestFixture { // Tokenizer.. ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); - // Check for leaks.. - runChecks(tokenizer, this); + CheckLeakAutoVar check; + runChecks(check, tokenizer, this); } void run() override { @@ -3319,8 +3313,8 @@ class TestLeakAutoVarStrcpy : public TestFixture { SimpleTokenizer tokenizer(settings, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check for leaks.. - runChecks(tokenizer, this); + CheckLeakAutoVar check; + runChecks(check, tokenizer, this); } void run() override { @@ -3424,8 +3418,8 @@ class TestLeakAutoVarWindows : public TestFixture { SimpleTokenizer tokenizer(settings, *this, false); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check for leaks.. - runChecks(tokenizer, this); + CheckLeakAutoVar check; + runChecks(check, tokenizer, this); } void run() override { @@ -3497,8 +3491,8 @@ class TestLeakAutoVarPosix : public TestFixture { SimpleTokenizer tokenizer(settings, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check for leaks.. - runChecks(tokenizer, this); + CheckLeakAutoVar check; + runChecks(check, tokenizer, this); } void run() override { diff --git a/test/testnullpointer.cpp b/test/testnullpointer.cpp index c025d7462d4..a154bd0d9c2 100644 --- a/test/testnullpointer.cpp +++ b/test/testnullpointer.cpp @@ -190,23 +190,17 @@ class TestNullPointer : public TestFixture { template void check_(const char* file, int line, const char (&code)[size], const CheckOptions& options = make_default_obj()) { const Settings& settings1 = options.inconclusive ? settings_i : settings; - - // Tokenize.. - SimpleTokenizer tokenizer(settings1, *this, options.cpp); - ASSERT_LOC(tokenizer.tokenize(code), file, line); - - // Check for null pointer dereferences.. - runChecks(tokenizer, this); + check_(file, line, code, settings1, options.cpp); } template - void check_(const char* file, int line, const char (&code)[size], bool cpp, const Settings& s) { + void check_(const char* file, int line, const char (&code)[size], const Settings& s, bool cpp = true) { // Tokenize.. SimpleTokenizer tokenizer(s, *this, cpp); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check for null pointer dereferences.. - runChecks(tokenizer, this); + CheckNullPointer check; + runChecks(check, tokenizer, this); } #define checkP(...) checkP_(__FILE__, __LINE__, __VA_ARGS__) @@ -217,8 +211,8 @@ class TestNullPointer : public TestFixture { // Tokenizer.. ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); - // Check for null pointer dereferences.. - runChecks(tokenizer, this); + CheckNullPointer check; + runChecks(check, tokenizer, this); } @@ -1349,7 +1343,7 @@ class TestNullPointer : public TestFixture { ASSERT_EQUALS("[test.cpp:4:11]: (error) Null pointer dereference: i [nullPointer]\n", errout_str()); const Settings s = settingsBuilder(settings).c(Standards::C17).build(); - check(code, false, s); // C17 file => nullptr does not mean NULL + check(code, s, false); // C17 file => nullptr does not mean NULL ASSERT_EQUALS("", errout_str()); check(code, dinit(CheckOptions, $.cpp = false)); @@ -4698,9 +4692,9 @@ class TestNullPointer : public TestFixture { CTU::FileInfo *ctu = CTU::getFileInfo(tokenizer); - // Check code.. + CheckNullPointer check; + Check& c = getCheck(check); std::list fileInfo; - Check& c = getCheck(); fileInfo.push_back(c.getFileInfo(tokenizer, settings, "")); c.analyseWholeProgram(*ctu, fileInfo, settings, *this); // TODO: check result while (!fileInfo.empty()) { diff --git a/test/testother.cpp b/test/testother.cpp index c5a94a673fe..9a18a067231 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -367,8 +367,8 @@ class TestOther : public TestFixture { SimpleTokenizer tokenizer(*settings, *this, opt.cpp); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check.. - runChecks(tokenizer, this); + CheckOther check; + runChecks(check, tokenizer, this); } struct CheckPOptions @@ -384,8 +384,8 @@ class TestOther : public TestFixture { // Tokenizer.. ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); - // Check.. - runChecks(tokenizer, this); + CheckOther check; + runChecks(check, tokenizer, this); } template @@ -4844,6 +4844,26 @@ class TestOther : public TestFixture { " return s->x ? 1 : 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2:10]: (style) Parameter 's' can be declared as pointer to const [constParameterPointer]\n", errout_str()); + + check("struct S { int a[1][1]; };\n" // #14714 + "int f(S* s) {\n" + " return s->a[0][0] ? 1 : 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2:10]: (style) Parameter 's' can be declared as pointer to const [constParameterPointer]\n", errout_str()); + + check("int f(int *p, int *q) {\n" // #14748 + " return p ? *p : *q;\n" + "}\n" + "void g(int *p, int *q) {\n" + " int& r = p ? *p : *q;\n" + " r = 0;\n" + "}\n" + "void h(int *p, int *q) {\n" + " i(p ? *p : *q);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1:12]: (style) Parameter 'p' can be declared as pointer to const [constParameterPointer]\n" + "[test.cpp:1:20]: (style) Parameter 'q' can be declared as pointer to const [constParameterPointer]\n", + errout_str()); } void constArray() { @@ -12294,8 +12314,8 @@ class TestOther : public TestFixture { SimpleTokenizer tokenizer(settings, *this, cpp); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check.. - runChecks(tokenizer, this); + CheckOther check; + runChecks(check, tokenizer, this); } void testEvaluationOrder() { diff --git a/test/testsizeof.cpp b/test/testsizeof.cpp index ad8aa3192af..b25afe8d153 100644 --- a/test/testsizeof.cpp +++ b/test/testsizeof.cpp @@ -54,8 +54,8 @@ class TestSizeof : public TestFixture { SimpleTokenizer tokenizer(settings, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check... - runChecks(tokenizer, this); + CheckSizeof check; + runChecks(check, tokenizer, this); } #define checkP(...) checkP_(__FILE__, __LINE__, __VA_ARGS__) @@ -66,8 +66,8 @@ class TestSizeof : public TestFixture { // Tokenize.. ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); - // Check... - runChecks(tokenizer, this); + CheckSizeof check; + runChecks(check, tokenizer, this); } void sizeofsizeof() { diff --git a/test/teststl.cpp b/test/teststl.cpp index 92933c37449..84dae0b0b35 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -199,7 +199,8 @@ class TestStl : public TestFixture { ASSERT_LOC(tokenizer.tokenize(code), file, line); - runChecks(tokenizer, this); + CheckStl check; + runChecks(check, tokenizer, this); } // TODO: get rid of this @@ -209,7 +210,8 @@ class TestStl : public TestFixture { ASSERT_LOC(tokenizer.tokenize(code), file, line); - runChecks(tokenizer, this); + CheckStl check; + runChecks(check, tokenizer, this); } #define checkNormal(...) checkNormal_(__FILE__, __LINE__, __VA_ARGS__) @@ -219,8 +221,8 @@ class TestStl : public TestFixture { SimpleTokenizer tokenizer(settings, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check.. - runChecks(tokenizer, this); + CheckStl check; + runChecks(check, tokenizer, this); } void outOfBounds() { diff --git a/test/teststring.cpp b/test/teststring.cpp index 051aa870788..e0e401256e2 100644 --- a/test/teststring.cpp +++ b/test/teststring.cpp @@ -74,8 +74,8 @@ class TestString : public TestFixture { // Tokenize.. ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); - // Check char variable usage.. - runChecks(tokenizer, this); + CheckString check; + runChecks(check, tokenizer, this); } void stringLiteralWrite() { diff --git a/test/testtype.cpp b/test/testtype.cpp index 280e3357f85..80c3dfd32b8 100644 --- a/test/testtype.cpp +++ b/test/testtype.cpp @@ -61,8 +61,8 @@ class TestType : public TestFixture { SimpleTokenizer tokenizer(settings1, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check.. - runChecks(tokenizer, this); + CheckType check; + runChecks(check, tokenizer, this); } // TODO: get rid of this @@ -73,8 +73,8 @@ class TestType : public TestFixture { SimpleTokenizer tokenizer(settings1, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check.. - runChecks(tokenizer, this); + CheckType check; + runChecks(check, tokenizer, this); } struct CheckPOptions @@ -93,8 +93,8 @@ class TestType : public TestFixture { // Tokenizer.. ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); - // Check.. - runChecks(tokenizer, this); + CheckType check; + runChecks(check, tokenizer, this); } void checkTooBigShift_Unix32() { diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index db499dabf72..6fe563a57ca 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -5580,7 +5580,8 @@ class TestUninitVar : public TestFixture { // Check code.. std::list fileInfo; - Check& c = getCheck(); + CheckUninitVar check; + Check& c = getCheck(check); fileInfo.push_back(c.getFileInfo(tokenizer, settings, "")); c.analyseWholeProgram(*ctu, fileInfo, settings, *this); // TODO: check result while (!fileInfo.empty()) { diff --git a/test/testvaarg.cpp b/test/testvaarg.cpp index 36e1d2cb038..33fb6c11389 100644 --- a/test/testvaarg.cpp +++ b/test/testvaarg.cpp @@ -38,8 +38,8 @@ class TestVaarg : public TestFixture { SimpleTokenizer tokenizer(settings, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); - // Check.. - runChecks(tokenizer, this); + CheckVaarg check; + runChecks(check, tokenizer, this); } void run() override {