From 4183336dc17a17d3c29cea946eae55acd91634ca Mon Sep 17 00:00:00 2001 From: tsteven4 <13596209+tsteven4@users.noreply.github.com> Date: Mon, 17 Apr 2023 12:36:25 -0600 Subject: [PATCH] fix skipping of cpp attributes. (#4971) * fix skipping of cpp attributes. * fix simplifyCPPAttribute loop. When the first token was the start of a c++ attribute on a function, so that tok->previous() was nullptr at the bottom of the loop, an extra token would be skipped. This could result in the corresponding function being omitted from the symbol table. * fix alignas test failure, enhance cpp attr test. * uncrustify. * fix redundantNextPrevious * delete redundant code. * add some tokenizer tests for simplifyCPPAttribute. * enhance noreturn symbol test. The order of a noreturn attribute and another attribute used to matter. Test both orders. --- lib/tokenize.cpp | 28 ++++++++++------------------ test/testsymboldatabase.cpp | 13 ++++++++++++- test/testtokenize.cpp | 18 ++++++++++++++++++ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 6a4672e56..e1d4b9f42 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -8825,36 +8825,35 @@ void Tokenizer::simplifyCPPAttribute() if (mSettings->standards.cpp < Standards::CPP11 || isC()) return; - for (Token *tok = list.front(); tok; tok = tok->next()) { + for (Token *tok = list.front(); tok;) { if (!isCPPAttribute(tok) && !isAlignAttribute(tok)) { + tok = tok->next(); continue; } if (isCPPAttribute(tok)) { if (Token::findsimplematch(tok->tokAt(2), "noreturn", tok->link())) { - Token * head = skipCPPOrAlignAttribute(tok); + Token * head = skipCPPOrAlignAttribute(tok)->next(); while (isCPPAttribute(head) || isAlignAttribute(head)) - head = skipCPPOrAlignAttribute(head); - head = head->next(); + head = skipCPPOrAlignAttribute(head)->next(); while (Token::Match(head, "%name%|::|*|&|<|>|,")) // skip return type head = head->next(); if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { head->previous()->isAttributeNoreturn(true); } } else if (Token::findsimplematch(tok->tokAt(2), "nodiscard", tok->link())) { - Token * head = skipCPPOrAlignAttribute(tok); + Token * head = skipCPPOrAlignAttribute(tok)->next(); while (isCPPAttribute(head) || isAlignAttribute(head)) - head = skipCPPOrAlignAttribute(head); - head = head->next(); + head = skipCPPOrAlignAttribute(head)->next(); while (Token::Match(head, "%name%|::|*|&|<|>|,")) head = head->next(); if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { head->previous()->isAttributeNodiscard(true); } } else if (Token::findsimplematch(tok->tokAt(2), "maybe_unused", tok->link())) { - Token* head = skipCPPOrAlignAttribute(tok); + Token* head = skipCPPOrAlignAttribute(tok)->next(); while (isCPPAttribute(head) || isAlignAttribute(head)) - head = skipCPPOrAlignAttribute(head); - head->next()->isAttributeMaybeUnused(true); + head = skipCPPOrAlignAttribute(head)->next(); + head->isAttributeMaybeUnused(true); } else if (Token::Match(tok->previous(), ") [ [ expects|ensures|assert default|audit|axiom| : %name% <|<=|>|>= %num% ] ]")) { const Token *vartok = tok->tokAt(4); if (vartok->str() == ":") @@ -8884,14 +8883,7 @@ void Tokenizer::simplifyCPPAttribute() } } Token::eraseTokens(tok, skipCPPOrAlignAttribute(tok)->next()); - // fix iterator after removing - if (tok->previous()) { - tok = tok->previous(); - tok->next()->deleteThis(); - } else { - tok->deleteThis(); - tok = list.front(); - } + tok->deleteThis(); } } diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 9b144fcb6..f229285f8 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -7408,7 +7408,10 @@ private: "void func1() { }\n" "[[noreturn]] void func2();\n" "[[noreturn]] void func3() { }\n" - "template [[noreturn]] void func4() { }"); + "template [[noreturn]] void func4() { }\n" + "[[noreturn]] [[gnu::format(printf, 1, 2)]] void func5(const char*, ...);\n" + "[[gnu::format(printf, 1, 2)]] [[noreturn]] void func6(const char*, ...);\n" + ); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null @@ -7428,6 +7431,14 @@ private: ASSERT_EQUALS(true, func != nullptr); ASSERT_EQUALS(true, func->isAttributeNoreturn()); + func = findFunctionByName("func5", &db->scopeList.front()); + ASSERT_EQUALS(true, func != nullptr); + ASSERT_EQUALS(true, func->isAttributeNoreturn()); + + func = findFunctionByName("func6", &db->scopeList.front()); + ASSERT_EQUALS(true, func != nullptr); + ASSERT_EQUALS(true, func->isAttributeNoreturn()); + } void nodiscardAttributeFunction() { diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 54ad99b7a..117784f8a 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -5691,6 +5691,24 @@ private: ASSERT_EQUALS("struct a ;", tokenizeAndStringify("struct [[deprecated,maybe_unused]] alignas(double) [[trivial_abi]] a;")); + + ASSERT_EQUALS("void func5 ( const char * , ... ) ;", + tokenizeAndStringify("[[noreturn]] void func5(const char*, ...);")); + + ASSERT_EQUALS("void func5 ( const char * , ... ) ;", + tokenizeAndStringify("[[noreturn]] [[gnu::format(printf, 1, 2)]] void func5(const char*, ...);")); + + ASSERT_EQUALS("void func5 ( const char * , ... ) ;", + tokenizeAndStringify("[[gnu::format(printf, 1, 2)]] [[noreturn]] void func5(const char*, ...);")); + + ASSERT_EQUALS("int func1 ( ) ;", + tokenizeAndStringify("[[nodiscard]] int func1();")); + + ASSERT_EQUALS("int func1 ( ) ;", + tokenizeAndStringify("[[nodiscard]] [[clang::optnone]] int func1();")); + + ASSERT_EQUALS("int func1 ( ) ;", + tokenizeAndStringify("[[clang::optnone]] [[nodiscard]] int func1();")); } void simplifyCaseRange() {