Skip to content

Commit fada646

Browse files
authored
Fix #13746: Add [[fallthrough]] attribute to Token and dump (danmar#7443)
1 parent 6d61fb6 commit fada646

File tree

4 files changed

+92
-25
lines changed

4 files changed

+92
-25
lines changed

lib/token.h

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,12 @@ class CPPCHECKLIB Token {
560560
void isAttributeMaybeUnused(const bool value) {
561561
setFlag(fIsAttributeMaybeUnused, value);
562562
}
563+
bool isAttributeFallthrough() const {
564+
return getFlag(fIsAttributeFallthrough);
565+
}
566+
void isAttributeFallthrough(const bool value) {
567+
setFlag(fIsAttributeFallthrough, value);
568+
}
563569
std::vector<std::string> getAttributeAlignas() const {
564570
return mImpl->mAttributeAlignas ? *mImpl->mAttributeAlignas : std::vector<std::string>();
565571
}
@@ -1394,31 +1400,32 @@ class CPPCHECKLIB Token {
13941400
fIsAttributeExport = (1ULL << 16), // __attribute__((__visibility__("default"))), __declspec(dllexport)
13951401
fIsAttributeMaybeUnused = (1ULL << 17), // [[maybe_unused]]
13961402
fIsAttributeNodiscard = (1ULL << 18), // __attribute__ ((warn_unused_result)), [[nodiscard]]
1397-
fIsControlFlowKeyword = (1ULL << 19), // if/switch/while/...
1398-
fIsOperatorKeyword = (1ULL << 20), // operator=, etc
1399-
fIsComplex = (1ULL << 21), // complex/_Complex type
1400-
fIsEnumType = (1ULL << 22), // enumeration type
1401-
fIsName = (1ULL << 23),
1402-
fIsLiteral = (1ULL << 24),
1403-
fIsTemplateArg = (1ULL << 25),
1404-
fAtAddress = (1ULL << 26), // @ 0x4000
1405-
fIncompleteVar = (1ULL << 27),
1406-
fConstexpr = (1ULL << 28),
1407-
fExternC = (1ULL << 29),
1408-
fIsSplitVarDeclComma = (1ULL << 30), // set to true when variable declarations are split up ('int a,b;' => 'int a; int b;')
1409-
fIsSplitVarDeclEq = (1ULL << 31), // set to true when variable declaration with initialization is split up ('int a=5;' => 'int a; a=5;')
1410-
fIsImplicitInt = (1ULL << 32), // Is "int" token implicitly added?
1411-
fIsInline = (1ULL << 33), // Is this a inline type
1412-
fIsTemplate = (1ULL << 34),
1413-
fIsSimplifedScope = (1ULL << 35), // scope added when simplifying e.g. if (int i = ...; ...)
1414-
fIsRemovedVoidParameter = (1ULL << 36), // A void function parameter has been removed
1415-
fIsIncompleteConstant = (1ULL << 37),
1416-
fIsRestrict = (1ULL << 38), // Is this a restrict pointer type
1417-
fIsAtomic = (1ULL << 39), // Is this a _Atomic declaration
1418-
fIsSimplifiedTypedef = (1ULL << 40),
1419-
fIsFinalType = (1ULL << 41), // Is this a type with final specifier
1420-
fIsInitComma = (1ULL << 42), // Is this comma located inside some {..}. i.e: {1,2,3,4}
1421-
fIsInitBracket = (1ULL << 43), // Is this bracket used as a part of variable initialization i.e: int a{5}, b(2);
1403+
fIsAttributeFallthrough = (1ULL << 19), // [[__fallthrough__]], [[fallthrough]]
1404+
fIsControlFlowKeyword = (1ULL << 20), // if/switch/while/...
1405+
fIsOperatorKeyword = (1ULL << 21), // operator=, etc
1406+
fIsComplex = (1ULL << 22), // complex/_Complex type
1407+
fIsEnumType = (1ULL << 23), // enumeration type
1408+
fIsName = (1ULL << 24),
1409+
fIsLiteral = (1ULL << 25),
1410+
fIsTemplateArg = (1ULL << 26),
1411+
fAtAddress = (1ULL << 27), // @ 0x4000
1412+
fIncompleteVar = (1ULL << 28),
1413+
fConstexpr = (1ULL << 29),
1414+
fExternC = (1ULL << 30),
1415+
fIsSplitVarDeclComma = (1ULL << 31), // set to true when variable declarations are split up ('int a,b;' => 'int a; int b;')
1416+
fIsSplitVarDeclEq = (1ULL << 32), // set to true when variable declaration with initialization is split up ('int a=5;' => 'int a; a=5;')
1417+
fIsImplicitInt = (1ULL << 33), // Is "int" token implicitly added?
1418+
fIsInline = (1ULL << 34), // Is this a inline type
1419+
fIsTemplate = (1ULL << 35),
1420+
fIsSimplifedScope = (1ULL << 36), // scope added when simplifying e.g. if (int i = ...; ...)
1421+
fIsRemovedVoidParameter = (1ULL << 37), // A void function parameter has been removed
1422+
fIsIncompleteConstant = (1ULL << 38),
1423+
fIsRestrict = (1ULL << 39), // Is this a restrict pointer type
1424+
fIsAtomic = (1ULL << 40), // Is this a _Atomic declaration
1425+
fIsSimplifiedTypedef = (1ULL << 41),
1426+
fIsFinalType = (1ULL << 42), // Is this a type with final specifier
1427+
fIsInitComma = (1ULL << 43), // Is this comma located inside some {..}. i.e: {1,2,3,4}
1428+
fIsInitBracket = (1ULL << 44), // Is this bracket used as a part of variable initialization i.e: int a{5}, b(2);
14221429
};
14231430

14241431
enum : std::uint8_t {

lib/tokenize.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6060,6 +6060,8 @@ void Tokenizer::dump(std::ostream &out) const
60606060
outs += " isAttributeMaybeUnused=\"true\"";
60616061
if (tok->isAttributeUnused())
60626062
outs += " isAttributeUnused=\"true\"";
6063+
if (tok->isAttributeFallthrough())
6064+
outs += " isAttributeFallthrough=\"true\"";
60636065
if (tok->isInitBracket())
60646066
outs += " isInitBracket=\"true\"";
60656067
if (tok->hasAttributeAlignas()) {
@@ -9443,6 +9445,14 @@ void Tokenizer::simplifyCPPAttribute()
94439445
if (head && head->str() == "(" && TokenList::isFunctionHead(head, "{;")) {
94449446
head->previous()->isAttributeNodiscard(true);
94459447
}
9448+
} else if (Token::findsimplematch(tok->tokAt(2), "fallthrough", tok->link()) || Token::findsimplematch(tok->tokAt(2), "__fallthrough__", tok->link())) {
9449+
Token * head = skipCPPOrAlignAttribute(tok)->next();
9450+
while (isCPPAttribute(head) || isAlignAttribute(head))
9451+
head = skipCPPOrAlignAttribute(head)->next();
9452+
while (head && head->str() == ";") // we have semicollon after the attribute which would be removed in 'removeRedundantSemicolons()' so we skip it
9453+
head = head->next();
9454+
if (head)
9455+
head->isAttributeFallthrough(true);
94469456
} else if ((hasMaybeUnusedUnderscores && Token::findsimplematch(tok->tokAt(2), "__maybe_unused__", tok->link()))
94479457
|| (hasMaybeUnused && Token::findsimplematch(tok->tokAt(2), "maybe_unused", tok->link()))) {
94489458
Token* head = skipCPPOrAlignAttribute(tok)->next();

test/testsymboldatabase.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ class TestSymbolDatabase : public TestFixture {
426426
TEST_CASE(symboldatabase108);
427427
TEST_CASE(symboldatabase109); // #13553
428428
TEST_CASE(symboldatabase110);
429+
TEST_CASE(symboldatabase111); // [[fallthrough]]
429430

430431
TEST_CASE(createSymbolDatabaseFindAllScopes1);
431432
TEST_CASE(createSymbolDatabaseFindAllScopes2);
@@ -5782,6 +5783,28 @@ class TestSymbolDatabase : public TestFixture {
57825783
ASSERT(B && !B->type());
57835784
}
57845785

5786+
void symboldatabase111() {
5787+
GET_SYMBOL_DB("void f(int n) {\n"
5788+
" void g(), h(), i();\n"
5789+
" switch (n) {\n"
5790+
" case 1:\n"
5791+
" case 2:\n"
5792+
" g();\n"
5793+
" [[fallthrough]];\n"
5794+
" case 3:\n"
5795+
" h();\n"
5796+
" break;\n"
5797+
" default:\n"
5798+
" i();\n"
5799+
" break;\n"
5800+
" }\n"
5801+
"}");
5802+
ASSERT(db != nullptr);
5803+
ASSERT_EQUALS("", errout_str());
5804+
const Token *case3 = Token::findsimplematch(tokenizer.tokens(), "case 3");
5805+
ASSERT(case3 && case3->isAttributeFallthrough());
5806+
}
5807+
57855808
void createSymbolDatabaseFindAllScopes1() {
57865809
GET_SYMBOL_DB("void f() { union {int x; char *p;} a={0}; }");
57875810
ASSERT(db->scopeList.size() == 3);

test/testtokenize.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,8 @@ class TestTokenizer : public TestFixture {
477477
TEST_CASE(constFunctionPtrTypedef); // #12135
478478

479479
TEST_CASE(simplifyPlatformTypes);
480+
481+
TEST_CASE(dumpFallthrough);
480482
}
481483

482484
#define tokenizeAndStringify(...) tokenizeAndStringify_(__FILE__, __LINE__, __VA_ARGS__)
@@ -8513,6 +8515,31 @@ class TestTokenizer : public TestFixture {
85138515
ASSERT_EQUALS("long f ( ) ;", tokenizeAndStringify(code, true, Platform::Type::Unix64));
85148516
}
85158517
}
8518+
8519+
void dumpFallthrough() {
8520+
Settings settings;
8521+
SimpleTokenizer tokenizer(settings, *this);
8522+
const char * code = "void f(int n) {\n"
8523+
" void g(), h(), i();\n"
8524+
" switch (n) {\n"
8525+
" case 1:\n"
8526+
" case 2:\n"
8527+
" g();\n"
8528+
" [[fallthrough]];\n"
8529+
" case 3:\n"
8530+
" h();\n"
8531+
" break;\n"
8532+
" default:\n"
8533+
" i();\n"
8534+
" break;\n"
8535+
" }\n"
8536+
"}";
8537+
ASSERT(tokenizer.tokenize(code, false));
8538+
std::ostringstream ostr;
8539+
tokenizer.dump(ostr);
8540+
const std::string dump = ostr.str();
8541+
ASSERT(dump.find(" isAttributeFallthrough=\"true\"") != std::string::npos);
8542+
}
85168543
};
85178544

85188545
REGISTER_TEST(TestTokenizer)

0 commit comments

Comments
 (0)