From a4953575f1d5cd18dfbe551da06822e708709ea5 Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Sat, 23 Jan 2021 11:55:25 -0500 Subject: [PATCH] fix #10127 (debug: Executable scope 'x' with unknown function.) (#3077) --- lib/tokenize.cpp | 40 +++++++++++++++++++++++++++++++++++-- test/testsymboldatabase.cpp | 23 ++++++++++++++++++++- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index ddae0d07b..f6e3c5185 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -1996,6 +1996,36 @@ bool Tokenizer::isMemberFunction(const Token *openParen) const isFunctionHead(openParen, "{|:"); } +static bool scopesMatch(const std::string &scope1, const std::string &scope2, const std::list &scopeList) +{ + if (scope1.empty() || scope2.empty()) + return false; + + // check if scopes match + if (scope1 == scope2) + return true; + + if (scopeList.size() < 2) + return false; + + // check if scopes only differ by global qualification + if (scope1 == (":: " + scope2)) { + std::string::size_type end = scope2.find_first_of(' '); + if (end == std::string::npos) + end = scope2.size(); + if ((++scopeList.begin())->name == scope2.substr(0, end)) + return true; + } else if (scope2 == (":: " + scope1)) { + std::string::size_type end = scope1.find_first_of(' '); + if (end == std::string::npos) + end = scope1.size(); + if ((++scopeList.begin())->name == scope1.substr(0, end)) + return true; + } + + return false; +} + bool Tokenizer::simplifyUsing() { bool substitute = false; @@ -2159,7 +2189,7 @@ bool Tokenizer::simplifyUsing() // remove the qualification std::string fullScope = scope; std::string removed; - while (tok1->strAt(-1) == "::") { + while (Token::Match(tok1->tokAt(-2), "%name% ::") && !tok1->tokAt(-2)->isKeyword()) { removed = (tok1->strAt(-2) + " :: ") + removed; if (fullScope == tok1->strAt(-2)) { tok1->deletePrevious(); @@ -2180,6 +2210,12 @@ bool Tokenizer::simplifyUsing() } } + // remove global namespace if present + if (tok1->strAt(-1) == "::") { + removed.insert(0, ":: "); + tok1->deletePrevious(); + } + Token * arrayStart = nullptr; // parse the type @@ -2297,7 +2333,7 @@ bool Tokenizer::simplifyUsing() std::string::size_type idx = removed1.rfind(" ::"); if (idx != std::string::npos) removed1.resize(idx); - if (removed1 == scope && !removed1.empty()) { + if (scopesMatch(removed1, scope, scopeList)) { for (std::list::const_reverse_iterator it = scopeList.crbegin(); it != scopeList.crend(); ++it) { if (it->recordTypes.find(start->str()) != it->recordTypes.end()) { std::string::size_type spaceIdx = 0; diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 90a019233..005857339 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -402,7 +402,8 @@ private: TEST_CASE(findFunction35); TEST_CASE(findFunction36); // #10122 TEST_CASE(findFunction37); // #10124 - TEST_CASE(findFunction38); // #10152 + TEST_CASE(findFunction38); // #10125 + TEST_CASE(findFunction39); // #10127 TEST_CASE(findFunctionContainer); TEST_CASE(findFunctionExternC); TEST_CASE(findFunctionGlobalScope); // ::foo @@ -6322,6 +6323,26 @@ private: ASSERT_EQUALS(6, functok->function()->tokenDef->linenr()); } + void findFunction39() { // #10127 + GET_SYMBOL_DB("namespace external {\n" + " class V {\n" + " public:\n" + " using I = int;\n" + " };\n" + "}\n" + "class A {\n" + " void f(external::V::I);\n" + "};\n" + "using ::external::V;\n" + "void A::f(V::I) {}"); + ASSERT_EQUALS("", errout.str()); + const Token *functok = Token::findsimplematch(tokenizer.tokens(), "f ( int )"); + ASSERT(functok); + ASSERT(functok->function()); + ASSERT(functok->function()->name() == "f"); + ASSERT_EQUALS(8, functok->function()->tokenDef->linenr()); + } + void findFunctionContainer() { { GET_SYMBOL_DB("void dostuff(std::vector v);\n"