Skip to content

Commit 326a930

Browse files
authored
fix #13954: Unnamed bit fields are removed. (#7611)
1 parent a214e76 commit 326a930

13 files changed

+123
-30
lines changed

addons/cppcheckdata.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class ValueType:
166166
Attributes:
167167
type nonstd/pod/record/smart-pointer/container/iterator/void/bool/char/short/wchar_t/int/long/long long/unknown int/float/double/long double
168168
sign signed/unsigned
169-
bits
169+
bits bit count for bit-fields, otherwise None
170170
pointer
171171
constness
172172
reference
@@ -178,7 +178,7 @@ class ValueType:
178178

179179
type = None
180180
sign = None
181-
bits = 0
181+
bits = None
182182
constness = 0
183183
pointer = 0
184184
typeScopeId = None
@@ -188,7 +188,8 @@ class ValueType:
188188
def __init__(self, element):
189189
self.type = element.get('valueType-type')
190190
self.sign = element.get('valueType-sign')
191-
self.bits = int(element.get('valueType-bits', 0))
191+
self.bits = element.get('valueType-bits', None)
192+
self.bits = int(self.bits) if self.bits else None
192193
self.pointer = int(element.get('valueType-pointer', 0))
193194
self.constness = int(element.get('valueType-constness', 0))
194195
self.reference = element.get('valueType-reference')
@@ -262,6 +263,7 @@ class Token:
262263
isComplex
263264
isRestrict
264265
isAttributeExport
266+
isAnonymous
265267
varId varId for token, each variable has a unique non-zero id
266268
exprId exprId for token, each expression has a unique non-zero id
267269
variable Variable information for this token. See the Variable class.
@@ -323,6 +325,7 @@ class Token:
323325
isComplex = False
324326
isRestrict = False
325327
isAttributeExport = False
328+
isAnonymous = False
326329
exprId = None
327330
varId = None
328331
variableId = None
@@ -406,6 +409,8 @@ def __init__(self, element):
406409
self.isRestrict = True
407410
if element.get('isAttributeExport'):
408411
self.isAttributeExport = True
412+
if element.get('isAnonymous'):
413+
self.isAnonymous = True
409414
self.linkId = element.get('link')
410415
self.link = None
411416
if element.get('varId'):
@@ -439,7 +444,7 @@ def __repr__(self):
439444
"isChar", "isBoolean", "isOp", "isArithmeticalOp", "isAssignmentOp",
440445
"isComparisonOp", "isLogicalOp", "isCast", "externLang", "isExpandedMacro",
441446
"isRemovedVoidParameter", "isSplittedVarDeclComma", "isSplittedVarDeclEq",
442-
"isImplicitInt", "isComplex", "isRestrict", "isAttributeExport", "linkId",
447+
"isImplicitInt", "isComplex", "isRestrict", "isAttributeExport", "isAnonymous", "linkId",
443448
"varId", "variableId", "functionId", "valuesId", "valueType",
444449
"typeScopeId", "astParentId", "astOperand1Id", "file",
445450
"linenr", "column"]

addons/misra.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2017,7 +2017,7 @@ def misra_6_1(self, data):
20172017
for token in data.tokenlist:
20182018
if not token.valueType:
20192019
continue
2020-
if token.valueType.bits == 0:
2020+
if token.valueType.bits is None:
20212021
continue
20222022
if not token.variable:
20232023
continue

lib/checkclass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ void CheckClass::constructors()
231231
if (usage.assign || usage.init || var.isStatic())
232232
continue;
233233

234+
if (!var.nameToken() || var.nameToken()->isAnonymous())
235+
continue;
236+
234237
if (var.valueType() && var.valueType()->pointer == 0 && var.type() && var.type()->needInitialization == Type::NeedInitialization::False && var.type()->derivedFrom.empty())
235238
continue;
236239

lib/checkunusedvar.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,9 @@ void CheckUnusedVar::checkStructMemberUsage()
15751575
if (isInherited && !var.isPrivate())
15761576
continue;
15771577

1578+
if (!var.nameToken() || var.nameToken()->isAttributeUnused() || var.nameToken()->isAnonymous())
1579+
continue;
1580+
15781581
if (mTokenizer->isVarUsedInTemplate(var.declarationId()))
15791582
continue;
15801583

lib/symboldatabase.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8141,7 +8141,7 @@ std::string ValueType::dump() const
81418141
break;
81428142
}
81438143

8144-
if (bits > 0) {
8144+
if (bits >= 0) {
81458145
ret += " valueType-bits=\"";
81468146
ret += std::to_string(bits);
81478147
ret += '\"';

lib/symboldatabase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1237,7 +1237,7 @@ class CPPCHECKLIB ValueType {
12371237
DOUBLE,
12381238
LONGDOUBLE
12391239
} type = UNKNOWN_TYPE;
1240-
nonneg int bits{}; ///< bitfield bitcount
1240+
int bits{}; ///< bitfield bitcount
12411241
nonneg int pointer{}; ///< 0=>not pointer, 1=>*, 2=>**, 3=>***, etc
12421242
nonneg int constness{}; ///< bit 0=data, bit 1=*, bit 2=**
12431243
nonneg int volatileness{}; ///< bit 0=data, bit 1=*, bit 2=**

lib/token.h

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ struct TokenImpl {
8282
nonneg int mIndex{};
8383

8484
/** Bitfield bit count. */
85-
unsigned char mBits{};
85+
short mBits = -1;
8686

8787
// AST..
8888
Token* mAstOperand1{};
@@ -742,11 +742,18 @@ class CPPCHECKLIB Token {
742742
setFlag(fIsInitBracket, b);
743743
}
744744

745+
bool isAnonymous() const {
746+
return getFlag(fIsAnonymous);
747+
}
748+
void isAnonymous(bool b) {
749+
setFlag(fIsAnonymous, b);
750+
}
751+
745752
// cppcheck-suppress unusedFunction
746753
bool isBitfield() const {
747-
return mImpl->mBits > 0;
754+
return mImpl->mBits >= 0;
748755
}
749-
unsigned char bits() const {
756+
short bits() const {
750757
return mImpl->mBits;
751758
}
752759
const std::set<TemplateSimplifier::TokenAndName*>* templateSimplifierPointers() const {
@@ -760,8 +767,14 @@ class CPPCHECKLIB Token {
760767
mImpl->mTemplateSimplifierPointers = new std::set<TemplateSimplifier::TokenAndName*>;
761768
mImpl->mTemplateSimplifierPointers->insert(tokenAndName);
762769
}
763-
void setBits(const unsigned char b) {
764-
mImpl->mBits = b;
770+
bool setBits(const MathLib::bigint b) {
771+
const MathLib::bigint max = std::numeric_limits<short>::max();
772+
if (b > max) {
773+
mImpl->mBits = max;
774+
return false;
775+
}
776+
mImpl->mBits = b < 0 ? -1 : b;
777+
return true;
765778
}
766779

767780
bool isUtf8() const {
@@ -1426,6 +1439,7 @@ class CPPCHECKLIB Token {
14261439
fIsFinalType = (1ULL << 42), // Is this a type with final specifier
14271440
fIsInitComma = (1ULL << 43), // Is this comma located inside some {..}. i.e: {1,2,3,4}
14281441
fIsInitBracket = (1ULL << 44), // Is this bracket used as a part of variable initialization i.e: int a{5}, b(2);
1442+
fIsAnonymous = (1ULL << 45), // Is this a token added for an unnamed member
14291443
};
14301444

14311445
enum : std::uint8_t {

lib/tokenize.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6102,6 +6102,8 @@ void Tokenizer::dump(std::ostream &out) const
61026102
outs += " isAttributeFallthrough=\"true\"";
61036103
if (tok->isInitBracket())
61046104
outs += " isInitBracket=\"true\"";
6105+
if (tok->isAnonymous())
6106+
outs += " isAnonymous=\"true\"";
61056107
if (tok->hasAttributeAlignas()) {
61066108
const std::vector<std::string>& a = tok->getAttributeAlignas();
61076109
outs += " alignas=\"" + ErrorLogger::toxml(a[0]) + "\"";
@@ -9984,12 +9986,8 @@ void Tokenizer::simplifyAt()
99849986
// Simplify bitfields
99859987
void Tokenizer::simplifyBitfields()
99869988
{
9987-
bool goback = false;
9989+
std::size_t anonymousBitfieldCounter = 0;
99889990
for (Token *tok = list.front(); tok; tok = tok->next()) {
9989-
if (goback) {
9990-
goback = false;
9991-
tok = tok->previous();
9992-
}
99939991
Token *last = nullptr;
99949992

99959993
if (Token::simpleMatch(tok, "for ("))
@@ -10010,6 +10008,14 @@ void Tokenizer::simplifyBitfields()
1001010008
}
1001110009
}
1001210010

10011+
const auto tooLargeError = [this](const Token *tok) {
10012+
const MathLib::bigint max = std::numeric_limits<short>::max();
10013+
reportError(tok,
10014+
Severity::warning,
10015+
"tooLargeBitField",
10016+
"Bit-field size exceeds max number of bits " + std::to_string(max));
10017+
};
10018+
1001310019
Token* typeTok = tok->next();
1001410020
while (Token::Match(typeTok, "const|volatile"))
1001510021
typeTok = typeTok->next();
@@ -10018,7 +10024,8 @@ void Tokenizer::simplifyBitfields()
1001810024
!Token::simpleMatch(tok->tokAt(2), "default :")) {
1001910025
Token *tok1 = typeTok->next();
1002010026
if (Token::Match(tok1, "%name% : %num% [;=]"))
10021-
tok1->setBits(static_cast<unsigned char>(MathLib::toBigNumber(tok1->tokAt(2))));
10027+
if (!tok1->setBits(MathLib::toBigNumber(tok1->tokAt(2))))
10028+
tooLargeError(tok1->tokAt(2));
1002210029
if (tok1 && tok1->tokAt(2) &&
1002310030
(Token::Match(tok1->tokAt(2), "%bool%|%num%") ||
1002410031
!Token::Match(tok1->tokAt(2), "public|protected|private| %type% ::|<|,|{|;"))) {
@@ -10039,8 +10046,18 @@ void Tokenizer::simplifyBitfields()
1003910046
}
1004010047
} else if (Token::Match(typeTok, "%type% : %num%|%bool% ;") &&
1004110048
typeTok->str() != "default") {
10042-
tok->deleteNext(4 + tokDistance(tok, typeTok) - 1);
10043-
goback = true;
10049+
const std::size_t id = anonymousBitfieldCounter++;
10050+
const std::string name = "anonymous@" + std::to_string(id);
10051+
Token *newTok = typeTok->insertToken(name);
10052+
newTok->isAnonymous(true);
10053+
bool failed;
10054+
if (newTok->tokAt(2)->isBoolean())
10055+
failed = !newTok->setBits(newTok->strAt(2) == "true");
10056+
else
10057+
failed = !newTok->setBits(MathLib::toBigNumber(newTok->tokAt(2)));
10058+
if (failed)
10059+
tooLargeError(newTok->tokAt(2));
10060+
newTok->deleteNext(2);
1004410061
}
1004510062

1004610063
if (last && last->str() == ",") {

lib/valueflow.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ static Result accumulateStructMembers(const Scope* scope, F f, ValueFlow::Accura
440440
for (const Variable& var : scope->varlist) {
441441
if (var.isStatic())
442442
continue;
443-
const size_t bits = var.nameToken() ? var.nameToken()->bits() : 0;
443+
const MathLib::bigint bits = var.nameToken() ? var.nameToken()->bits() : -1;
444444
if (const ValueType* vt = var.valueType()) {
445445
if (vt->type == ValueType::Type::RECORD && vt->typeScope == scope)
446446
return {0, false};
@@ -455,7 +455,7 @@ static Result accumulateStructMembers(const Scope* scope, F f, ValueFlow::Accura
455455
else
456456
total = f(total, *vt, dim, bits);
457457
}
458-
if (accuracy == ValueFlow::Accuracy::ExactOrZero && total == 0 && bits == 0)
458+
if (accuracy == ValueFlow::Accuracy::ExactOrZero && total == 0 && bits == -1)
459459
return {0, false};
460460
}
461461
return {total, true};
@@ -537,10 +537,17 @@ size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, Accur
537537
if (vt.type == ValueType::Type::RECORD && vt.typeScope) {
538538
size_t currentBitCount = 0;
539539
size_t currentBitfieldAlloc = 0;
540-
auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, size_t bits) -> size_t {
540+
auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, MathLib::bigint bits) -> size_t {
541541
const size_t charBit = settings.platform.char_bit;
542542
size_t n = ValueFlow::getSizeOf(vt2, settings,accuracy, ++maxRecursion);
543543
size_t a = getAlignOf(vt2, settings, accuracy);
544+
if (bits == 0) {
545+
if (currentBitfieldAlloc == 0) {
546+
bits = n * charBit;
547+
} else {
548+
bits = currentBitfieldAlloc * charBit - currentBitCount;
549+
}
550+
}
544551
if (bits > 0) {
545552
size_t ret = total;
546553
if (currentBitfieldAlloc == 0) {
@@ -551,6 +558,10 @@ size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, Accur
551558
currentBitfieldAlloc = n;
552559
currentBitCount = 0;
553560
}
561+
while (bits > charBit * currentBitfieldAlloc) {
562+
ret += currentBitfieldAlloc;
563+
bits -= charBit * currentBitfieldAlloc;
564+
}
554565
currentBitCount += bits;
555566
return ret;
556567
}

test/testconstructors.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ class TestConstructors : public TestFixture {
172172
TEST_CASE(uninitVar33); // ticket #10295
173173
TEST_CASE(uninitVar34); // ticket #10841
174174
TEST_CASE(uninitVar35);
175+
TEST_CASE(uninitVar36);
175176
TEST_CASE(uninitVarEnum1);
176177
TEST_CASE(uninitVarEnum2); // ticket #8146
177178
TEST_CASE(uninitVarStream);
@@ -3020,6 +3021,16 @@ class TestConstructors : public TestFixture {
30203021
ASSERT_EQUALS("", errout_str());
30213022
}
30223023

3024+
void uninitVar36() {
3025+
check("struct S {\n"
3026+
" unsigned int a : 16;\n"
3027+
" unsigned int : 8;\n"
3028+
" unsigned int b : 8;\n"
3029+
" S() : a(0) {}\n"
3030+
"};\n");
3031+
ASSERT_EQUALS("[test.cpp:5:5]: (warning) Member variable 'S::b' is not initialized in the constructor. [uninitMemberVar]\n", errout_str());
3032+
}
3033+
30233034
void uninitVarArray1() {
30243035
check("class John\n"
30253036
"{\n"

0 commit comments

Comments
 (0)