diff --git a/lib/checkunusedfunctions.cpp b/lib/checkunusedfunctions.cpp index 593299dc129..bf5b00d0801 100644 --- a/lib/checkunusedfunctions.cpp +++ b/lib/checkunusedfunctions.cpp @@ -82,6 +82,9 @@ void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const Setting if (func->isAttributeConstructor() || func->isAttributeDestructor() || func->type != Function::eFunction || func->isOperator()) continue; + if (func->isAttributeUnused() || func->isAttributeMaybeUnused()) + continue; + if (func->isExtern()) continue; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 1a7cda9593a..c77b70749c6 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -819,6 +819,12 @@ class CPPCHECKLIB Function { bool isAttributeNodiscard() const { return tokenDef->isAttributeNodiscard(); } + bool isAttributeUnused() const { + return tokenDef->isAttributeUnused(); + } + bool isAttributeMaybeUnused() const { + return tokenDef->isAttributeMaybeUnused(); + } bool hasBody() const { return getFlag(fHasBody); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index a1d9842de69..c22877fc1a1 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -9382,6 +9382,8 @@ void Tokenizer::simplifyCPPAttribute() // According to cppreference alignas is a c21 feature however the macro is often available when compiling c11 const bool hasAlignas = ((isCPP() && mSettings.standards.cpp >= Standards::CPP11) || (isC() && mSettings.standards.c >= Standards::C11)); const bool hasCppAttribute = ((isCPP() && mSettings.standards.cpp >= Standards::CPP11) || (isC() && mSettings.standards.c >= Standards::C23)); + const bool hasMaybeUnused =((isCPP() && mSettings.standards.cpp >= Standards::CPP17) || (isC() && mSettings.standards.c >= Standards::C23)); + const bool hasMaybeUnusedUnderscores = (isC() && mSettings.standards.c >= Standards::C23); if (!hasAlignas && !hasCppAttribute) return; @@ -9414,11 +9416,17 @@ void Tokenizer::simplifyCPPAttribute() if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { head->previous()->isAttributeNodiscard(true); } - } else if (Token::findsimplematch(tok->tokAt(2), "maybe_unused", tok->link())) { + } 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(); while (isCPPAttribute(head) || isAlignAttribute(head)) head = skipCPPOrAlignAttribute(head)->next(); head->isAttributeMaybeUnused(true); + } else if (Token::findsimplematch(tok->tokAt(2), "unused", tok->link())) { + Token* head = skipCPPOrAlignAttribute(tok)->next(); + while (isCPPAttribute(head) || isAlignAttribute(head)) + head = skipCPPOrAlignAttribute(head)->next(); + head->isAttributeUnused(true); } else if (Token::Match(tok->previous(), ") [ [ expects|ensures|assert default|audit|axiom| : %name% <|<=|>|>= %num% ] ]")) { const Token *vartok = tok->tokAt(4); if (vartok->str() == ":") diff --git a/test/testunusedfunctions.cpp b/test/testunusedfunctions.cpp index a132d51d958..9fc1ea9140d 100644 --- a/test/testunusedfunctions.cpp +++ b/test/testunusedfunctions.cpp @@ -86,16 +86,18 @@ class TestUnusedFunctions : public TestFixture { TEST_CASE(parensInit); TEST_CASE(typeInCast); TEST_CASE(attributeCleanup); + TEST_CASE(attributeUnused); + TEST_CASE(attributeMaybeUnused); } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) template - void check_(const char* file, int line, const char (&code)[size], Platform::Type platform = Platform::Type::Native, const Settings *s = nullptr) { + void check_(const char* file, int line, const char (&code)[size], Platform::Type platform = Platform::Type::Native, const Settings *s = nullptr, bool cpp = true) { const Settings settings1 = settingsBuilder(s ? *s : settings).platform(platform).build(); // Tokenize.. SimpleTokenizer tokenizer(settings1, *this); - ASSERT_LOC(tokenizer.tokenize(code), file, line); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); // Check for unused functions.. CheckUnusedFunctions checkUnusedFunctions; @@ -794,6 +796,30 @@ class TestUnusedFunctions : public TestFixture { "}\n"); ASSERT_EQUALS("", errout_str()); } + + void attributeUnused() + { + check("[[unused]] void f() {}\n"); + ASSERT_EQUALS("", errout_str()); + + check("[[gnu::unused]] void f() {}\n"); + ASSERT_EQUALS("", errout_str()); + + check("__attribute__((unused)) void f() {}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void attributeMaybeUnused() + { + check("[[__maybe_unused__]] void f() {}\n", Platform::Type::Native, nullptr, false); + ASSERT_EQUALS("", errout_str()); + + check("[[maybe_unused]] void f() {}\n", Platform::Type::Native, nullptr, false); + ASSERT_EQUALS("", errout_str()); + + check("[[maybe_unused]] void f() {}\n"); + ASSERT_EQUALS("", errout_str()); + } }; REGISTER_TEST(TestUnusedFunctions)