fix #10136 (debug: Executable scope 'x' with unknown function.) (#3113)

This commit is contained in:
IOBYTE 2021-02-05 09:50:32 -05:00 committed by GitHub
parent da3eb2e411
commit c70fcf8e2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 246 additions and 52 deletions

View File

@ -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<ScopeInfo3> children;
Type type;
std::string fullName;
const std::string name;
const Token * const bodyStart;
const Token * const bodyEnd;
std::set<std::string> usingNamespaces;
std::set<std::string> recordTypes;
std::set<std::string> 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<ScopeInfo3> &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<ScopeInfo3> *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<std::string> 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<ScopeInfo3> &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<ScopeInfo3>::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<ScopeInfo3> &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<ScopeInfo3> 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<Using> 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, &currentScope);
}
// 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<ScopeInfo3> 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, &currentScope1, 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<ScopeInfo3>::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;
}
}

View File

@ -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<char>;\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<char>&) 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<char>;\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)