Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .selfcheck_unused_suppressions
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ unusedFunction:lib/symboldatabase.cpp

# Q_OBJECT functions which are not called in our code
unusedFunction:cmake.output.notest/gui/cppcheck-gui_autogen/*/moc_aboutdialog.cpp

# CheckUnionZeroInit::generateTestMessage only used in tests.
unusedFunction:lib/checkunionzeroinit.cpp
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \
$(libcppdir)/checkstring.o \
$(libcppdir)/checktype.o \
$(libcppdir)/checkuninitvar.o \
$(libcppdir)/checkunionzeroinit.o \
$(libcppdir)/checkunusedfunctions.o \
$(libcppdir)/checkunusedvar.o \
$(libcppdir)/checkvaarg.o \
Expand Down Expand Up @@ -346,6 +347,7 @@ TESTOBJ = test/fixture.o \
test/testtokenrange.o \
test/testtype.o \
test/testuninitvar.o \
test/testunionzeroinit.o \
test/testunusedfunctions.o \
test/testunusedprivfunc.o \
test/testunusedvar.o \
Expand Down Expand Up @@ -561,6 +563,9 @@ $(libcppdir)/checktype.o: lib/checktype.cpp lib/addoninfo.h lib/astutils.h lib/c
$(libcppdir)/checkuninitvar.o: lib/checkuninitvar.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkers.h lib/checknullpointer.h lib/checkuninitvar.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.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)/checkuninitvar.cpp

$(libcppdir)/checkunionzeroinit.o: lib/checkunionzeroinit.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checkunionzeroinit.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.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/valueflow.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunionzeroinit.cpp

$(libcppdir)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/checkers.h lib/checkunusedfunctions.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.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 lib/xml.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunusedfunctions.cpp

Expand Down Expand Up @@ -909,6 +914,9 @@ test/testtype.o: test/testtype.cpp lib/addoninfo.h lib/check.h lib/checkers.h li
test/testuninitvar.o: test/testuninitvar.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.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/testuninitvar.cpp

test/testunionzeroinit.o: test/testunionzeroinit.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checkunionzeroinit.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/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/testunionzeroinit.cpp

test/testunusedfunctions.o: test/testunusedfunctions.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checkunusedfunctions.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/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/testunusedfunctions.cpp

Expand Down
1 change: 1 addition & 0 deletions lib/checkers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ namespace checkers {
{"CheckType::checkLongCast","style"},
{"CheckType::checkSignConversion","warning"},
{"CheckType::checkTooBigBitwiseShift","platform"},
{"CheckUninitVar::check",""},
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does not seem correct but anyway it is auto generated so will be fixed later..

{"CheckUninitVar::analyseWholeProgram",""},
{"CheckUninitVar::check",""},
{"CheckUninitVar::valueFlowUninit",""},
Expand Down
209 changes: 209 additions & 0 deletions lib/checkunionzeroinit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
Copy link
Copy Markdown
Owner

@danmar danmar Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My feeling is that we could add this in some existing checkers class.. checkother maybe. it is just 1 checker in this class and I doubt we will add more related checkers here later.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't find that argument compelling and rather keep the logic separated.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have more than 100 checkers and we have always grouped checkers into classes we did not write each checker in its own class.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would find it more difficult to navigate our sourcecode, buildfiles, etc if we had 100's more of checkxyz.cpp , checkxyz.h and testxyz.cpp files..

* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2025 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 <http://www.gnu.org/licenses/>.
*/

#include "checkunionzeroinit.h"

#include <sstream>
#include <unordered_map>
#include <vector>

#include "errortypes.h"
#include "settings.h"
#include "symboldatabase.h"
#include "token.h"
#include "tokenize.h"
#include "valueflow.h"

static const CWE CWEXXX(0U); /* TODO */

static const std::string noname;

// Register this check class (by creating a static instance of it)
namespace {
CheckUnionZeroInit instance;
}

struct UnionMember {
UnionMember()
: name(noname)
, size(0) {}

UnionMember(const std::string &name_, size_t size_)
: name(name_)
, size(size_) {}

const std::string &name;
size_t size;
};

struct Union {
Union(const Scope *scope_, const std::string &name_)
: scope(scope_)
, name(name_) {}

const Scope *scope;
const std::string &name;
std::vector<UnionMember> members;

const UnionMember *largestMember() const {
const UnionMember *largest = nullptr;
for (const UnionMember &m : members) {
if (m.size == 0)
return nullptr;
if (largest == nullptr || m.size > largest->size)
largest = &m;
}
return largest;
}

bool isLargestMemberFirst() const {
const UnionMember *largest = largestMember();
return largest == nullptr || largest == &members[0];
}
};

static UnionMember parseUnionMember(const Variable &var,
const Settings &settings)
{
const Token *nameToken = var.nameToken();
if (nameToken == nullptr)
return UnionMember();

const ValueType *vt = nameToken->valueType();
size_t size = 0;
if (var.isArray()) {
size = var.dimension(0);
} else if (vt != nullptr) {
size = ValueFlow::getSizeOf(*vt, settings,
ValueFlow::Accuracy::ExactOrZero);
}
return UnionMember(nameToken->str(), size);
}

static std::vector<Union> parseUnions(const SymbolDatabase &symbolDatabase,
const Settings &settings)
{
std::vector<Union> unions;

for (const Scope &scope : symbolDatabase.scopeList) {
if (scope.type != ScopeType::eUnion)
continue;

Union u(&scope, scope.className);
for (const Variable &var : scope.varlist) {
u.members.push_back(parseUnionMember(var, settings));
}
unions.push_back(u);
}

return unions;
}

static bool isZeroInitializer(const Token *tok)
{
return Token::simpleMatch(tok, "= { 0 } ;") ||
Token::simpleMatch(tok, "= { } ;");
Comment on lines +119 to +120
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return Token::simpleMatch(tok, "= { 0 } ;") ||
Token::simpleMatch(tok, "= { } ;");
return Token::Match(tok, "= { 0| };");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not work as intended...

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops I forgot a space

Suggested change
return Token::simpleMatch(tok, "= { 0 } ;") ||
Token::simpleMatch(tok, "= { } ;");
return Token::Match(tok, "= { 0| } ;");

}

void CheckUnionZeroInit::check()
{
if (!mSettings->severity.isEnabled(Severity::portability))
return;

logChecker("CheckUnionZeroInit::check"); // portability

const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();

std::unordered_map<const Scope *, Union> unionsByScopeId;
const std::vector<Union> unions = parseUnions(*symbolDatabase, *mSettings);
for (const Union &u : unions) {
unionsByScopeId.insert({u.scope, u});
}

for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
if (!tok->isName() || !isZeroInitializer(tok->next()))
continue;

const ValueType *vt = tok->valueType();
if (vt == nullptr || vt->typeScope == nullptr)
continue;
auto it = unionsByScopeId.find(vt->typeScope);
if (it == unionsByScopeId.end())
continue;
const Union &u = it->second;
if (!u.isLargestMemberFirst()) {
const UnionMember *largestMember = u.largestMember();
assert(largestMember != nullptr);

Check warning

Code scanning / CppCheck

The assert macro shall not be used with a constant-expression Warning

The assert macro shall not be used with a constant-expression
Copy link
Copy Markdown
Owner

@danmar danmar Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assert macro shall not be used with a constant-expression

This is a false positive. We will fix it in Cppcheck Premium. The expression is not a constant-expression.

unionZeroInitError(tok, *largestMember);
}
}
}

void CheckUnionZeroInit::runChecks(const Tokenizer &tokenizer,
ErrorLogger *errorLogger)
{
CheckUnionZeroInit(&tokenizer, &tokenizer.getSettings(), errorLogger).check();
}

void CheckUnionZeroInit::unionZeroInitError(const Token *tok,
const UnionMember& largestMember)
{
reportError(tok, Severity::portability, "UnionZeroInit",
"$symbol:" + (tok != nullptr ? tok->str() : "") + "\n"
"Zero initializing union '$symbol' does not guarantee " +
"its complete storage to be zero initialized as its largest member " +
"is not declared as the first member. Consider making " +
largestMember.name + " the first member or favor memset().",
CWEXXX, Certainty::normal);
}

void CheckUnionZeroInit::getErrorMessages(ErrorLogger *errorLogger,
const Settings *settings) const
{
CheckUnionZeroInit c(nullptr, settings, errorLogger);
c.unionZeroInitError(nullptr, UnionMember());
Comment thread Fixed
}

std::string CheckUnionZeroInit::generateTestMessage(const Tokenizer &tokenizer,
const Settings &settings)
{
std::stringstream ss;

const std::vector<Union> unions = parseUnions(*tokenizer.getSymbolDatabase(),
settings);
for (const Union &u : unions) {
ss << "Union{";
ss << "name=\"" << u.name << "\", ";
ss << "scope=" << u.scope << ", ";
ss << "isLargestMemberFirst=" << u.isLargestMemberFirst();
ss << "}" << std::endl;

const UnionMember *largest = u.largestMember();
for (const UnionMember &m : u.members) {
ss << " Member{";
ss << "name=\"" << m.name << "\", ";
ss << "size=" << m.size;
ss << "}";
if (&m == largest)
ss << " (largest)";
ss << std::endl;
}
}

return ss.str();
}
77 changes: 77 additions & 0 deletions lib/checkunionzeroinit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* -*- C++ -*-
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2025 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 <http://www.gnu.org/licenses/>.
*/


//---------------------------------------------------------------------------
#ifndef checkunionzeroinitH
#define checkunionzeroinitH
//---------------------------------------------------------------------------

#include "check.h"
#include "config.h"

#include <string>

class ErrorLogger;
class Settings;
class Token;
class Tokenizer;
struct UnionMember;

/// @addtogroup Checks
/// @{

/**
* @brief Check for error-prone zero initialization of unions.
*/

class CPPCHECKLIB CheckUnionZeroInit : public Check {
friend class TestUnionZeroInit;

public:
/** This constructor is used when registering the CheckUnionZeroInit */
CheckUnionZeroInit() : Check(myName()) {}

private:
/** This constructor is used when running checks. */
CheckUnionZeroInit(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
: Check(myName(), tokenizer, settings, errorLogger) {}

/** @brief Run checks against the normal token list */
void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override;

/** Check for error-prone zero initialization of unions. */
void check();

void unionZeroInitError(const Token *tok, const UnionMember &largestMember);

void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override;

static std::string generateTestMessage(const Tokenizer &tokenizer, const Settings &settings);

static std::string myName() {
return "CheckUnionZeroInit";
}

std::string classInfo() const override {
return "Check for error-prone zero initialization of unions.\n";
}
};
/// @}
//---------------------------------------------------------------------------
#endif // checkunionzeroinitH
2 changes: 2 additions & 0 deletions lib/cppcheck.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<ClCompile Include="checkstring.cpp" />
<ClCompile Include="checktype.cpp" />
<ClCompile Include="checkuninitvar.cpp" />
<ClCompile Include="checkunionzeroinit.cpp" />
<ClCompile Include="checkunusedfunctions.cpp" />
<ClCompile Include="checkunusedvar.cpp" />
<ClCompile Include="checkvaarg.cpp" />
Expand Down Expand Up @@ -127,6 +128,7 @@
<ClInclude Include="checkstring.h" />
<ClInclude Include="checktype.h" />
<ClInclude Include="checkuninitvar.h" />
<ClInclude Include="checkunionzeroinit.h" />
<ClInclude Include="checkunusedfunctions.h" />
<ClInclude Include="checkunusedvar.h" />
<ClInclude Include="checkvaarg.h" />
Expand Down
2 changes: 1 addition & 1 deletion lib/tokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10043,7 +10043,7 @@ void Tokenizer::simplifyBitfields()
!Token::Match(tok->next(), "case|public|protected|private|class|struct") &&
!Token::simpleMatch(tok->tokAt(2), "default :")) {
Token *tok1 = typeTok->next();
if (Token::Match(tok1, "%name% : %num% [;=]"))
if (Token::Match(tok1, "%name% : %num% [;=,]"))
Copy link
Copy Markdown
Owner

@danmar danmar Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good.. but I am not sure about doing this in the same PR.

separate ticket+pr+testcase (in testtokenizer.cpp) would be ideal..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #7865.

if (!tok1->setBits(MathLib::toBigNumber(tok1->tokAt(2))))
tooLargeError(tok1->tokAt(2));
if (tok1 && tok1->tokAt(2) &&
Expand Down
4 changes: 4 additions & 0 deletions oss-fuzz/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \
$(libcppdir)/checkstring.o \
$(libcppdir)/checktype.o \
$(libcppdir)/checkuninitvar.o \
$(libcppdir)/checkunionzeroinit.o \
$(libcppdir)/checkunusedfunctions.o \
$(libcppdir)/checkunusedvar.o \
$(libcppdir)/checkvaarg.o \
Expand Down Expand Up @@ -243,6 +244,9 @@ $(libcppdir)/checktype.o: ../lib/checktype.cpp ../lib/addoninfo.h ../lib/astutil
$(libcppdir)/checkuninitvar.o: ../lib/checkuninitvar.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkers.h ../lib/checknullpointer.h ../lib/checkuninitvar.h ../lib/config.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.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)/checkuninitvar.cpp

$(libcppdir)/checkunionzeroinit.o: ../lib/checkunionzeroinit.cpp ../lib/addoninfo.h ../lib/check.h ../lib/checkers.h ../lib/checkunionzeroinit.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.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/valueflow.h ../lib/vfvalue.h
$(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunionzeroinit.cpp

$(libcppdir)/checkunusedfunctions.o: ../lib/checkunusedfunctions.cpp ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/astutils.h ../lib/checkers.h ../lib/checkunusedfunctions.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.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 ../lib/xml.h
$(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunusedfunctions.cpp

Expand Down
1 change: 1 addition & 0 deletions test/testrunner.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
<ClCompile Include="testtokenrange.cpp" />
<ClCompile Include="testtype.cpp" />
<ClCompile Include="testuninitvar.cpp" />
<ClCompile Include="testunionzeroinit.cpp" />
<ClCompile Include="testunusedfunctions.cpp" />
<ClCompile Include="testunusedprivfunc.cpp" />
<ClCompile Include="testunusedvar.cpp" />
Expand Down
Loading