From 9b7f1f6280aae72ba82a8a5c2949a4b45ef3eceb Mon Sep 17 00:00:00 2001 From: Tetrix Date: Sat, 20 Mar 2021 10:38:47 +0100 Subject: [PATCH] simplifyCPPAttribute tokenizer strips alignas (#3171) --- lib/tokenize.cpp | 119 ++++++++++++++++++++++++------------ test/testsymboldatabase.cpp | 49 +++++++++++++++ test/testtokenize.cpp | 15 +++++ 3 files changed, 143 insertions(+), 40 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 37b192c74..4abaa140e 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -4816,7 +4816,7 @@ bool Tokenizer::simplifyTokenList1(const char FileName[]) // simplify namespace aliases simplifyNamespaceAliases(); - // Remove [[attribute]] + // Remove [[attribute]] and alignas(?) simplifyCPPAttribute(); // remove __attribute__((?)) @@ -9919,6 +9919,21 @@ static bool isCPPAttribute(const Token * tok) return Token::simpleMatch(tok, "[ [") && tok->link() && tok->link()->previous() == tok->linkAt(1); } +static bool isAlignAttribute(const Token * tok) +{ + return Token::simpleMatch(tok, "alignas (") && tok->next()->link(); +} + +static const Token* skipCPPOrAlignAttribute(const Token * tok) +{ + if(isCPPAttribute(tok)) { + return tok->link(); + }else if(isAlignAttribute(tok)) { + return tok->next()->link(); + } + return tok; +} + void Tokenizer::reportUnknownMacros() const { // Report unknown macros used in expressions "%name% %num%" @@ -10182,8 +10197,8 @@ void Tokenizer::findGarbageCode() const continue; } // skip C++ attributes [[...]] - if (isCPP11 && isCPPAttribute(tok)) { - tok = tok->link(); + if (isCPP11 && (isCPPAttribute(tok) || isAlignAttribute(tok)) ) { + tok = skipCPPOrAlignAttribute(tok); continue; } { @@ -10825,48 +10840,72 @@ void Tokenizer::simplifyCPPAttribute() return; for (Token *tok = list.front(); tok; tok = tok->next()) { - if (!isCPPAttribute(tok)) + if (!isCPPAttribute(tok) && !isAlignAttribute(tok)){ continue; - if (Token::Match(tok->tokAt(2), "noreturn|nodiscard")) { - const Token * head = tok->link()->next(); - while (Token::Match(head, "%name%|::|*|&")) + } + if(isCPPAttribute(tok)){ + if (Token::findsimplematch(tok->tokAt(2), "noreturn", tok->link())) { + const Token * head = skipCPPOrAlignAttribute(tok); + while (isCPPAttribute(head) || isAlignAttribute(head)) + head = skipCPPOrAlignAttribute(head); head = head->next(); - if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { - if (tok->strAt(2) == "noreturn") - head->previous()->isAttributeNoreturn(true); - else - head->previous()->isAttributeNodiscard(true); + 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())) { + const Token * head = skipCPPOrAlignAttribute(tok); + while (isCPPAttribute(head) || isAlignAttribute(head)) + head = skipCPPOrAlignAttribute(head); + head = 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())) { + const Token* head = skipCPPOrAlignAttribute(tok); + while (isCPPAttribute(head) || isAlignAttribute(head)) + head = skipCPPOrAlignAttribute(head); + head->next()->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() == ":") + vartok = vartok->next(); + Token *argtok = tok->tokAt(-2); + while (argtok && argtok->str() != "(") { + if (argtok->str() == vartok->str()) + break; + if (argtok->str() == ")") + argtok = argtok->link(); + argtok = argtok->previous(); + } + if (argtok && argtok->str() == vartok->str()) { + if (vartok->next()->str() == ">=") + argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(vartok->strAt(2))); + else if (vartok->next()->str() == ">") + argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(vartok->strAt(2))+1); + else if (vartok->next()->str() == "<=") + argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(vartok->strAt(2))); + else if (vartok->next()->str() == "<") + argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(vartok->strAt(2))-1); + } } - } else if (Token::simpleMatch(tok->tokAt(2), "maybe_unused")) { - Token* head = tok->tokAt(5); - while (isCPPAttribute(head)) - head = head->tokAt(5); - 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() == ":") - vartok = vartok->next(); - Token *argtok = tok->tokAt(-2); - while (argtok && argtok->str() != "(") { - if (argtok->str() == vartok->str()) - break; - if (argtok->str() == ")") - argtok = argtok->link(); - argtok = argtok->previous(); - } - if (argtok && argtok->str() == vartok->str()) { - if (vartok->next()->str() == ">=") - argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(vartok->strAt(2))); - else if (vartok->next()->str() == ">") - argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(vartok->strAt(2))+1); - else if (vartok->next()->str() == "<=") - argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(vartok->strAt(2))); - else if (vartok->next()->str() == "<") - argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(vartok->strAt(2))-1); + } else { + if (Token::simpleMatch(tok, "alignas (")) { + // alignment requirements could be checked here } } - Token::eraseTokens(tok, tok->link()->next()); - tok->deleteThis(); + 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(); + } } } diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 186765b46..4d9e111b0 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -345,6 +345,7 @@ private: TEST_CASE(symboldatabase90); TEST_CASE(symboldatabase91); TEST_CASE(symboldatabase92); // daca crash + TEST_CASE(symboldatabase93); // alignas attribute TEST_CASE(createSymbolDatabaseFindAllScopes1); @@ -426,6 +427,7 @@ private: TEST_CASE(nothrowDeclspecFunction); TEST_CASE(noreturnAttributeFunction); + TEST_CASE(nodiscardAttributeFunction); TEST_CASE(varTypesIntegral); // known integral TEST_CASE(varTypesFloating); // known floating @@ -4689,6 +4691,15 @@ private: ASSERT_EQUALS("", errout.str()); } + void symboldatabase93() { // alignas attribute + GET_SYMBOL_DB("struct alignas(int) A{\n" + "};\n" + ); + ASSERT(db != nullptr); + const Scope* scope = db->findScopeByName("A"); + ASSERT(scope); + } + void createSymbolDatabaseFindAllScopes1() { GET_SYMBOL_DB("void f() { union {int x; char *p;} a={0}; }"); ASSERT(db->scopeList.size() == 3); @@ -6647,6 +6658,44 @@ private: func = findFunctionByName("func4", &db->scopeList.front()); ASSERT_EQUALS(true, func != nullptr); ASSERT_EQUALS(true, func->isAttributeNoreturn()); + + } + + void nodiscardAttributeFunction() { + GET_SYMBOL_DB("[[nodiscard]] int func1();\n" + "int func1() { }\n" + "[[nodiscard]] int func2();\n" + "[[nodiscard]] int func3() { }\n" + "template [[nodiscard]] int func4() { }" + "std::pair [[nodiscard]] func5();\n" + "[[nodiscard]] std::pair func6();\n" + ); + ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS(true, db != nullptr); // not null + + const Function *func = findFunctionByName("func1", &db->scopeList.front()); + ASSERT_EQUALS(true, func != nullptr); + ASSERT_EQUALS(true, func->isAttributeNodiscard()); + + func = findFunctionByName("func2", &db->scopeList.front()); + ASSERT_EQUALS(true, func != nullptr); + ASSERT_EQUALS(true, func->isAttributeNodiscard()); + + func = findFunctionByName("func3", &db->scopeList.front()); + ASSERT_EQUALS(true, func != nullptr); + ASSERT_EQUALS(true, func->isAttributeNodiscard()); + + func = findFunctionByName("func4", &db->scopeList.front()); + ASSERT_EQUALS(true, func != nullptr); + ASSERT_EQUALS(true, func->isAttributeNodiscard()); + + func = findFunctionByName("func5", &db->scopeList.front()); + ASSERT_EQUALS(true, func != nullptr); + ASSERT_EQUALS(true, func->isAttributeNodiscard()); + + func = findFunctionByName("func6", &db->scopeList.front()); + ASSERT_EQUALS(true, func != nullptr); + ASSERT_EQUALS(true, func->isAttributeNodiscard()); } void varTypesIntegral() { diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 635f7613a..d79381a83 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -5690,6 +5690,21 @@ private: ASSERT_EQUALS("struct a ;", tokenizeAndStringify("struct [[,,,]] a;", false, true, Settings::Native, "test.cpp", true)); + + ASSERT_EQUALS("struct a ;", + tokenizeAndStringify("struct alignas(int) a;", false, true, Settings::Native, "test.cpp", true)); + + ASSERT_EQUALS("struct a ;", + tokenizeAndStringify("struct alignas ( alignof ( float ) ) a;", false, true, Settings::Native, "test.cpp", true)); + + ASSERT_EQUALS("char a [ 256 ] ;", + tokenizeAndStringify("alignas(256) char a[256];", false, true, Settings::Native, "test.cpp", true)); + + ASSERT_EQUALS("struct a ;", + tokenizeAndStringify("struct alignas(float) [[deprecated(reason)]] a;", false, true, Settings::Native, "test.cpp", true)); + + ASSERT_EQUALS("struct a ;", + tokenizeAndStringify("struct [[deprecated,maybe_unused]] alignas(double) [[trivial_abi]] a;", false, true, Settings::Native, "test.cpp", true)); } void simplifyCaseRange() {