From 163ae86e741de3ac491ba815e7cd248355e5327c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sat, 4 Jan 2025 15:12:21 +0100 Subject: [PATCH 1/5] add test --- test/testunusedfunctions.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/testunusedfunctions.cpp b/test/testunusedfunctions.cpp index a132d51d958..8440f0b88cc 100644 --- a/test/testunusedfunctions.cpp +++ b/test/testunusedfunctions.cpp @@ -86,6 +86,8 @@ 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__) @@ -794,6 +796,27 @@ 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"); + ASSERT_EQUALS("", errout_str()); + + check("[[maybe_unused]] void f() {}\n"); + ASSERT_EQUALS("", errout_str()); + } }; REGISTER_TEST(TestUnusedFunctions) From 34a2277eaec05da49dd775e6eeecd07d18199aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sat, 4 Jan 2025 15:07:51 +0100 Subject: [PATCH 2/5] fix #13272 --- lib/checkunusedfunctions.cpp | 3 +++ lib/symboldatabase.h | 6 ++++++ lib/tokenize.cpp | 11 ++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) 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..7fbafe408f6 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -9414,11 +9414,20 @@ 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 ((Token::findsimplematch(tok->tokAt(2), "maybe_unused", tok->link()) + && (mSettings.standards.c >= Standards::C23 || mSettings.standards.cpp >= Standards::CPP17)) + || (Token::findsimplematch(tok->tokAt(2), "__maybe_unused__", tok->link()) + && mSettings.standards.c >= Standards::C23)) { Token* head = skipCPPOrAlignAttribute(tok)->next(); while (isCPPAttribute(head) || isAlignAttribute(head)) head = skipCPPOrAlignAttribute(head)->next(); head->isAttributeMaybeUnused(true); + } else if (Token::findmatch(tok->tokAt(2), "gnu :: unused", tok->link()) || + 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() == ":") From 14b44168283f74a8de9d9075beca332eeb20c231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sat, 4 Jan 2025 16:48:08 +0100 Subject: [PATCH 3/5] fix warning --- lib/tokenize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 7fbafe408f6..6220ac98797 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -9422,7 +9422,7 @@ void Tokenizer::simplifyCPPAttribute() while (isCPPAttribute(head) || isAlignAttribute(head)) head = skipCPPOrAlignAttribute(head)->next(); head->isAttributeMaybeUnused(true); - } else if (Token::findmatch(tok->tokAt(2), "gnu :: unused", tok->link()) || + } else if (Token::findsimplematch(tok->tokAt(2), "gnu :: unused", tok->link()) || Token::findsimplematch(tok->tokAt(2), "unused", tok->link())) { Token* head = skipCPPOrAlignAttribute(tok)->next(); while (isCPPAttribute(head) || isAlignAttribute(head)) From 8256294969ad8e7d1b7b7f3e853420c1e8aaa1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sun, 5 Jan 2025 14:49:18 +0100 Subject: [PATCH 4/5] properly check language standard --- lib/tokenize.cpp | 8 ++++---- test/testunusedfunctions.cpp | 9 ++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 6220ac98797..0f67a61398f 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,10 +9416,8 @@ void Tokenizer::simplifyCPPAttribute() if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { head->previous()->isAttributeNodiscard(true); } - } else if ((Token::findsimplematch(tok->tokAt(2), "maybe_unused", tok->link()) - && (mSettings.standards.c >= Standards::C23 || mSettings.standards.cpp >= Standards::CPP17)) - || (Token::findsimplematch(tok->tokAt(2), "__maybe_unused__", tok->link()) - && mSettings.standards.c >= Standards::C23)) { + } else if ((Token::findsimplematch(tok->tokAt(2), "maybe_unused", tok->link()) && hasMaybeUnused) + || (Token::findsimplematch(tok->tokAt(2), "__maybe_unused__", tok->link()) && hasMaybeUnusedUnderscores)) { Token* head = skipCPPOrAlignAttribute(tok)->next(); while (isCPPAttribute(head) || isAlignAttribute(head)) head = skipCPPOrAlignAttribute(head)->next(); diff --git a/test/testunusedfunctions.cpp b/test/testunusedfunctions.cpp index 8440f0b88cc..9fc1ea9140d 100644 --- a/test/testunusedfunctions.cpp +++ b/test/testunusedfunctions.cpp @@ -92,12 +92,12 @@ class TestUnusedFunctions : public TestFixture { #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; @@ -811,7 +811,10 @@ class TestUnusedFunctions : public TestFixture { void attributeMaybeUnused() { - check("[[__maybe_unused__]] void f() {}\n"); + 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"); From ecaae0797fbfe0b8424a42e5b4ce23fb87bb92a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sun, 5 Jan 2025 17:30:09 +0100 Subject: [PATCH 5/5] use short circuit, remove redundant match --- lib/tokenize.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 0f67a61398f..c22877fc1a1 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -9416,14 +9416,13 @@ void Tokenizer::simplifyCPPAttribute() if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { head->previous()->isAttributeNodiscard(true); } - } else if ((Token::findsimplematch(tok->tokAt(2), "maybe_unused", tok->link()) && hasMaybeUnused) - || (Token::findsimplematch(tok->tokAt(2), "__maybe_unused__", tok->link()) && hasMaybeUnusedUnderscores)) { + } 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), "gnu :: unused", tok->link()) || - Token::findsimplematch(tok->tokAt(2), "unused", tok->link())) { + } else if (Token::findsimplematch(tok->tokAt(2), "unused", tok->link())) { Token* head = skipCPPOrAlignAttribute(tok)->next(); while (isCPPAttribute(head) || isAlignAttribute(head)) head = skipCPPOrAlignAttribute(head)->next();