diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 6204325b0..021152220 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -20,6 +20,7 @@ #include "mathlib.h" #include "token.h" #include "tokenlist.h" +#include "tokenize.h" #include "errorlogger.h" #include "settings.h" #include @@ -126,10 +127,9 @@ const Token* TemplateSimplifier::hasComplicatedSyntaxErrorsInTemplates(Token *to tok = tok->link(); // skip executing scopes.. - if (Token::simpleMatch(tok, ") {") || Token::Match(tok, ") %var% {") || Token::Match(tok, "[;,=] {")) { - while (tok->str() != "{") - tok = tok->next(); - tok = tok->link(); + const Token *start = Tokenizer::startOfExecutableScope(tok); + if (start) { + tok = start->link(); } // skip executing scopes (ticket #1985).. @@ -342,7 +342,8 @@ bool TemplateSimplifier::removeTemplate(Token *tok) // don't remove constructor if (tok2->str() == "explicit" || - (countgt == 1 && Token::Match(tok2->previous(), "> %type% (") && Token::simpleMatch(tok2->next()->link(), ") {"))) { + (countgt == 1 && Token::Match(tok2->previous(), "> %type% (") && + Tokenizer::startOfExecutableScope(const_cast(tok2->next()->link())))) { Token::eraseTokens(tok, tok2); tok->deleteThis(); return true; diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 3ade24267..c6cb3a6bd 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -2086,6 +2086,82 @@ static Token *skipTernaryOp(Token *tok) return tok; } +Token * Tokenizer::startOfFunction(Token * tok) +{ + if (tok && tok->str() == ")") { + tok = tok->next(); + while (tok && tok->str() != "{") { + if (tok->str() == "const" || tok->str() == "volatile") { + tok = tok->next(); + } else if (tok->str() == "noexcept") { + tok = tok->next(); + if (tok && tok->str() == "(") { + tok = tok->link()->next(); + } + } else if (tok->str() == "throw" && tok->next() && tok->next()->str() == "(") { + tok = tok->next()->link()->next(); + } + // unknown macros ") MACRO {" and ") MACRO(...) {" + else if (tok->isUpperCaseName()) { + tok = tok->next(); + if (tok && tok->str() == "(") { + tok = tok->link()->next(); + } + } else + return nullptr; + } + + return tok; + } + + return nullptr; +} + +const Token * Tokenizer::startOfExecutableScope(const Token * tok) +{ + if (tok && tok->str() == ")") { + tok = tok->next(); + bool inInit = false; + while (tok && tok->str() != "{") { + if (!inInit) { + if (tok->str() == "const" || tok->str() == "volatile") { + tok = tok->next(); + } else if (tok->str() == "noexcept") { + tok = tok->next(); + if (tok && tok->str() == "(") { + tok = tok->link()->next(); + } + } else if (tok->str() == "throw" && tok->next() && tok->next()->str() == "(") { + tok = tok->next()->link()->next(); + } else if (tok->str() == ":") { + inInit = true; + tok = tok->next(); + } + // unknown macros ") MACRO {" and ") MACRO(...) {" + else if (tok->isUpperCaseName()) { + tok = tok->next(); + if (tok && tok->str() == "(") { + tok = tok->link()->next(); + } + } else + return nullptr; + } else { + if (tok->isName() && tok->next() && tok->next()->str() == "(") { + tok = tok->next()->link()->next(); + } else if (tok->str() == ",") { + tok = tok->next(); + } else + return nullptr; + } + } + + return tok; + } + + return nullptr; +} + + /** simplify labels and case|default in the code: add a ";" if not already in.*/ void Tokenizer::simplifyLabelsCaseDefault() @@ -2094,10 +2170,9 @@ void Tokenizer::simplifyLabelsCaseDefault() unsigned int indentlevel = 0; for (Token *tok = list.front(); tok; tok = tok->next()) { // Simplify labels in the executable scope.. - if (Token::Match(tok, ") const| {")) { - tok = tok->next(); - if (tok->str() != "{") - tok = tok->next(); + Token *start = startOfExecutableScope(tok); + if (start) { + tok = start; executablescope = true; } @@ -2607,10 +2682,9 @@ void Tokenizer::setVarId() break; // If this is a function implementation.. add it to funclist - if (Token::Match(tok2, ") const|volatile| {")) { - while (tok2->str() != "{") - tok2 = tok2->next(); - setVarIdClassFunction(classname, tok2, tok2->link(), varlist, &structMembers, &_varId); + Token * start = startOfFunction(tok2); + if (start) { + setVarIdClassFunction(classname, start, start->link(), varlist, &structMembers, &_varId); } // constructor with initializer list @@ -3764,11 +3838,11 @@ void Tokenizer::removeRedundantAssignment() if (tok->str() == "{") tok = tok->link(); - if (Token::Match(tok, ") const| {")) { + Token * start = startOfExecutableScope(tok); + if (start) { + tok = start->previous(); // parse in this function.. std::set localvars; - if (tok->next()->str() == "const") - tok = tok->next(); const Token * const end = tok->next()->link(); for (Token *tok2 = tok->next(); tok2 && tok2 != end; tok2 = tok2->next()) { // skip local class or struct @@ -6040,9 +6114,9 @@ bool Tokenizer::simplifyCAlternativeTokens() { bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { - if (Token::Match(tok, ") const| {")) { // Check for executable scope - while (tok->str() != "{") - tok = tok->next(); + Token *start = startOfExecutableScope(tok); + if (start) { // Check for executable scope + tok = start; Token * const end = tok->link(); for (Token *tok2 = tok->next(); tok2 && tok2 != end; tok2 = tok2->next()) { if (Token::Match(tok2, "%var%|%num%|)|] %any% %var%|%num%|(")) { @@ -6244,9 +6318,11 @@ bool Tokenizer::simplifyKnownVariables() // auto variables.. for (Token *tok = list.front(); tok; tok = tok->next()) { // Search for a block of code - if (! Token::Match(tok, ") const| {")) + Token *start = startOfExecutableScope(tok); + if (!start) continue; + tok = start; // parse the block of code.. int indentlevel = 0; Token *tok2 = tok; @@ -7061,10 +7137,10 @@ void Tokenizer::simplifyReference() for (Token *tok = list.front(); tok; tok = tok->next()) { // starting executable scope.. - if (Token::Match(tok, ") const| {")) { + Token *start = startOfExecutableScope(tok); + if (start) { + tok = start; // replace references in this scope.. - if (tok->next()->str() != "{") - tok = tok->next(); Token * const end = tok->next()->link(); for (Token *tok2 = tok; tok2 && tok2 != end; tok2 = tok2->next()) { // found a reference.. diff --git a/lib/tokenize.h b/lib/tokenize.h index b15ef0b6d..cd551318c 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -776,6 +776,14 @@ public: */ static bool isTwoNumber(const std::string &s); + /** + * Helper function to check for start of function execution scope. + * Do not use this in checks. Use the symbol database. + * @param tok --> pointer to end parentheses of parameter list + * @return pointer to start brace of function scope or nullptr if not start. + */ + static const Token * startOfExecutableScope(const Token * tok); + private: /** Disable copy constructor, no implementation */ Tokenizer(const Tokenizer &); @@ -783,6 +791,11 @@ private: /** Disable assignment operator, no implementation */ Tokenizer &operator=(const Tokenizer &); + static Token * startOfFunction(Token * tok); + static Token * startOfExecutableScope(Token * tok) { + return const_cast(startOfExecutableScope(const_cast(tok))); + } + /** settings */ const Settings * _settings; diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index f46392236..9d75f7742 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -575,6 +575,8 @@ private: TEST_CASE(astunaryop); TEST_CASE(astfunction); TEST_CASE(asttemplate); + + TEST_CASE(startOfExecutableScope); } std::string tokenizeAndStringify(const char code[], bool simplify = false, bool expand = true, Settings::PlatformType platform = Settings::Unspecified, const char* filename = "test.cpp", bool cpp11 = true) { @@ -739,11 +741,11 @@ private: // ticket #2118 - invalid syntax error void tokenize12() { - tokenizeAndStringify("Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {\n" - " for (unsigned i = 0 ; i < count; i++) {\n" - " }\n" - "});"); - ASSERT_EQUALS("", errout.str()); + const char code[] = "Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {\n" + " for (unsigned i = 0 ; i < count; i++) {\n" + " }\n" + "});"; + ASSERT_THROW(tokenizeAndStringify(code), InternalError); } // bailout if there is "@" - it is not handled well @@ -10463,6 +10465,35 @@ private: tokenizeAndStringify(code.c_str()); // just survive... } + bool isStartOfExecutableScope(int offset, const char code[]) { + const Settings settings; + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + return Tokenizer::startOfExecutableScope(tokenizer.tokens()->tokAt(offset)) != nullptr; + } + + void startOfExecutableScope() { + ASSERT(isStartOfExecutableScope(3, "void foo() { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() const { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() volatile { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() noexcept { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() NOEXCEPT { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() CONST NOEXCEPT { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() const noexcept { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() noexcept(true) { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() const noexcept(true) { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() throw() { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() THROW() { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() CONST THROW() { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() const throw() { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() throw(int) { }")); + ASSERT(isStartOfExecutableScope(3, "void foo() const throw(int) { }")); + ASSERT(isStartOfExecutableScope(2, "foo() : a(1) { }")); + ASSERT(isStartOfExecutableScope(2, "foo() : a(1), b(2) { }")); + } + }; REGISTER_TEST(TestTokenizer)