Fixed #8269 (Tokenizer: wrong varid (using namespace A::B))

This commit is contained in:
Daniel Marjamäki 2017-11-09 23:15:16 +01:00
parent 2408f01cc0
commit 799f953c00
2 changed files with 97 additions and 31 deletions

View File

@ -2860,7 +2860,8 @@ void Tokenizer::setVarIdPass1()
namespace {
struct Member {
Member(const std::list<std::string> &s, Token *t) : scope(s), tok(t) {}
Member(const std::list<std::string> &s, const std::list<const Token *> ns, Token *t) : usingnamespaces(ns), scope(s), tok(t) {}
std::list<const Token *> usingnamespaces;
std::list<std::string> scope;
Token *tok;
};
@ -2880,32 +2881,70 @@ static std::string getScopeName(const std::list<ScopeInfo2> &scopeInfo)
return ret;
}
static Token * matchMemberName(const std::list<std::string> &scope, const Token *nsToken, Token *memberToken, const std::list<ScopeInfo2> &scopeInfo)
{
std::list<ScopeInfo2>::const_iterator scopeIt = scopeInfo.begin();
// Current scope..
for (std::list<std::string>::const_iterator it = scope.begin(); it != scope.end(); ++it) {
if (scopeIt == scopeInfo.end() || scopeIt->name != *it)
return nullptr;
++scopeIt;
}
// using namespace..
if (nsToken) {
while (Token::Match(nsToken, "%name% ::")) {
if (scopeIt != scopeInfo.end() && nsToken->str() == scopeIt->name) {
nsToken = nsToken->tokAt(2);
++scopeIt;
} else {
return nullptr;
}
}
if (!Token::Match(nsToken, "%name% ;"))
return nullptr;
if (scopeIt == scopeInfo.end() || nsToken->str() != scopeIt->name)
return nullptr;
++scopeIt;
}
// Parse member tokens..
while (scopeIt != scopeInfo.end()) {
if (!Token::Match(memberToken, "%name% ::|<"))
return nullptr;
if (memberToken->str() != scopeIt->name)
return nullptr;
if (memberToken->next()->str() == "<") {
memberToken = memberToken->next()->findClosingBracket();
if (!Token::simpleMatch(memberToken, "> ::"))
return nullptr;
}
memberToken = memberToken->tokAt(2);
scopeIt++;
}
return Token::Match(memberToken, "~| %name%") ? memberToken : nullptr;
}
static Token * matchMemberName(const Member &member, const std::list<ScopeInfo2> &scopeInfo)
{
if (scopeInfo.empty())
return nullptr;
std::list<std::string>::const_iterator memberScopeIt = member.scope.begin();
Token *tok2 = member.tok;
for (std::list<ScopeInfo2>::const_iterator it = scopeInfo.begin(); tok2 && it != scopeInfo.end(); ++it) {
if (memberScopeIt != member.scope.end()) {
if (it->name != *memberScopeIt)
return nullptr;
++memberScopeIt;
continue;
}
if (!Token::Match(tok2, "%name% ::|<"))
return nullptr;
if (tok2->str() != it->name)
return nullptr;
if (tok2->next()->str() == "<") {
tok2 = tok2->next()->findClosingBracket();
if (!Token::simpleMatch(tok2, "> ::"))
return nullptr;
}
tok2 = tok2->tokAt(2);
// Does this member match without "using namespace"..
Token *ret = matchMemberName(member.scope, nullptr, member.tok, scopeInfo);
if (ret)
return ret;
// Try to match member using the "using namespace ..." namespaces..
for (std::list<const Token *>::const_iterator ns = member.usingnamespaces.begin(); ns != member.usingnamespaces.end(); ++ns) {
ret = matchMemberName(member.scope, *ns, member.tok, scopeInfo);
if (ret)
return ret;
}
return (memberScopeIt == member.scope.end() && Token::Match(tok2, "~| %name%")) ? tok2 : nullptr;
return nullptr;
}
static Token * matchMemberVarName(const Member &var, const std::list<ScopeInfo2> &scopeInfo)
@ -2917,11 +2956,6 @@ static Token * matchMemberVarName(const Member &var, const std::list<ScopeInfo2>
static Token * matchMemberFunctionName(const Member &func, const std::list<ScopeInfo2> &scopeInfo)
{
Token *tok = matchMemberName(func, scopeInfo);
if (!tok) {
const std::list<std::string> emptyScope;
const Member m2(emptyScope,func.tok);
tok = matchMemberName(m2, scopeInfo);
}
return Token::Match(tok, "~| %name% (") ? tok : nullptr;
}
@ -2935,11 +2969,16 @@ void Tokenizer::setVarIdPass2()
if (!isC()) {
std::map<const Token *, std::string> endOfScope;
std::list<std::string> scope;
std::list<const Token *> usingnamespaces;
for (Token *tok2 = list.front(); tok2; tok2 = tok2->next()) {
if (!tok2->previous() || Token::Match(tok2->previous(), "[;{}]")) {
if (Token::Match(tok2, "using namespace %name% ;"))
scope.push_back(tok2->strAt(2));
else if (Token::Match(tok2, "namespace %name% {")) {
if (Token::Match(tok2, "using namespace %name% ::|;")) {
const Token *endtok = tok2->tokAt(2);
while (Token::Match(endtok, "%name% ::"))
endtok = endtok->tokAt(2);
if (Token::Match(endtok, "%name% ;"))
usingnamespaces.push_back(tok2->tokAt(2));
} else if (Token::Match(tok2, "namespace %name% {")) {
scope.push_back(tok2->strAt(1));
endOfScope[tok2->linkAt(2)] = tok2->strAt(1);
}
@ -2969,9 +3008,9 @@ void Tokenizer::setVarIdPass2()
syntaxError(tok2);
const std::string& str3 = tok3->str();
if (str3 == "(")
allMemberFunctions.push_back(Member(scope, tok2));
allMemberFunctions.push_back(Member(scope, usingnamespaces, tok2));
else if (str3 != "::" && tok2->strAt(-1) != "::") // Support only one depth
allMemberVars.push_back(Member(scope, tok2));
allMemberVars.push_back(Member(scope, usingnamespaces, tok2));
}
}

View File

@ -182,6 +182,7 @@ private:
TEST_CASE(varidnamespace2);
TEST_CASE(usingNamespace1);
TEST_CASE(usingNamespace2);
TEST_CASE(usingNamespace3);
}
std::string tokenize(const char code[], bool simplify = false, const char filename[] = "test.cpp") {
@ -2875,6 +2876,32 @@ private:
"3: void A :: dostuff ( ) { x@1 = 0 ; }\n";
ASSERT_EQUALS(expected, tokenize(code));
}
void usingNamespace3() {
const char code[] = "namespace A {\n"
" namespace B {\n"
" class C {\n"
" double m;\n"
" C();\n"
" };\n"
" }\n"
"}\n"
"using namespace A::B;\n"
"C::C() : m(42) {}";
const char expected[] = "1: namespace A {\n"
"2: namespace B {\n"
"3: class C {\n"
"4: double m@1 ;\n"
"5: C ( ) ;\n"
"6: } ;\n"
"7: }\n"
"8: }\n"
"9: using namespace A :: B ;\n"
"10: C :: C ( ) : m@1 ( 42 ) { }\n";
ASSERT_EQUALS(expected, tokenize(code));
}
};
REGISTER_TEST(TestVarID)