From 0fb65791fd1309e34de8f343bbba8fd10b1e906e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 19 Jun 2025 11:07:17 +0200 Subject: [PATCH 01/15] update existing tests --- test/testtokenize.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index c2781a1fb03..f0659efe6ea 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -4724,7 +4724,7 @@ class TestTokenizer : public TestFixture { ASSERT_EQUALS("struct RGB { unsigned int r ; unsigned int g ; unsigned int b ; } ;", tokenizeAndStringify(code1)); const char code2[] = "struct A { int a : 3; int : 3; int c : 3; };"; - ASSERT_EQUALS("struct A { int a ; int c ; } ;", tokenizeAndStringify(code2)); + ASSERT_EQUALS("struct A { int a ; int __cppcheck_anon_bit_field_0__ ; int c ; } ;", tokenizeAndStringify(code2)); const char code3[] = "struct A { virtual void f() {} int f1 : 1; };"; ASSERT_EQUALS("struct A { virtual void f ( ) { } int f1 ; } ;", tokenizeAndStringify(code3)); @@ -4738,7 +4738,7 @@ class TestTokenizer : public TestFixture { ASSERT_EQUALS("struct A { bool b ; bool c ; } ;", tokenizeAndStringify(code2)); const char code3[] = "struct A { bool : true; };"; - ASSERT_EQUALS("struct A { } ;", tokenizeAndStringify(code3)); + ASSERT_EQUALS("struct A { bool __cppcheck_anon_bit_field_0__ ; } ;", tokenizeAndStringify(code3)); } void bitfields7() { // ticket #1987 @@ -4789,7 +4789,7 @@ class TestTokenizer : public TestFixture { void bitfields12() { // ticket #3485 (segmentation fault) const char code[] = "{a:1;};\n"; - ASSERT_EQUALS("{ } ;", tokenizeAndStringify(code)); + ASSERT_EQUALS("{ a __cppcheck_anon_bit_field_0__ ; } ;", tokenizeAndStringify(code)); } void bitfields13() { // ticket #3502 (segmentation fault) @@ -4829,9 +4829,9 @@ class TestTokenizer : public TestFixture { "};\n"; const char expected[] = "struct S {\n" "volatile uint32_t a ;\n" - "\n" + "volatile uint32_t __cppcheck_anon_bit_field_0__ ;\n" "volatile uint32_t b ;\n" - "\n" + "volatile uint32_t __cppcheck_anon_bit_field_1__ ;\n" "} ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); @@ -4841,7 +4841,7 @@ class TestTokenizer : public TestFixture { "};\n"; const char expected2[] = "struct S {\n" "const volatile uint32_t a ;\n" - "\n" + "const volatile uint32_t __cppcheck_anon_bit_field_0__ ;\n" "} ;"; ASSERT_EQUALS(expected2, tokenizeAndStringify(code2)); } From 841c628b9bb7fdff359515f3d908073af0377460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 19 Jun 2025 15:38:35 +0200 Subject: [PATCH 02/15] add valueflow test --- test/testvalueflow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 86fa02b9191..d8d4913c5e1 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -9122,6 +9122,10 @@ class TestValueFlow : public TestFixture { "int b : 16;\n" "unsigned short c;\n", 8); + + testBitfields("unsigned int a : 31;\n" + "unsigned int : 2;\n", + 8); } }; From 38aa42f18b20f1e97f6e165cef4046b4b2270db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 19 Jun 2025 11:03:15 +0200 Subject: [PATCH 03/15] fix #13954 --- lib/checkclass.cpp | 3 +++ lib/checkunusedvar.cpp | 3 +++ lib/token.h | 8 ++++++++ lib/tokenize.cpp | 17 ++++++++++------- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 8c9cd789fc0..83bcc8b8e06 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -231,6 +231,9 @@ void CheckClass::constructors() if (usage.assign || usage.init || var.isStatic()) continue; + if (var.nameToken() && var.nameToken()->isAnonymousBitfield()) + continue; + if (var.valueType() && var.valueType()->pointer == 0 && var.type() && var.type()->needInitialization == Type::NeedInitialization::False && var.type()->derivedFrom.empty()) continue; diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index a01de5a87ca..687f5c5983b 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1575,6 +1575,9 @@ void CheckUnusedVar::checkStructMemberUsage() if (isInherited && !var.isPrivate()) continue; + if (var.nameToken() && (var.nameToken()->isAttributeUnused() || var.nameToken()->isAnonymousBitfield())) + continue; + if (mTokenizer->isVarUsedInTemplate(var.declarationId())) continue; diff --git a/lib/token.h b/lib/token.h index 59774747f70..bf6d0bc0f7d 100644 --- a/lib/token.h +++ b/lib/token.h @@ -742,6 +742,13 @@ class CPPCHECKLIB Token { setFlag(fIsInitBracket, b); } + bool isAnonymousBitfield() const { + return getFlag(fIsAnonymousBitfield); + } + void isAnonymousBitfield(bool b) { + setFlag(fIsAnonymousBitfield, b); + } + // cppcheck-suppress unusedFunction bool isBitfield() const { return mImpl->mBits > 0; @@ -1426,6 +1433,7 @@ class CPPCHECKLIB Token { fIsFinalType = (1ULL << 42), // Is this a type with final specifier fIsInitComma = (1ULL << 43), // Is this comma located inside some {..}. i.e: {1,2,3,4} fIsInitBracket = (1ULL << 44), // Is this bracket used as a part of variable initialization i.e: int a{5}, b(2); + fIsAnonymousBitfield = (1ULL << 45), // Is this a token added for an unnamed bit-field }; enum : std::uint8_t { diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index d91e9e7c3b9..83f2c660baf 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -9985,12 +9985,8 @@ void Tokenizer::simplifyAt() // Simplify bitfields void Tokenizer::simplifyBitfields() { - bool goback = false; + std::size_t anonymousBitfieldCounter = 0; for (Token *tok = list.front(); tok; tok = tok->next()) { - if (goback) { - goback = false; - tok = tok->previous(); - } Token *last = nullptr; if (Token::simpleMatch(tok, "for (")) @@ -10040,8 +10036,15 @@ void Tokenizer::simplifyBitfields() } } else if (Token::Match(typeTok, "%type% : %num%|%bool% ;") && typeTok->str() != "default") { - tok->deleteNext(4 + tokDistance(tok, typeTok) - 1); - goback = true; + const std::size_t id = anonymousBitfieldCounter++; + const std::string name = "__cppcheck_anon_bit_field_" + std::to_string(id) + "__"; + Token *newTok = typeTok->insertToken(name); + newTok->isAnonymousBitfield(true); + if (newTok->tokAt(2)->isBoolean()) + newTok->setBits(static_cast(newTok->strAt(2) == "true")); + else + newTok->setBits(static_cast(MathLib::toBigNumber(newTok->tokAt(2)))); + newTok->deleteNext(2); } if (last && last->str() == ",") { From 5db961c16932b0f85699bc6e05563d1fb4b8de3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 19 Jun 2025 11:14:03 +0200 Subject: [PATCH 04/15] add tests relating to struct members --- test/testconstructors.cpp | 11 +++++++++++ test/testunusedvar.cpp | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index b57d6d80305..39de3b1cf1d 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -172,6 +172,7 @@ class TestConstructors : public TestFixture { TEST_CASE(uninitVar33); // ticket #10295 TEST_CASE(uninitVar34); // ticket #10841 TEST_CASE(uninitVar35); + TEST_CASE(uninitVar36); TEST_CASE(uninitVarEnum1); TEST_CASE(uninitVarEnum2); // ticket #8146 TEST_CASE(uninitVarStream); @@ -3020,6 +3021,16 @@ class TestConstructors : public TestFixture { ASSERT_EQUALS("", errout_str()); } + void uninitVar36() { + check("struct S {\n" + " unsigned int a : 16;\n" + " unsigned int : 8;\n" + " unsigned int b : 8;\n" + " S() : a(0) {}\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:5:5]: (warning) Member variable 'S::b' is not initialized in the constructor. [uninitMemberVar]\n", errout_str()); + } + void uninitVarArray1() { check("class John\n" "{\n" diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index ac23e1e75f4..51e429019a6 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -70,6 +70,7 @@ class TestUnusedVar : public TestFixture { TEST_CASE(structmember25); TEST_CASE(structmember26); // #13345 TEST_CASE(structmember27); // #13367 + TEST_CASE(structmember28); TEST_CASE(structmember_macro); TEST_CASE(structmember_template_argument); // #13887 - do not report that member used in template argument is unused TEST_CASE(classmember); @@ -1998,6 +1999,14 @@ class TestUnusedVar : public TestFixture { errout_str()); } + void structmember28() { + checkStructMemberUsage("struct S {\n" + " unsigned int a : 16;\n" + " unsigned int : 16;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2:18]: (style) struct member 'S::a' is never used. [unusedStructMember]\n", errout_str()); + } + void structmember_macro() { checkStructMemberUsageP("#define S(n) struct n { int a, b, c; };\n" "S(unused);\n"); From 05125270221783c72e39e7444e5edc10458aa38c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 23 Jun 2025 10:58:06 +0200 Subject: [PATCH 05/15] add test for unnamed bit-fields of size zero --- test/testvalueflow.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index d8d4913c5e1..1d00d0b8133 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -9126,6 +9126,11 @@ class TestValueFlow : public TestFixture { testBitfields("unsigned int a : 31;\n" "unsigned int : 2;\n", 8); + + testBitfields("unsigned int a : 16;\n" + "unsigned int : 0;\n" + "unsigned int b : 16;\n", + 8); } }; From 4a392ca427247e0ad0c881b46f83e1a18e2efcdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 23 Jun 2025 10:53:38 +0200 Subject: [PATCH 06/15] handle unnamed bitfields of size zero --- addons/cppcheckdata.py | 7 ++++--- addons/misra.py | 2 +- lib/symboldatabase.cpp | 2 +- lib/symboldatabase.h | 2 +- lib/token.h | 6 +++--- lib/tokenize.cpp | 6 +++--- lib/valueflow.cpp | 13 ++++++++++--- 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/addons/cppcheckdata.py b/addons/cppcheckdata.py index 88a4d8cb157..a4257e2a3ed 100755 --- a/addons/cppcheckdata.py +++ b/addons/cppcheckdata.py @@ -166,7 +166,7 @@ class ValueType: Attributes: type nonstd/pod/record/smart-pointer/container/iterator/void/bool/char/short/wchar_t/int/long/long long/unknown int/float/double/long double sign signed/unsigned - bits + bits bit count for bit-fields, otherwise None pointer constness reference @@ -178,7 +178,7 @@ class ValueType: type = None sign = None - bits = 0 + bits = None constness = 0 pointer = 0 typeScopeId = None @@ -188,7 +188,8 @@ class ValueType: def __init__(self, element): self.type = element.get('valueType-type') self.sign = element.get('valueType-sign') - self.bits = int(element.get('valueType-bits', 0)) + self.bits = element.get('valueType-bits', None) + self.bits = int(self.bits) if self.bits else None self.pointer = int(element.get('valueType-pointer', 0)) self.constness = int(element.get('valueType-constness', 0)) self.reference = element.get('valueType-reference') diff --git a/addons/misra.py b/addons/misra.py index 1bb190adeef..655b5b0c8c5 100755 --- a/addons/misra.py +++ b/addons/misra.py @@ -2017,7 +2017,7 @@ def misra_6_1(self, data): for token in data.tokenlist: if not token.valueType: continue - if token.valueType.bits == 0: + if token.valueType.bits is None: continue if not token.variable: continue diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index d007b858e15..982bef2a4b2 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -8110,7 +8110,7 @@ std::string ValueType::dump() const break; } - if (bits > 0) { + if (bits >= 0) { ret += " valueType-bits=\""; ret += std::to_string(bits); ret += '\"'; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 6d16fddbe84..b260bf08354 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1236,7 +1236,7 @@ class CPPCHECKLIB ValueType { DOUBLE, LONGDOUBLE } type = UNKNOWN_TYPE; - nonneg int bits{}; ///< bitfield bitcount + int bits{}; ///< bitfield bitcount nonneg int pointer{}; ///< 0=>not pointer, 1=>*, 2=>**, 3=>***, etc nonneg int constness{}; ///< bit 0=data, bit 1=*, bit 2=** nonneg int volatileness{}; ///< bit 0=data, bit 1=*, bit 2=** diff --git a/lib/token.h b/lib/token.h index bf6d0bc0f7d..494bfaa01cb 100644 --- a/lib/token.h +++ b/lib/token.h @@ -82,7 +82,7 @@ struct TokenImpl { nonneg int mIndex{}; /** Bitfield bit count. */ - unsigned char mBits{}; + char mBits = -1; // AST.. Token* mAstOperand1{}; @@ -751,9 +751,9 @@ class CPPCHECKLIB Token { // cppcheck-suppress unusedFunction bool isBitfield() const { - return mImpl->mBits > 0; + return mImpl->mBits >= 0; } - unsigned char bits() const { + char bits() const { return mImpl->mBits; } const std::set* templateSimplifierPointers() const { diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 83f2c660baf..f1c4993ebbd 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -10015,7 +10015,7 @@ void Tokenizer::simplifyBitfields() !Token::simpleMatch(tok->tokAt(2), "default :")) { Token *tok1 = typeTok->next(); if (Token::Match(tok1, "%name% : %num% [;=]")) - tok1->setBits(static_cast(MathLib::toBigNumber(tok1->tokAt(2)))); + tok1->setBits(static_cast(MathLib::toBigNumber(tok1->tokAt(2)))); if (tok1 && tok1->tokAt(2) && (Token::Match(tok1->tokAt(2), "%bool%|%num%") || !Token::Match(tok1->tokAt(2), "public|protected|private| %type% ::|<|,|{|;"))) { @@ -10041,9 +10041,9 @@ void Tokenizer::simplifyBitfields() Token *newTok = typeTok->insertToken(name); newTok->isAnonymousBitfield(true); if (newTok->tokAt(2)->isBoolean()) - newTok->setBits(static_cast(newTok->strAt(2) == "true")); + newTok->setBits(static_cast(newTok->strAt(2) == "true")); else - newTok->setBits(static_cast(MathLib::toBigNumber(newTok->tokAt(2)))); + newTok->setBits(static_cast(MathLib::toBigNumber(newTok->tokAt(2)))); newTok->deleteNext(2); } diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 81e65c8347f..a102082a82b 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -440,7 +440,7 @@ static Result accumulateStructMembers(const Scope* scope, F f, ValueFlow::Accura for (const Variable& var : scope->varlist) { if (var.isStatic()) continue; - const size_t bits = var.nameToken() ? var.nameToken()->bits() : 0; + const char bits = var.nameToken() ? var.nameToken()->bits() : -1; if (const ValueType* vt = var.valueType()) { if (vt->type == ValueType::Type::RECORD && vt->typeScope == scope) return {0, false}; @@ -455,7 +455,7 @@ static Result accumulateStructMembers(const Scope* scope, F f, ValueFlow::Accura else total = f(total, *vt, dim, bits); } - if (accuracy == ValueFlow::Accuracy::ExactOrZero && total == 0 && bits == 0) + if (accuracy == ValueFlow::Accuracy::ExactOrZero && total == 0 && bits == -1) return {0, false}; } return {total, true}; @@ -537,10 +537,17 @@ size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, Accur if (vt.type == ValueType::Type::RECORD && vt.typeScope) { size_t currentBitCount = 0; size_t currentBitfieldAlloc = 0; - auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, size_t bits) -> size_t { + auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, char bits) -> size_t { const size_t charBit = settings.platform.char_bit; size_t n = ValueFlow::getSizeOf(vt2, settings,accuracy, ++maxRecursion); size_t a = getAlignOf(vt2, settings, accuracy); + if (bits == 0) { + if (currentBitfieldAlloc == 0) { + bits = n * charBit; + } else { + bits = currentBitfieldAlloc * charBit - currentBitCount; + } + } if (bits > 0) { size_t ret = total; if (currentBitfieldAlloc == 0) { From b59140b511bedd7d099ef39c1b542e623a85d2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 24 Jun 2025 14:09:53 +0200 Subject: [PATCH 07/15] add test for bit-field with greater number of bits than its type --- test/testvalueflow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 1d00d0b8133..d8908294a9b 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -9131,6 +9131,9 @@ class TestValueFlow : public TestFixture { "unsigned int : 0;\n" "unsigned int b : 16;\n", 8); + + testBitfields("unsigned char a : 16;\n", + 2); } }; From bfd9490e7f83b8e647203d5ec87b4ac1979263d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 24 Jun 2025 14:09:34 +0200 Subject: [PATCH 08/15] account for bit-fields with greater number of bits than its type --- lib/token.h | 6 +++--- lib/tokenize.cpp | 6 +++--- lib/valueflow.cpp | 8 ++++++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/token.h b/lib/token.h index 494bfaa01cb..b722609cb62 100644 --- a/lib/token.h +++ b/lib/token.h @@ -82,7 +82,7 @@ struct TokenImpl { nonneg int mIndex{}; /** Bitfield bit count. */ - char mBits = -1; + MathLib::bigint mBits = -1; // AST.. Token* mAstOperand1{}; @@ -753,7 +753,7 @@ class CPPCHECKLIB Token { bool isBitfield() const { return mImpl->mBits >= 0; } - char bits() const { + MathLib::bigint bits() const { return mImpl->mBits; } const std::set* templateSimplifierPointers() const { @@ -767,7 +767,7 @@ class CPPCHECKLIB Token { mImpl->mTemplateSimplifierPointers = new std::set; mImpl->mTemplateSimplifierPointers->insert(tokenAndName); } - void setBits(const unsigned char b) { + void setBits(const MathLib::bigint b) { mImpl->mBits = b; } diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index f1c4993ebbd..8bcefa60c97 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -10015,7 +10015,7 @@ void Tokenizer::simplifyBitfields() !Token::simpleMatch(tok->tokAt(2), "default :")) { Token *tok1 = typeTok->next(); if (Token::Match(tok1, "%name% : %num% [;=]")) - tok1->setBits(static_cast(MathLib::toBigNumber(tok1->tokAt(2)))); + tok1->setBits(MathLib::toBigNumber(tok1->tokAt(2))); if (tok1 && tok1->tokAt(2) && (Token::Match(tok1->tokAt(2), "%bool%|%num%") || !Token::Match(tok1->tokAt(2), "public|protected|private| %type% ::|<|,|{|;"))) { @@ -10041,9 +10041,9 @@ void Tokenizer::simplifyBitfields() Token *newTok = typeTok->insertToken(name); newTok->isAnonymousBitfield(true); if (newTok->tokAt(2)->isBoolean()) - newTok->setBits(static_cast(newTok->strAt(2) == "true")); + newTok->setBits(newTok->strAt(2) == "true"); else - newTok->setBits(static_cast(MathLib::toBigNumber(newTok->tokAt(2)))); + newTok->setBits(MathLib::toBigNumber(newTok->tokAt(2))); newTok->deleteNext(2); } diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index a102082a82b..0de180e9d3f 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -440,7 +440,7 @@ static Result accumulateStructMembers(const Scope* scope, F f, ValueFlow::Accura for (const Variable& var : scope->varlist) { if (var.isStatic()) continue; - const char bits = var.nameToken() ? var.nameToken()->bits() : -1; + const MathLib::bigint bits = var.nameToken() ? var.nameToken()->bits() : -1; if (const ValueType* vt = var.valueType()) { if (vt->type == ValueType::Type::RECORD && vt->typeScope == scope) return {0, false}; @@ -537,7 +537,7 @@ size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, Accur if (vt.type == ValueType::Type::RECORD && vt.typeScope) { size_t currentBitCount = 0; size_t currentBitfieldAlloc = 0; - auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, char bits) -> size_t { + auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, MathLib::bigint bits) -> size_t { const size_t charBit = settings.platform.char_bit; size_t n = ValueFlow::getSizeOf(vt2, settings,accuracy, ++maxRecursion); size_t a = getAlignOf(vt2, settings, accuracy); @@ -558,6 +558,10 @@ size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, Accur currentBitfieldAlloc = n; currentBitCount = 0; } + while (bits > charBit * currentBitfieldAlloc) { + ret += currentBitfieldAlloc; + bits -= charBit * currentBitfieldAlloc; + } currentBitCount += bits; return ret; } From 93eb7720c35204a822066ed2294bcff479df9c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 24 Jun 2025 15:11:36 +0200 Subject: [PATCH 09/15] change string of token inserted for unnamed bit-fields --- lib/checkclass.cpp | 2 +- lib/checkunusedvar.cpp | 2 +- lib/token.h | 10 +++++----- lib/tokenize.cpp | 4 ++-- test/testtokenize.cpp | 12 ++++++------ 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 83bcc8b8e06..c1171345676 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -231,7 +231,7 @@ void CheckClass::constructors() if (usage.assign || usage.init || var.isStatic()) continue; - if (var.nameToken() && var.nameToken()->isAnonymousBitfield()) + if (var.nameToken() && var.nameToken()->isAnonymous()) continue; if (var.valueType() && var.valueType()->pointer == 0 && var.type() && var.type()->needInitialization == Type::NeedInitialization::False && var.type()->derivedFrom.empty()) diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 687f5c5983b..49602a28f25 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1575,7 +1575,7 @@ void CheckUnusedVar::checkStructMemberUsage() if (isInherited && !var.isPrivate()) continue; - if (var.nameToken() && (var.nameToken()->isAttributeUnused() || var.nameToken()->isAnonymousBitfield())) + if (var.nameToken() && (var.nameToken()->isAttributeUnused() || var.nameToken()->isAnonymous())) continue; if (mTokenizer->isVarUsedInTemplate(var.declarationId())) diff --git a/lib/token.h b/lib/token.h index b722609cb62..01aab826628 100644 --- a/lib/token.h +++ b/lib/token.h @@ -742,11 +742,11 @@ class CPPCHECKLIB Token { setFlag(fIsInitBracket, b); } - bool isAnonymousBitfield() const { - return getFlag(fIsAnonymousBitfield); + bool isAnonymous() const { + return getFlag(fIsAnonymous); } - void isAnonymousBitfield(bool b) { - setFlag(fIsAnonymousBitfield, b); + void isAnonymous(bool b) { + setFlag(fIsAnonymous, b); } // cppcheck-suppress unusedFunction @@ -1433,7 +1433,7 @@ class CPPCHECKLIB Token { fIsFinalType = (1ULL << 42), // Is this a type with final specifier fIsInitComma = (1ULL << 43), // Is this comma located inside some {..}. i.e: {1,2,3,4} fIsInitBracket = (1ULL << 44), // Is this bracket used as a part of variable initialization i.e: int a{5}, b(2); - fIsAnonymousBitfield = (1ULL << 45), // Is this a token added for an unnamed bit-field + fIsAnonymous = (1ULL << 45), // Is this a token added for an unnamed bit-field }; enum : std::uint8_t { diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 8bcefa60c97..775e52b7493 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -10037,9 +10037,9 @@ void Tokenizer::simplifyBitfields() } else if (Token::Match(typeTok, "%type% : %num%|%bool% ;") && typeTok->str() != "default") { const std::size_t id = anonymousBitfieldCounter++; - const std::string name = "__cppcheck_anon_bit_field_" + std::to_string(id) + "__"; + const std::string name = "anonymous@" + std::to_string(id); Token *newTok = typeTok->insertToken(name); - newTok->isAnonymousBitfield(true); + newTok->isAnonymous(true); if (newTok->tokAt(2)->isBoolean()) newTok->setBits(newTok->strAt(2) == "true"); else diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index f0659efe6ea..d204c51eb59 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -4724,7 +4724,7 @@ class TestTokenizer : public TestFixture { ASSERT_EQUALS("struct RGB { unsigned int r ; unsigned int g ; unsigned int b ; } ;", tokenizeAndStringify(code1)); const char code2[] = "struct A { int a : 3; int : 3; int c : 3; };"; - ASSERT_EQUALS("struct A { int a ; int __cppcheck_anon_bit_field_0__ ; int c ; } ;", tokenizeAndStringify(code2)); + ASSERT_EQUALS("struct A { int a ; int anonymous@0 ; int c ; } ;", tokenizeAndStringify(code2)); const char code3[] = "struct A { virtual void f() {} int f1 : 1; };"; ASSERT_EQUALS("struct A { virtual void f ( ) { } int f1 ; } ;", tokenizeAndStringify(code3)); @@ -4738,7 +4738,7 @@ class TestTokenizer : public TestFixture { ASSERT_EQUALS("struct A { bool b ; bool c ; } ;", tokenizeAndStringify(code2)); const char code3[] = "struct A { bool : true; };"; - ASSERT_EQUALS("struct A { bool __cppcheck_anon_bit_field_0__ ; } ;", tokenizeAndStringify(code3)); + ASSERT_EQUALS("struct A { bool anonymous@0 ; } ;", tokenizeAndStringify(code3)); } void bitfields7() { // ticket #1987 @@ -4789,7 +4789,7 @@ class TestTokenizer : public TestFixture { void bitfields12() { // ticket #3485 (segmentation fault) const char code[] = "{a:1;};\n"; - ASSERT_EQUALS("{ a __cppcheck_anon_bit_field_0__ ; } ;", tokenizeAndStringify(code)); + ASSERT_EQUALS("{ a anonymous@0 ; } ;", tokenizeAndStringify(code)); } void bitfields13() { // ticket #3502 (segmentation fault) @@ -4829,9 +4829,9 @@ class TestTokenizer : public TestFixture { "};\n"; const char expected[] = "struct S {\n" "volatile uint32_t a ;\n" - "volatile uint32_t __cppcheck_anon_bit_field_0__ ;\n" + "volatile uint32_t anonymous@0 ;\n" "volatile uint32_t b ;\n" - "volatile uint32_t __cppcheck_anon_bit_field_1__ ;\n" + "volatile uint32_t anonymous@1 ;\n" "} ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); @@ -4841,7 +4841,7 @@ class TestTokenizer : public TestFixture { "};\n"; const char expected2[] = "struct S {\n" "const volatile uint32_t a ;\n" - "const volatile uint32_t __cppcheck_anon_bit_field_0__ ;\n" + "const volatile uint32_t anonymous@0 ;\n" "} ;"; ASSERT_EQUALS(expected2, tokenizeAndStringify(code2)); } From de24a8f9ee9bb030b2ccbbaa0beec6284ade06ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 25 Jun 2025 09:05:58 +0200 Subject: [PATCH 10/15] update comment --- lib/token.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/token.h b/lib/token.h index 01aab826628..5c6b2295d4b 100644 --- a/lib/token.h +++ b/lib/token.h @@ -1433,7 +1433,7 @@ class CPPCHECKLIB Token { fIsFinalType = (1ULL << 42), // Is this a type with final specifier fIsInitComma = (1ULL << 43), // Is this comma located inside some {..}. i.e: {1,2,3,4} fIsInitBracket = (1ULL << 44), // Is this bracket used as a part of variable initialization i.e: int a{5}, b(2); - fIsAnonymous = (1ULL << 45), // Is this a token added for an unnamed bit-field + fIsAnonymous = (1ULL << 45), // Is this a token added for an unnamed member }; enum : std::uint8_t { From 60ec8283809a2e24fd4fa02f4063f2c3ddc58c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 25 Jun 2025 09:07:22 +0200 Subject: [PATCH 11/15] skip unnamed variables in checkclass and checkunusedvar --- lib/checkclass.cpp | 2 +- lib/checkunusedvar.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index c1171345676..8a12a3e2f66 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -231,7 +231,7 @@ void CheckClass::constructors() if (usage.assign || usage.init || var.isStatic()) continue; - if (var.nameToken() && var.nameToken()->isAnonymous()) + if (!var.nameToken() || var.nameToken()->isAnonymous()) continue; if (var.valueType() && var.valueType()->pointer == 0 && var.type() && var.type()->needInitialization == Type::NeedInitialization::False && var.type()->derivedFrom.empty()) diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 49602a28f25..a7e26fe41fc 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1575,7 +1575,7 @@ void CheckUnusedVar::checkStructMemberUsage() if (isInherited && !var.isPrivate()) continue; - if (var.nameToken() && (var.nameToken()->isAttributeUnused() || var.nameToken()->isAnonymous())) + if (!var.nameToken() || var.nameToken()->isAttributeUnused() || var.nameToken()->isAnonymous()) continue; if (mTokenizer->isVarUsedInTemplate(var.declarationId())) From 373922d97fc944ec70d6d8f96e91ce85a63ee177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 25 Jun 2025 11:02:23 +0200 Subject: [PATCH 12/15] add test for too large bit-fields --- test/testtokenize.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index d204c51eb59..7107eb091d0 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -304,6 +304,7 @@ class TestTokenizer : public TestFixture { TEST_CASE(bitfields15); // ticket #7747 (enum Foo {A,B}:4;) TEST_CASE(bitfields16); // Save bitfield bit count TEST_CASE(bitfields17); + TEST_CASE(bitfields18); TEST_CASE(simplifyNamespaceStd); @@ -4846,6 +4847,12 @@ class TestTokenizer : public TestFixture { ASSERT_EQUALS(expected2, tokenizeAndStringify(code2)); } + void bitfields18() { + const char code[] = "struct S { unsigned int a : 100000; };"; + (void) tokenizeAndStringify(code); + ASSERT_EQUALS("[test.cpp:1:29]: (warning) Bit-field size exceeds max number of bits 32767 [tooLargeBitField]\n", errout_str()); + } + void simplifyNamespaceStd() { const char *expected; From 480662379e933c904d4db3aea376d718020bd853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 25 Jun 2025 10:38:53 +0200 Subject: [PATCH 13/15] use short for bit count, print error on larger values --- lib/token.h | 14 ++++++++++---- lib/tokenize.cpp | 18 +++++++++++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/token.h b/lib/token.h index 5c6b2295d4b..a1f78c30245 100644 --- a/lib/token.h +++ b/lib/token.h @@ -82,7 +82,7 @@ struct TokenImpl { nonneg int mIndex{}; /** Bitfield bit count. */ - MathLib::bigint mBits = -1; + short mBits = -1; // AST.. Token* mAstOperand1{}; @@ -753,7 +753,7 @@ class CPPCHECKLIB Token { bool isBitfield() const { return mImpl->mBits >= 0; } - MathLib::bigint bits() const { + short bits() const { return mImpl->mBits; } const std::set* templateSimplifierPointers() const { @@ -767,8 +767,14 @@ class CPPCHECKLIB Token { mImpl->mTemplateSimplifierPointers = new std::set; mImpl->mTemplateSimplifierPointers->insert(tokenAndName); } - void setBits(const MathLib::bigint b) { - mImpl->mBits = b; + bool setBits(const MathLib::bigint b) { + const MathLib::bigint max = std::numeric_limits::max(); + if (b > max) { + mImpl->mBits = max; + return false; + } + mImpl->mBits = b < 0 ? -1 : b; + return true; } bool isUtf8() const { diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 775e52b7493..ae2791738f1 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -10007,6 +10007,14 @@ void Tokenizer::simplifyBitfields() } } + const auto tooLargeError = [this](const Token *tok) { + const MathLib::bigint max = std::numeric_limits::max(); + reportError(tok, + Severity::warning, + "tooLargeBitField", + "Bit-field size exceeds max number of bits " + std::to_string(max)); + }; + Token* typeTok = tok->next(); while (Token::Match(typeTok, "const|volatile")) typeTok = typeTok->next(); @@ -10015,7 +10023,8 @@ void Tokenizer::simplifyBitfields() !Token::simpleMatch(tok->tokAt(2), "default :")) { Token *tok1 = typeTok->next(); if (Token::Match(tok1, "%name% : %num% [;=]")) - tok1->setBits(MathLib::toBigNumber(tok1->tokAt(2))); + if (!tok1->setBits(MathLib::toBigNumber(tok1->tokAt(2)))) + tooLargeError(tok1->tokAt(2)); if (tok1 && tok1->tokAt(2) && (Token::Match(tok1->tokAt(2), "%bool%|%num%") || !Token::Match(tok1->tokAt(2), "public|protected|private| %type% ::|<|,|{|;"))) { @@ -10040,10 +10049,13 @@ void Tokenizer::simplifyBitfields() const std::string name = "anonymous@" + std::to_string(id); Token *newTok = typeTok->insertToken(name); newTok->isAnonymous(true); + bool failed; if (newTok->tokAt(2)->isBoolean()) - newTok->setBits(newTok->strAt(2) == "true"); + failed = !newTok->setBits(newTok->strAt(2) == "true"); else - newTok->setBits(MathLib::toBigNumber(newTok->tokAt(2))); + failed = !newTok->setBits(MathLib::toBigNumber(newTok->tokAt(2))); + if (failed) + tooLargeError(newTok->tokAt(2)); newTok->deleteNext(2); } From cc64353a52519cde0604cf6a12241c501ecf10d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 26 Jun 2025 09:54:11 +0200 Subject: [PATCH 14/15] force Unix64 platform in bitfields test --- test/testvalueflow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index d8908294a9b..68656d5c356 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -9084,8 +9084,9 @@ class TestValueFlow : public TestFixture { #define testBitfields(structBody, expectedSize) testBitfields_(__FILE__, __LINE__, structBody, expectedSize) void testBitfields_(const char *file, int line, const std::string &structBody, std::size_t expectedSize) { + const Settings settingsUnix64 = settingsBuilder().platform(Platform::Type::Unix64).build(); const std::string code = "struct S { " + structBody + " }; const std::size_t size = sizeof(S);"; - const auto values = tokenValues(code.c_str(), "( S"); + const auto values = tokenValues(code.c_str(), "( S", &settingsUnix64); ASSERT_LOC(!values.empty(), file, line); ASSERT_EQUALS_LOC(expectedSize, values.back().intvalue, file, line); } From 1e455057f2841ecd31b3bef82f58112a04d6b4bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 4 Jul 2025 11:58:20 +0200 Subject: [PATCH 15/15] add isAnonymous to dumpfile and cppcheckdata --- addons/cppcheckdata.py | 6 +++++- lib/tokenize.cpp | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/cppcheckdata.py b/addons/cppcheckdata.py index a4257e2a3ed..b0e53161f3a 100755 --- a/addons/cppcheckdata.py +++ b/addons/cppcheckdata.py @@ -263,6 +263,7 @@ class Token: isComplex isRestrict isAttributeExport + isAnonymous varId varId for token, each variable has a unique non-zero id exprId exprId for token, each expression has a unique non-zero id variable Variable information for this token. See the Variable class. @@ -324,6 +325,7 @@ class Token: isComplex = False isRestrict = False isAttributeExport = False + isAnonymous = False exprId = None varId = None variableId = None @@ -407,6 +409,8 @@ def __init__(self, element): self.isRestrict = True if element.get('isAttributeExport'): self.isAttributeExport = True + if element.get('isAnonymous'): + self.isAnonymous = True self.linkId = element.get('link') self.link = None if element.get('varId'): @@ -440,7 +444,7 @@ def __repr__(self): "isChar", "isBoolean", "isOp", "isArithmeticalOp", "isAssignmentOp", "isComparisonOp", "isLogicalOp", "isCast", "externLang", "isExpandedMacro", "isRemovedVoidParameter", "isSplittedVarDeclComma", "isSplittedVarDeclEq", - "isImplicitInt", "isComplex", "isRestrict", "isAttributeExport", "linkId", + "isImplicitInt", "isComplex", "isRestrict", "isAttributeExport", "isAnonymous", "linkId", "varId", "variableId", "functionId", "valuesId", "valueType", "typeScopeId", "astParentId", "astOperand1Id", "file", "linenr", "column"] diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index ae2791738f1..7be8b213504 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -6103,6 +6103,8 @@ void Tokenizer::dump(std::ostream &out) const outs += " isAttributeFallthrough=\"true\""; if (tok->isInitBracket()) outs += " isInitBracket=\"true\""; + if (tok->isAnonymous()) + outs += " isAnonymous=\"true\""; if (tok->hasAttributeAlignas()) { const std::vector& a = tok->getAttributeAlignas(); outs += " alignas=\"" + ErrorLogger::toxml(a[0]) + "\"";