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 {