diff --git a/addons/cppcheckdata.py b/addons/cppcheckdata.py index 88a4d8cb157..b0e53161f3a 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') @@ -262,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. @@ -323,6 +325,7 @@ class Token: isComplex = False isRestrict = False isAttributeExport = False + isAnonymous = False exprId = None varId = None variableId = None @@ -406,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'): @@ -439,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/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/checkclass.cpp b/lib/checkclass.cpp index 8c9cd789fc0..8a12a3e2f66 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()->isAnonymous()) + 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..a7e26fe41fc 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()->isAnonymous()) + continue; + if (mTokenizer->isVarUsedInTemplate(var.declarationId())) 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 59774747f70..a1f78c30245 100644 --- a/lib/token.h +++ b/lib/token.h @@ -82,7 +82,7 @@ struct TokenImpl { nonneg int mIndex{}; /** Bitfield bit count. */ - unsigned char mBits{}; + short mBits = -1; // AST.. Token* mAstOperand1{}; @@ -742,11 +742,18 @@ class CPPCHECKLIB Token { setFlag(fIsInitBracket, b); } + bool isAnonymous() const { + return getFlag(fIsAnonymous); + } + void isAnonymous(bool b) { + setFlag(fIsAnonymous, b); + } + // cppcheck-suppress unusedFunction bool isBitfield() const { - return mImpl->mBits > 0; + return mImpl->mBits >= 0; } - unsigned char bits() const { + short bits() const { return mImpl->mBits; } const std::set* templateSimplifierPointers() const { @@ -760,8 +767,14 @@ class CPPCHECKLIB Token { mImpl->mTemplateSimplifierPointers = new std::set; mImpl->mTemplateSimplifierPointers->insert(tokenAndName); } - void setBits(const unsigned char 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 { @@ -1426,6 +1439,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 member }; enum : std::uint8_t { diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index d91e9e7c3b9..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]) + "\""; @@ -9985,12 +9987,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 (")) @@ -10011,6 +10009,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(); @@ -10019,7 +10025,8 @@ 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)))); + 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,8 +10047,18 @@ 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 = "anonymous@" + std::to_string(id); + Token *newTok = typeTok->insertToken(name); + newTok->isAnonymous(true); + bool failed; + if (newTok->tokAt(2)->isBoolean()) + failed = !newTok->setBits(newTok->strAt(2) == "true"); + else + failed = !newTok->setBits(MathLib::toBigNumber(newTok->tokAt(2))); + if (failed) + tooLargeError(newTok->tokAt(2)); + newTok->deleteNext(2); } if (last && last->str() == ",") { diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 81e65c8347f..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 size_t bits = var.nameToken() ? var.nameToken()->bits() : 0; + 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}; @@ -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, 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); + if (bits == 0) { + if (currentBitfieldAlloc == 0) { + bits = n * charBit; + } else { + bits = currentBitfieldAlloc * charBit - currentBitCount; + } + } if (bits > 0) { size_t ret = total; if (currentBitfieldAlloc == 0) { @@ -551,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; } 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/testtokenize.cpp b/test/testtokenize.cpp index c2781a1fb03..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); @@ -4724,7 +4725,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 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 +4739,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 anonymous@0 ; } ;", tokenizeAndStringify(code3)); } void bitfields7() { // ticket #1987 @@ -4789,7 +4790,7 @@ class TestTokenizer : public TestFixture { void bitfields12() { // ticket #3485 (segmentation fault) const char code[] = "{a:1;};\n"; - ASSERT_EQUALS("{ } ;", tokenizeAndStringify(code)); + ASSERT_EQUALS("{ a anonymous@0 ; } ;", tokenizeAndStringify(code)); } void bitfields13() { // ticket #3502 (segmentation fault) @@ -4829,9 +4830,9 @@ class TestTokenizer : public TestFixture { "};\n"; const char expected[] = "struct S {\n" "volatile uint32_t a ;\n" - "\n" + "volatile uint32_t anonymous@0 ;\n" "volatile uint32_t b ;\n" - "\n" + "volatile uint32_t anonymous@1 ;\n" "} ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); @@ -4841,11 +4842,17 @@ class TestTokenizer : public TestFixture { "};\n"; const char expected2[] = "struct S {\n" "const volatile uint32_t a ;\n" - "\n" + "const volatile uint32_t anonymous@0 ;\n" "} ;"; 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; 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"); diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 86fa02b9191..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); } @@ -9122,6 +9123,18 @@ class TestValueFlow : public TestFixture { "int b : 16;\n" "unsigned short c;\n", 8); + + 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); + + testBitfields("unsigned char a : 16;\n", + 2); } };