From 799f953c008fbeadfdfb46bb8301b9690f33b181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 9 Nov 2017 23:15:16 +0100 Subject: [PATCH] Fixed #8269 (Tokenizer: wrong varid (using namespace A::B)) --- lib/tokenize.cpp | 101 +++++++++++++++++++++++++++++++-------------- test/testvarid.cpp | 27 ++++++++++++ 2 files changed, 97 insertions(+), 31 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 2bc2b159f..c995e5fb0 100755 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -2860,7 +2860,8 @@ void Tokenizer::setVarIdPass1() namespace { struct Member { - Member(const std::list &s, Token *t) : scope(s), tok(t) {} + Member(const std::list &s, const std::list ns, Token *t) : usingnamespaces(ns), scope(s), tok(t) {} + std::list usingnamespaces; std::list scope; Token *tok; }; @@ -2880,32 +2881,70 @@ static std::string getScopeName(const std::list &scopeInfo) return ret; } +static Token * matchMemberName(const std::list &scope, const Token *nsToken, Token *memberToken, const std::list &scopeInfo) +{ + std::list::const_iterator scopeIt = scopeInfo.begin(); + + // Current scope.. + for (std::list::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 &scopeInfo) { if (scopeInfo.empty()) return nullptr; - std::list::const_iterator memberScopeIt = member.scope.begin(); - Token *tok2 = member.tok; - for (std::list::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_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 &scopeInfo) @@ -2917,11 +2956,6 @@ static Token * matchMemberVarName(const Member &var, const std::list static Token * matchMemberFunctionName(const Member &func, const std::list &scopeInfo) { Token *tok = matchMemberName(func, scopeInfo); - if (!tok) { - const std::list 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 endOfScope; std::list scope; + std::list 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)); } } diff --git a/test/testvarid.cpp b/test/testvarid.cpp index 0d80b6f3a..732277bd9 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -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)