Skip to content

fix #13954: Unnamed bit fields are removed. #7611

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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 lib/checkclass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
3 changes: 3 additions & 0 deletions lib/checkunusedvar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
8 changes: 8 additions & 0 deletions lib/token.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
17 changes: 10 additions & 7 deletions lib/tokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ("))
Expand Down Expand Up @@ -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<unsigned char>(newTok->strAt(2) == "true"));
else
newTok->setBits(static_cast<unsigned char>(MathLib::toBigNumber(newTok->tokAt(2))));
newTok->deleteNext(2);
}

if (last && last->str() == ",") {
Expand Down
11 changes: 11 additions & 0 deletions test/testconstructors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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"
Expand Down
12 changes: 6 additions & 6 deletions test/testtokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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));

Expand All @@ -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));
}
Expand Down
9 changes: 9 additions & 0 deletions test/testunusedvar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down
4 changes: 4 additions & 0 deletions test/testvalueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9105,6 +9105,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);
}
};

Expand Down
Loading