From c70fcf8e2b4e70dcb12588e327a05e442f6b06b8 Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Fri, 5 Feb 2021 09:50:32 -0500 Subject: [PATCH] fix #10136 (debug: Executable scope 'x' with unknown function.) (#3113) --- lib/tokenize.cpp | 185 ++++++++++++++++++++++++++----------- test/testsimplifyusing.cpp | 113 ++++++++++++++++++++++ 2 files changed, 246 insertions(+), 52 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index aa299cd3a..acaeeb0f3 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -1746,34 +1746,86 @@ void Tokenizer::simplifyTypedef() namespace { struct ScopeInfo3 { - ScopeInfo3(const std::string &name_, const Token *bodyStart_, const Token *bodyEnd_) - : name(name_), bodyStart(bodyStart_), bodyEnd(bodyEnd_) {} + enum Type { Global, Namespace, Record, MemberFunction, Other }; + ScopeInfo3() : parent(nullptr), type(Global), bodyStart(nullptr), bodyEnd(nullptr) {} + ScopeInfo3(ScopeInfo3 *parent_, Type type_, const std::string &name_, const Token *bodyStart_, const Token *bodyEnd_) + : parent(parent_), type(type_), name(name_), bodyStart(bodyStart_), bodyEnd(bodyEnd_) { + fullName = name; + ScopeInfo3 *scope = parent; + while (!fullName.empty() && scope && scope->parent) { + fullName = scope->name + " :: " + fullName; + scope = scope->parent; + } + } + ScopeInfo3 *parent; + std::list children; + Type type; + std::string fullName; const std::string name; const Token * const bodyStart; const Token * const bodyEnd; std::set usingNamespaces; std::set recordTypes; + std::set baseTypes; + + ScopeInfo3 *addChild(Type type, const std::string &name, const Token *bodyStart, const Token *bodyEnd) + { + children.emplace_back(this, type, name, bodyStart, bodyEnd); + return &children.back(); + } + + bool hasChild(const std::string &name) const + { + for (const auto & child : children) { + if (child.name == name) + return true; + } + return false; + } + + void printOut(const std::string & indent = " ") const + { + std::cerr << indent << "type: " << (type == Global ? "Global" : + type == Namespace ? "Namespace" : + type == Record ? "Record" : + type == MemberFunction ? "MemberFunction" : + type == Other ? "Other" : + "Unknown") << std::endl; + std::cerr << indent << "fullName: " << fullName << std::endl; + std::cerr << indent << "name: " << name << std::endl; + std::cerr << indent << usingNamespaces.size() << " usingNamespaces:"; + for (const auto & usingNamespace : usingNamespaces) + std::cerr << " " << usingNamespace; + std::cerr << std::endl; + std::cerr << indent << baseTypes.size() << " baseTypes:"; + for (const auto & baseType : baseTypes) + std::cerr << " " << baseType; + std::cerr << std::endl; + std::cerr << indent << children.size() << " children:" << std::endl; + size_t i = 0; + for (const auto & child : children) { + std::cerr << indent << "child " << i++ << std::endl; + child.printOut(indent + " "); + } + } + + bool findTypeInBase(const std::string &scope) const + { + if (baseTypes.find(scope) != baseTypes.end()) + return true; + return false; + } }; - std::string getScopeName(const std::list &scopeInfo) - { - std::string ret; - for (const ScopeInfo3 &i : scopeInfo) { - if (!i.name.empty()) - ret += (ret.empty() ? "" : " :: ") + i.name; - } - return ret; - } - - void setScopeInfo(Token *tok, std::list *scopeInfo, bool all = false) + void setScopeInfo(Token *tok, ScopeInfo3 **scopeInfo, bool all = false) { if (!tok) return; - if (tok->str() == "{" && !scopeInfo->empty() && tok == scopeInfo->back().bodyStart) + if (tok->str() == "{" && (*scopeInfo)->parent && tok == (*scopeInfo)->bodyStart) return; - while (tok->str() == "}" && !scopeInfo->empty() && tok == scopeInfo->back().bodyEnd) - scopeInfo->pop_back(); + while (tok->str() == "}" && (*scopeInfo)->parent && tok == (*scopeInfo)->bodyEnd) + *scopeInfo = (*scopeInfo)->parent; if (!Token::Match(tok, "namespace|class|struct|union %name% {|:|::|<")) { // check for using namespace if (Token::Match(tok, "using namespace %name% ;|::")) { @@ -1785,7 +1837,7 @@ namespace { nameSpace += tok1->str(); tok1 = tok1->next(); } - scopeInfo->back().usingNamespaces.insert(nameSpace); + (*scopeInfo)->usingNamespaces.insert(nameSpace); } // check for member function else if (tok->str() == "{") { @@ -1818,13 +1870,13 @@ namespace { scope = tok1->strAt(-3) + " :: " + scope; tok1 = tok1->tokAt(-2); } - scopeInfo->emplace_back(scope, tok, tok->link()); + *scopeInfo = (*scopeInfo)->addChild(ScopeInfo3::MemberFunction, scope, tok, tok->link()); added = true; } } if (all && !added) - scopeInfo->emplace_back("", tok, tok->link()); + *scopeInfo = (*scopeInfo)->addChild(ScopeInfo3::Other, "", tok, tok->link()); } return; } @@ -1836,22 +1888,46 @@ namespace { tok = tok->tokAt(2); classname += " :: " + tok->str(); } + // add record type to scope info if (record) - scopeInfo->back().recordTypes.insert(classname); + (*scopeInfo)->recordTypes.insert(classname); tok = tok->next(); - if (tok && tok->str() == ":") { - while (tok && !Token::Match(tok, ";|{")) - tok = tok->next(); - } + // skip template parameters if (tok && tok->str() == "<") { tok = tok->findClosingBracket(); if (tok) tok = tok->next(); } + + // get base class types + std::set baseTypes; + if (tok && tok->str() == ":") { + do { + tok = tok->next(); + while (Token::Match(tok, "public|protected|private|virtual")) + tok = tok->next(); + std::string base; + while (tok && !Token::Match(tok, ";|,|{")) { + if (!base.empty()) + base += ' '; + base += tok->str(); + tok = tok->next(); + // skip template parameters + if (tok && tok->str() == "<") { + tok = tok->findClosingBracket(); + if (tok) + tok = tok->next(); + } + } + baseTypes.insert(base); + } while (tok && !Token::Match(tok, ";|{")); + } + if (tok && tok->str() == "{") { - scopeInfo->emplace_back(classname, tok, tok->link()); + *scopeInfo = (*scopeInfo)->addChild(record ? ScopeInfo3::Record : ScopeInfo3::Namespace, classname, tok, tok->link()); + (*scopeInfo)->baseTypes = baseTypes; } } @@ -1874,7 +1950,7 @@ namespace { const std::string &scope, Token **tok, const std::string &scope1, - const std::list &scopeList1) + const ScopeInfo3 *scopeInfo) { Token *tok1 = *tok; @@ -1891,8 +1967,8 @@ namespace { if (tok1->strAt(-1) == "using") { // fixme: this is wrong // skip to end of scope - if (scopeList1.back().bodyEnd) - *tok = scopeList1.back().bodyEnd->previous(); + if (scopeInfo->bodyEnd) + *tok = scopeInfo->bodyEnd->previous(); return false; } @@ -1937,19 +2013,26 @@ namespace { if (scope == fullScope1) return true; + // check in base types + if (scopeInfo->findTypeInBase(scope)) + return true; + // check using namespace - for (std::list::const_reverse_iterator it = scopeList1.crbegin(); it != scopeList1.crend(); ++it) { - if (!it->usingNamespaces.empty()) { + const ScopeInfo3 * tempScope = scopeInfo; + while (tempScope) { + //if (!tempScope->parent->usingNamespaces.empty()) { + if (!tempScope->usingNamespaces.empty()) { if (qualification.empty()) { - if (it->usingNamespaces.find(scope) != it->usingNamespaces.end()) + if (tempScope->usingNamespaces.find(scope) != tempScope->usingNamespaces.end()) return true; } else { - for (auto ns : it->usingNamespaces) { + for (auto ns : tempScope->usingNamespaces) { if (scope == ns + " :: " + qualification) return true; } } } + tempScope = tempScope->parent; } std::string newScope1 = scope1; @@ -1996,7 +2079,7 @@ bool Tokenizer::isMemberFunction(const Token *openParen) const isFunctionHead(openParen, "{|:"); } -static bool scopesMatch(const std::string &scope1, const std::string &scope2, const std::list &scopeList) +static bool scopesMatch(const std::string &scope1, const std::string &scope2, const ScopeInfo3 *globalScope) { if (scope1.empty() || scope2.empty()) return false; @@ -2005,21 +2088,18 @@ static bool scopesMatch(const std::string &scope1, const std::string &scope2, co 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)) + if (globalScope->hasChild(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)) + if (globalScope->hasChild(scope1.substr(0, end))) return true; } @@ -2029,7 +2109,8 @@ static bool scopesMatch(const std::string &scope1, const std::string &scope2, co bool Tokenizer::simplifyUsing() { bool substitute = false; - std::list scopeList; + ScopeInfo3 scopeInfo; + ScopeInfo3 *currentScope = &scopeInfo; struct Using { Using(Token *start, Token *end) : startTok(start), endTok(end) { } Token *startTok; @@ -2037,8 +2118,6 @@ bool Tokenizer::simplifyUsing() }; std::list usingList; - scopeList.emplace_back("", nullptr, nullptr); - for (Token *tok = list.front(); tok; tok = tok->next()) { if (mErrorLogger && !list.getFiles().empty()) mErrorLogger->reportProgress(list.getFiles()[0], "Tokenize (using)", tok->progressValue()); @@ -2048,7 +2127,7 @@ bool Tokenizer::simplifyUsing() if (Token::Match(tok, "{|}|namespace|class|struct|union") || Token::Match(tok, "using namespace %name% ;|::")) { - setScopeInfo(tok, &scopeList); + setScopeInfo(tok, ¤tScope); } // skip template declarations @@ -2056,7 +2135,7 @@ bool Tokenizer::simplifyUsing() // add template record type to scope info const Token *end = tok->next()->findClosingBracket(); if (end && Token::Match(end->next(), "class|struct|union %name%")) - scopeList.back().recordTypes.insert(end->strAt(2)); + currentScope->recordTypes.insert(end->strAt(2)); Token *endToken = TemplateSimplifier::findTemplateDeclarationEnd(tok); if (endToken) @@ -2071,11 +2150,11 @@ bool Tokenizer::simplifyUsing() Token::Match(tok->linkAt(2), "] ] = ::| %name%"))))) continue; - std::list scopeList1; - scopeList1.emplace_back("", nullptr, nullptr); + ScopeInfo3 scopeInfo1; + ScopeInfo3 *currentScope1 = &scopeInfo1; std::string name = tok->strAt(1); const Token *nameToken = tok->next(); - std::string scope = getScopeName(scopeList); + std::string scope = currentScope->fullName; Token *usingStart = tok; Token *start; if (tok->strAt(2) == "=") @@ -2159,8 +2238,8 @@ bool Tokenizer::simplifyUsing() for (Token* tok1 = list.front(); tok1; tok1 = tok1->next()) { if ((Token::Match(tok1, "{|}|namespace|class|struct|union") && tok1->strAt(-1) != "using") || Token::Match(tok1, "using namespace %name% ;|::")) { - setScopeInfo(tok1, &scopeList1, true); - scope1 = getScopeName(scopeList1); + setScopeInfo(tok1, ¤tScope1, true); + scope1 = currentScope1->fullName; continue; } @@ -2183,7 +2262,7 @@ bool Tokenizer::simplifyUsing() scope1 += memberFunctionScope(tok1); } - if (!usingMatch(nameToken, scope, &tok1, scope1, scopeList1)) + if (!usingMatch(nameToken, scope, &tok1, scope1, currentScope1)) continue; // remove the qualification @@ -2333,9 +2412,10 @@ bool Tokenizer::simplifyUsing() std::string::size_type idx = removed1.rfind(" ::"); if (idx != std::string::npos) removed1.resize(idx); - 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()) { + if (scopesMatch(removed1, scope, &scopeInfo1)) { + ScopeInfo3 * tempScope = currentScope; + while (tempScope->parent) { + if (tempScope->recordTypes.find(start->str()) != tempScope->recordTypes.end()) { std::string::size_type spaceIdx = 0; std::string::size_type startIdx = 0; while ((spaceIdx = removed1.find(" ", startIdx)) != std::string::npos) { @@ -2351,6 +2431,7 @@ bool Tokenizer::simplifyUsing() break; removed1.resize(idx); + tempScope = tempScope->parent; } } diff --git a/test/testsimplifyusing.cpp b/test/testsimplifyusing.cpp index 2fac16c0b..a30a860cb 100644 --- a/test/testsimplifyusing.cpp +++ b/test/testsimplifyusing.cpp @@ -78,6 +78,7 @@ private: TEST_CASE(simplifyUsing9757); TEST_CASE(simplifyUsing10008); TEST_CASE(simplifyUsing10054); + TEST_CASE(simplifyUsing10136); } std::string tok(const char code[], bool simplify = true, Settings::PlatformType type = Settings::Native, bool debugwarnings = true) { @@ -888,6 +889,118 @@ private: ASSERT_EQUALS("", errout.str()); } } + + void simplifyUsing10136() { + { + const char code[] = "class B {\n" + "public:\n" + " using V = std::vector;\n" + " virtual void f(const V&) const = 0;\n" + "};\n" + "class A final : public B {\n" + "public:\n" + " void f(const V&) const override;\n" + "};\n" + "void A::f(const std::vector&) const { }"; + const char exp[] = "class B { " + "public: " + "virtual void f ( const std :: vector < char > & ) const = 0 ; " + "} ; " + "class A : public B { " + "public: " + "void f ( const std :: vector < char > & ) const override ; " + "} ; " + "void A :: f ( const std :: vector < char > & ) const { }"; + ASSERT_EQUALS(exp, tok(code, true)); + ASSERT_EQUALS("", errout.str()); + } + { + const char code[] = "namespace NS1 {\n" + " class B {\n" + " public:\n" + " using V = std::vector;\n" + " virtual void f(const V&) const = 0;\n" + " };\n" + "}\n" + "namespace NS2 {\n" + " class A : public NS1::B {\n" + " public:\n" + " void f(const V&) const override;\n" + " };\n" + " namespace NS3 {\n" + " class C : public A {\n" + " public:\n" + " void f(const V&) const override;\n" + " };\n" + " void C::f(const V&) const { }\n" + " }\n" + " void A::f(const V&) const { }\n" + "}\n" + "void foo() {\n" + " NS2::A a;\n" + " NS2::NS3::C c;\n" + " NS1::B::V v;\n" + " a.f(v);\n" + " c.f(v);\n" + "}"; + const char exp[] = "namespace NS1 { " + "class B { " + "public: " + "virtual void f ( const std :: vector < char > & ) const = 0 ; " + "} ; " + "} " + "namespace NS2 { " + "class A : public NS1 :: B { " + "public: " + "void f ( const std :: vector < char > & ) const override ; " + "} ; " + "namespace NS3 { " + "class C : public A { " + "public: " + "void f ( const std :: vector < char > & ) const override ; " + "} ; " + "void C :: f ( const std :: vector < char > & ) const { } " + "} " + "void A :: f ( const std :: vector < char > & ) const { } " + "} " + "void foo ( ) { " + "NS2 :: A a ; " + "NS2 :: NS3 :: C c ; " + "std :: vector < char > v ; " + "a . f ( v ) ; " + "c . f ( v ) ; " + "}"; + const char act[] = "namespace NS1 { " + "class B { " + "public: " + "virtual void f ( const std :: vector < char > & ) const = 0 ; " + "} ; " + "} " + "namespace NS2 { " + "class A : public NS1 :: B { " + "public: " + "void f ( const std :: vector < char > & ) const override ; " + "} ; " + "namespace NS3 { " + "class C : public A { " + "public: " + "void f ( const V & ) const override ; " + "} ; " + "void C :: f ( const V & ) const { } " + "} " + "void A :: f ( const V & ) const { } " + "} " + "void foo ( ) { " + "NS2 :: A a ; " + "NS2 :: NS3 :: C c ; " + "std :: vector < char > v ; " + "a . f ( v ) ; " + "c . f ( v ) ; " + "}"; + TODO_ASSERT_EQUALS(exp, act, tok(code, true)); + TODO_ASSERT_EQUALS("", "[test.cpp:20]: (debug) Executable scope 'f' with unknown function.\n", errout.str()); + } + } }; REGISTER_TEST(TestSimplifyUsing)