Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
57 changes: 32 additions & 25 deletions lib/token.h
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,12 @@ class CPPCHECKLIB Token {
void isAttributeMaybeUnused(const bool value) {
setFlag(fIsAttributeMaybeUnused, value);
}
bool isAttributeFallthrough() const {
return getFlag(fIsAttributeFallthrough);
}
void isAttributeFallthrough(const bool value) {
setFlag(fIsAttributeFallthrough, value);
}
std::vector<std::string> getAttributeAlignas() const {
return mImpl->mAttributeAlignas ? *mImpl->mAttributeAlignas : std::vector<std::string>();
}
Expand Down Expand Up @@ -1394,31 +1400,32 @@ class CPPCHECKLIB Token {
fIsAttributeExport = (1ULL << 16), // __attribute__((__visibility__("default"))), __declspec(dllexport)
fIsAttributeMaybeUnused = (1ULL << 17), // [[maybe_unused]]
fIsAttributeNodiscard = (1ULL << 18), // __attribute__ ((warn_unused_result)), [[nodiscard]]
fIsControlFlowKeyword = (1ULL << 19), // if/switch/while/...
fIsOperatorKeyword = (1ULL << 20), // operator=, etc
fIsComplex = (1ULL << 21), // complex/_Complex type
fIsEnumType = (1ULL << 22), // enumeration type
fIsName = (1ULL << 23),
fIsLiteral = (1ULL << 24),
fIsTemplateArg = (1ULL << 25),
fAtAddress = (1ULL << 26), // @ 0x4000
fIncompleteVar = (1ULL << 27),
fConstexpr = (1ULL << 28),
fExternC = (1ULL << 29),
fIsSplitVarDeclComma = (1ULL << 30), // set to true when variable declarations are split up ('int a,b;' => 'int a; int b;')
fIsSplitVarDeclEq = (1ULL << 31), // set to true when variable declaration with initialization is split up ('int a=5;' => 'int a; a=5;')
fIsImplicitInt = (1ULL << 32), // Is "int" token implicitly added?
fIsInline = (1ULL << 33), // Is this a inline type
fIsTemplate = (1ULL << 34),
fIsSimplifedScope = (1ULL << 35), // scope added when simplifying e.g. if (int i = ...; ...)
fIsRemovedVoidParameter = (1ULL << 36), // A void function parameter has been removed
fIsIncompleteConstant = (1ULL << 37),
fIsRestrict = (1ULL << 38), // Is this a restrict pointer type
fIsAtomic = (1ULL << 39), // Is this a _Atomic declaration
fIsSimplifiedTypedef = (1ULL << 40),
fIsFinalType = (1ULL << 41), // Is this a type with final specifier
fIsInitComma = (1ULL << 42), // Is this comma located inside some {..}. i.e: {1,2,3,4}
fIsInitBracket = (1ULL << 43), // Is this bracket used as a part of variable initialization i.e: int a{5}, b(2);
fIsAttributeFallthrough = (1ULL << 19), // [[__fallthrough__]], [[fallthrough]]
fIsControlFlowKeyword = (1ULL << 20), // if/switch/while/...
fIsOperatorKeyword = (1ULL << 21), // operator=, etc
fIsComplex = (1ULL << 22), // complex/_Complex type
fIsEnumType = (1ULL << 23), // enumeration type
fIsName = (1ULL << 24),
fIsLiteral = (1ULL << 25),
fIsTemplateArg = (1ULL << 26),
fAtAddress = (1ULL << 27), // @ 0x4000
fIncompleteVar = (1ULL << 28),
fConstexpr = (1ULL << 29),
fExternC = (1ULL << 30),
fIsSplitVarDeclComma = (1ULL << 31), // set to true when variable declarations are split up ('int a,b;' => 'int a; int b;')
fIsSplitVarDeclEq = (1ULL << 32), // set to true when variable declaration with initialization is split up ('int a=5;' => 'int a; a=5;')
fIsImplicitInt = (1ULL << 33), // Is "int" token implicitly added?
fIsInline = (1ULL << 34), // Is this a inline type
fIsTemplate = (1ULL << 35),
fIsSimplifedScope = (1ULL << 36), // scope added when simplifying e.g. if (int i = ...; ...)
fIsRemovedVoidParameter = (1ULL << 37), // A void function parameter has been removed
fIsIncompleteConstant = (1ULL << 38),
fIsRestrict = (1ULL << 39), // Is this a restrict pointer type
fIsAtomic = (1ULL << 40), // Is this a _Atomic declaration
fIsSimplifiedTypedef = (1ULL << 41),
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);
};

enum : std::uint8_t {
Expand Down
10 changes: 10 additions & 0 deletions lib/tokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6060,6 +6060,8 @@ void Tokenizer::dump(std::ostream &out) const
outs += " isAttributeMaybeUnused=\"true\"";
if (tok->isAttributeUnused())
outs += " isAttributeUnused=\"true\"";
if (tok->isAttributeFallthrough())
outs += " isAttributeFallthrough=\"true\"";
if (tok->isInitBracket())
outs += " isInitBracket=\"true\"";
if (tok->hasAttributeAlignas()) {
Expand Down Expand Up @@ -9443,6 +9445,14 @@ void Tokenizer::simplifyCPPAttribute()
if (head && head->str() == "(" && TokenList::isFunctionHead(head, "{;")) {
head->previous()->isAttributeNodiscard(true);
}
} else if (Token::findsimplematch(tok->tokAt(2), "fallthrough", tok->link()) || Token::findsimplematch(tok->tokAt(2), "__fallthrough__", tok->link())) {
Token * head = skipCPPOrAlignAttribute(tok)->next();
while (isCPPAttribute(head) || isAlignAttribute(head))
head = skipCPPOrAlignAttribute(head)->next();
if (head && head->str() == ";") // we have semicollon after the attribute which would be removed in 'removeRedundantSemicolons()' so we skip it
Comment thread
danmar marked this conversation as resolved.
Outdated
head = head->next();
if (head)
head->isAttributeFallthrough(true);
} else if ((hasMaybeUnusedUnderscores && Token::findsimplematch(tok->tokAt(2), "__maybe_unused__", tok->link()))
|| (hasMaybeUnused && Token::findsimplematch(tok->tokAt(2), "maybe_unused", tok->link()))) {
Token* head = skipCPPOrAlignAttribute(tok)->next();
Expand Down
23 changes: 23 additions & 0 deletions test/testsymboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ class TestSymbolDatabase : public TestFixture {
TEST_CASE(symboldatabase108);
TEST_CASE(symboldatabase109); // #13553
TEST_CASE(symboldatabase110);
TEST_CASE(symboldatabase111); // [[fallthrough]]

TEST_CASE(createSymbolDatabaseFindAllScopes1);
TEST_CASE(createSymbolDatabaseFindAllScopes2);
Expand Down Expand Up @@ -5782,6 +5783,28 @@ class TestSymbolDatabase : public TestFixture {
ASSERT(B && !B->type());
}

void symboldatabase111() {
GET_SYMBOL_DB("void f(int n) {\n"
" void g(), h(), i();\n"
" switch (n) {\n"
" case 1:\n"
" case 2:\n"
" g();\n"
" [[fallthrough]];\n"
" case 3:\n"
" h();\n"
" break;\n"
" default:\n"
" i();\n"
" break;\n"
" }\n"
"}");
ASSERT(db != nullptr);
ASSERT_EQUALS("", errout_str());
const Token *case3 = Token::findsimplematch(tokenizer.tokens(), "case 3");
ASSERT(case3 && case3->isAttributeFallthrough());
}

void createSymbolDatabaseFindAllScopes1() {
GET_SYMBOL_DB("void f() { union {int x; char *p;} a={0}; }");
ASSERT(db->scopeList.size() == 3);
Expand Down
27 changes: 27 additions & 0 deletions test/testtokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@ class TestTokenizer : public TestFixture {
TEST_CASE(constFunctionPtrTypedef); // #12135

TEST_CASE(simplifyPlatformTypes);

TEST_CASE(dumpFallthrough);
}

#define tokenizeAndStringify(...) tokenizeAndStringify_(__FILE__, __LINE__, __VA_ARGS__)
Expand Down Expand Up @@ -8513,6 +8515,31 @@ class TestTokenizer : public TestFixture {
ASSERT_EQUALS("long f ( ) ;", tokenizeAndStringify(code, true, Platform::Type::Unix64));
}
}

void dumpFallthrough() {
Settings settings;
SimpleTokenizer tokenizer(settings, *this);
const char * code = "void f(int n) {\n"
" void g(), h(), i();\n"
" switch (n) {\n"
" case 1:\n"
" case 2:\n"
" g();\n"
" [[fallthrough]];\n"
" case 3:\n"
" h();\n"
" break;\n"
" default:\n"
" i();\n"
" break;\n"
" }\n"
"}";
ASSERT(tokenizer.tokenize(code, false));
std::ostringstream ostr;
tokenizer.dump(ostr);
const std::string dump = ostr.str();
ASSERT(dump.find(" isAttributeFallthrough=\"true\"") != std::string::npos);
}
};

REGISTER_TEST(TestTokenizer)
Expand Down
Loading