From 110ba00b0b7d4ecf3d726dd2c95037e7d59e92f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 12 Dec 2025 12:51:11 +0100 Subject: [PATCH 1/2] Add tests --- test/testtokenize.cpp | 18 ++++++++++++++++-- test/testunusedvar.cpp | 6 ++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index c9d1b8336ab..8850b55582a 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -276,7 +276,8 @@ class TestTokenizer : public TestFixture { TEST_CASE(functionAttributeListAfter); TEST_CASE(functionAttributeListAfter2); TEST_CASE(cppMaybeUnusedBefore); - TEST_CASE(cppMaybeUnusedAfter); + TEST_CASE(cppMaybeUnusedAfter1); + TEST_CASE(cppMaybeUnusedAfter2); TEST_CASE(cppMaybeUnusedStructuredBinding); TEST_CASE(splitTemplateRightAngleBrackets); @@ -4258,7 +4259,7 @@ class TestTokenizer : public TestFixture { ASSERT(var && var->isAttributeMaybeUnused()); } - void cppMaybeUnusedAfter() { + void cppMaybeUnusedAfter1() { const char code[] = "int var [[maybe_unused]] {};"; const char expected[] = "int var { } ;"; @@ -4271,6 +4272,19 @@ class TestTokenizer : public TestFixture { ASSERT(var && var->isAttributeMaybeUnused()); } + void cppMaybeUnusedAfter2() { + const char code[] = "std::string var [[maybe_unused]];"; + const char expected[] = "std :: string var ;"; + + SimpleTokenizer tokenizer(settings0, *this); + ASSERT(tokenizer.tokenize(code)); + + ASSERT_EQUALS(expected, tokenizer.tokens()->stringifyList(nullptr, false)); + + const Token *var = Token::findsimplematch(tokenizer.tokens(), "var"); + ASSERT(var && var->isAttributeMaybeUnused()); + } + void cppMaybeUnusedStructuredBinding() { const char code[] = "[[maybe_unused]] auto [var1, var2] = f();"; const char expected[] = "auto [ var1 , var2 ] = f ( ) ;"; diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index 5b94a1f7fba..84f21a80585 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -6531,6 +6531,12 @@ class TestUnusedVar : public TestFixture { " [[maybe_unused]] char b[1][2];\n" "}"); ASSERT_EQUALS("", errout_str()); + + functionVariableUsage("int main() {\n" + " std::string a [[maybe_unused]];\n" + " f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); } void localvarrvalue() { // ticket #13977 From fb80be8f748f579b13f129b614c16f6c90cba4ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 12 Dec 2025 12:45:52 +0100 Subject: [PATCH 2/2] Fix #14125 --- lib/tokenize.cpp | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index b48887aea25..d4f4b0c2e71 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -9593,19 +9593,33 @@ void Tokenizer::simplifyCPPAttribute() if (!head) syntaxError(tok); - while (Token::Match(head->next(), "%name%|*|&|&&|const|static|inline|volatile")) - head = head->next(); - if (Token::Match(head, "%name%") && !Token::Match(head, "auto [")) - head->isAttributeMaybeUnused(true); - else if (Token::Match(tok->previous(), "%name%") && Token::Match(tok->link(), "] [;={]")) { - tok->previous()->isAttributeMaybeUnused(true); + if (Token::simpleMatch(head, ";")) { + Token *backTok = tok; + while (backTok && !Token::Match(backTok, "%name%")) { + if (Token::Match(backTok, "]|)")) + backTok = backTok->link(); + backTok = backTok->previous(); + if (Token::Match(backTok, "[;{}]")) + backTok = nullptr; + } + if (backTok) { + backTok->isAttributeMaybeUnused(true); + } } else { - if (Token::simpleMatch(head->next(), "[")) { + while (Token::Match(head->next(), "%name%|::|*|&|&&|const|static|inline|volatile")) head = head->next(); - const Token *end = head->link(); - for (head = head->next(); end && head != end; head = head->next()) { - if (Token::Match(head, "%name%")) { - head->isAttributeMaybeUnused(true); + if (Token::Match(head, "%name%") && !Token::Match(head, "auto [")) + head->isAttributeMaybeUnused(true); + else if (Token::Match(tok->previous(), "%name%") && Token::Match(tok->link(), "] [;={]")) { + tok->previous()->isAttributeMaybeUnused(true); + } else { + if (Token::simpleMatch(head->next(), "[")) { + head = head->next(); + const Token *end = head->link(); + for (head = head->next(); end && head != end; head = head->next()) { + if (Token::Match(head, "%name%")) { + head->isAttributeMaybeUnused(true); + } } } }