From 99ce89c003b227689a51b737c6994d81f4d2ae7a Mon Sep 17 00:00:00 2001 From: PKEuS <731902+PKEuS@users.noreply.github.com> Date: Sat, 11 Jun 2022 08:11:16 +0200 Subject: [PATCH] Improvement: Set varId for variables with global scope operator :: Refactorization: Moved internal class from tokenize.h to tokenize.cpp (#4184) Merged from LCppC. --- lib/tokenize.cpp | 199 ++++++++++++++++++++++++++------------------- lib/tokenize.h | 36 ++------ lib/valueflow.cpp | 2 +- test/testvarid.cpp | 24 ++++++ 4 files changed, 147 insertions(+), 114 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 882664267..1ba4a30cd 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -3424,14 +3424,85 @@ void Tokenizer::simplifyTemplates() //--------------------------------------------------------------------------- -static bool setVarIdParseDeclaration(const Token **tok, const std::map &variableId, bool executableScope, bool cpp, bool c) +/** Class used in Tokenizer::setVarIdPass1 */ +class VariableMap { +private: + std::map mVariableId; + std::map mVariableId_global; + std::stack>> mScopeInfo; + mutable nonneg int mVarId; +public: + VariableMap() : mVarId(0) {} + void enterScope(); + bool leaveScope(); + void addVariable(const std::string& varname, bool globalNamespace); + bool hasVariable(const std::string& varname) const { + return mVariableId.find(varname) != mVariableId.end(); + } + std::map::const_iterator find(const std::string& varname, bool global) const { + return map(global).find(varname); + } + std::map::const_iterator end() const { + return mVariableId.end(); + } + const std::map& map(bool global) const { + return global ? mVariableId_global : mVariableId; + } + nonneg int* getVarId() const { + return &mVarId; + } +}; + + +void VariableMap::enterScope() +{ + mScopeInfo.push(std::vector>()); +} + +bool VariableMap::leaveScope() +{ + if (mScopeInfo.empty()) + return false; + + for (const std::pair& outerVariable : mScopeInfo.top()) { + if (outerVariable.second != 0) + mVariableId[outerVariable.first] = outerVariable.second; + else + mVariableId.erase(outerVariable.first); + } + mScopeInfo.pop(); + return true; +} + +void VariableMap::addVariable(const std::string& varname, bool globalNamespace) +{ + if (mScopeInfo.empty()) { + mVariableId[varname] = ++mVarId; + if (globalNamespace) + mVariableId_global[varname] = mVariableId[varname]; + return; + } + std::map::iterator it = mVariableId.find(varname); + if (it == mVariableId.end()) { + mScopeInfo.top().push_back(std::pair(varname, 0)); + mVariableId[varname] = ++mVarId; + if (globalNamespace) + mVariableId_global[varname] = mVariableId[varname]; + return; + } + mScopeInfo.top().push_back(std::pair(varname, it->second)); + it->second = ++mVarId; +} + + +static bool setVarIdParseDeclaration(const Token **tok, const VariableMap& variableMap, bool executableScope, bool cpp, bool c) { const Token *tok2 = *tok; if (!tok2->isName()) return false; - int typeCount = 0; - int singleNameCount = 0; + nonneg int typeCount = 0; + nonneg int singleNameCount = 0; bool hasstruct = false; // Is there a "struct" or "class"? bool bracket = false; bool ref = false; @@ -3450,7 +3521,7 @@ static bool setVarIdParseDeclaration(const Token **tok, const std::mapstr()) != variableId.end() && tok2->previous()->str() != "::") { + } else if (!hasstruct && variableMap.find(tok2->str(), false) != variableMap.end() && tok2->previous()->str() != "::") { ++typeCount; tok2 = tok2->next(); if (!tok2 || tok2->str() != "::") @@ -3554,17 +3625,17 @@ static bool setVarIdParseDeclaration(const Token **tok, const std::map>& structMembers, + std::map>& structMembers, nonneg int *varId) const { Token *tok = *tok1; if (Token::Match(tok, "%name% = { . %name% =|{")) { - const int struct_varid = tok->varId(); + const nonneg int struct_varid = tok->varId(); if (struct_varid == 0) return; - std::map& members = structMembers[struct_varid]; + std::map& members = structMembers[struct_varid]; tok = tok->tokAt(3); while (tok->str() != "}") { @@ -3572,7 +3643,7 @@ void Tokenizer::setVarIdStructMembers(Token **tok1, tok = tok->link(); if (Token::Match(tok->previous(), "[,{] . %name% =|{")) { tok = tok->next(); - const std::map::iterator it = members.find(tok->str()); + const std::map::iterator it = members.find(tok->str()); if (it == members.end()) { members[tok->str()] = ++(*varId); tok->varId(*varId); @@ -3593,7 +3664,7 @@ void Tokenizer::setVarIdStructMembers(Token **tok1, tok = tok->tokAt(3); continue; } - const int struct_varid = tok->varId(); + const nonneg int struct_varid = tok->varId(); tok = tok->tokAt(2); if (struct_varid == 0) continue; @@ -3605,8 +3676,8 @@ void Tokenizer::setVarIdStructMembers(Token **tok1, if (TemplateSimplifier::templateParameters(tok->next()) > 0) break; - std::map& members = structMembers[struct_varid]; - const std::map::iterator it = members.find(tok->str()); + std::map& members = structMembers[struct_varid]; + const std::map::iterator it = members.find(tok->str()); if (it == members.end()) { members[tok->str()] = ++(*varId); tok->varId(*varId); @@ -3622,7 +3693,7 @@ void Tokenizer::setVarIdStructMembers(Token **tok1, void Tokenizer::setVarIdClassDeclaration(const Token * const startToken, const VariableMap &variableMap, const nonneg int scopeStartVarId, - std::map>& structMembers) + std::map>& structMembers) { // end of scope const Token * const endToken = startToken->link(); @@ -3663,7 +3734,7 @@ void Tokenizer::setVarIdClassDeclaration(const Token * const startToken, --indentlevel; inEnum = false; } else if (initList && indentlevel == 0 && Token::Match(tok->previous(), "[,:] %name% [({]")) { - const std::map::const_iterator it = variableMap.find(tok->str()); + const std::map::const_iterator it = variableMap.find(tok->str(), false); if (it != variableMap.end()) { tok->varId(it->second); } @@ -3681,7 +3752,7 @@ void Tokenizer::setVarIdClassDeclaration(const Token * const startToken, } if (!inEnum) { - const std::map::const_iterator it = variableMap.find(tok->str()); + const std::map::const_iterator it = variableMap.find(tok->str(), false); if (it != variableMap.end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); @@ -3700,8 +3771,8 @@ void Tokenizer::setVarIdClassDeclaration(const Token * const startToken, void Tokenizer::setVarIdClassFunction(const std::string &classname, Token * const startToken, const Token * const endToken, - const std::map &varlist, - std::map>& structMembers, + const std::map &varlist, + std::map>& structMembers, nonneg int *varId_) { for (Token *tok2 = startToken; tok2 && tok2 != endToken; tok2 = tok2->next()) { @@ -3716,7 +3787,7 @@ void Tokenizer::setVarIdClassFunction(const std::string &classname, if (Token::Match(tok2, "%name% ::")) continue; - const std::map::const_iterator it = varlist.find(tok2->str()); + const std::map::const_iterator it = varlist.find(tok2->str()); if (it != varlist.end()) { tok2->varId(it->second); setVarIdStructMembers(&tok2, structMembers, varId_); @@ -3755,7 +3826,7 @@ void Tokenizer::setVarIdPass1() const std::unordered_set& notstart = (isC()) ? notstart_c : notstart_cpp; VariableMap variableMap; - std::map> structMembers; + std::map> structMembers; std::stack scopeStack; @@ -3817,8 +3888,8 @@ void Tokenizer::setVarIdPass1() const Token * const startToken = (tok->str() == "{") ? tok : tok->link(); - // parse anonymous unions as part of the current scope - if (!Token::Match(startToken->previous(), "union|struct|enum {") && + // parse anonymous namespaces as part of the current scope + if (!Token::Match(startToken->previous(), "union|struct|enum|namespace {") && !(initlist && Token::Match(startToken->previous(), "%name%|>|>>") && Token::Match(startToken->link(), "} ,|{"))) { if (tok->str() == "{") { @@ -3918,7 +3989,7 @@ void Tokenizer::setVarIdPass1() Token::simpleMatch(tok2->link(), "] =")) { while (tok2 && tok2->str() != "]") { if (Token::Match(tok2, "%name% [,]]")) - variableMap.addVariable(tok2->str()); + variableMap.addVariable(tok2->str(), false); tok2 = tok2->next(); } continue; @@ -3926,7 +3997,7 @@ void Tokenizer::setVarIdPass1() } try { /* Ticket #8151 */ - decl = setVarIdParseDeclaration(&tok2, variableMap.map(), scopeStack.top().isExecutable, isCPP(), isC()); + decl = setVarIdParseDeclaration(&tok2, variableMap, scopeStack.top().isExecutable, isCPP(), isC()); } catch (const Token * errTok) { syntaxError(errTok); } @@ -3935,7 +4006,7 @@ void Tokenizer::setVarIdPass1() if (Token *declTypeTok = Token::findsimplematch(tok, "decltype (", tok2)) { for (Token *declTok = declTypeTok->linkAt(1); declTok != declTypeTok; declTok = declTok->previous()) { if (declTok->isName() && !Token::Match(declTok->previous(), "::|.") && variableMap.hasVariable(declTok->str())) - declTok->varId(variableMap.find(declTok->str())->second); + declTok->varId(variableMap.find(declTok->str(), false)->second); } } } @@ -3994,14 +4065,14 @@ void Tokenizer::setVarIdPass1() decl = false; if (decl) { - variableMap.addVariable(prev2->str()); + variableMap.addVariable(prev2->str(), scopeStack.size() <= 1); if (Token::simpleMatch(tok->previous(), "for (") && Token::Match(prev2, "%name% [=,]")) { for (const Token *tok3 = prev2->next(); tok3 && tok3->str() != ";"; tok3 = tok3->next()) { if (Token::Match(tok3, "[([]")) tok3 = tok3->link(); if (Token::Match(tok3, ", %name% [,=;]")) - variableMap.addVariable(tok3->next()->str()); + variableMap.addVariable(tok3->next()->str(), false); } } @@ -4014,7 +4085,7 @@ void Tokenizer::setVarIdPass1() while (tok != end) { if (tok->isName() && !(Token::simpleMatch(tok->next(), "<") && Token::Match(tok->tokAt(-1), ":: %name%"))) { - const std::map::const_iterator it = variableMap.find(tok->str()); + const std::map::const_iterator it = variableMap.find(tok->str(), false); if (it != variableMap.end()) tok->varId(it->second); } @@ -4032,9 +4103,14 @@ void Tokenizer::setVarIdPass1() if (Token::Match(tok->previous(), "struct|enum|union") || (isCPP() && tok->strAt(-1) == "class")) continue; + bool globalNamespace = false; if (!isC()) { - if (tok->previous() && tok->previous()->str() == "::") - continue; + if (tok->previous() && tok->previous()->str() == "::") { + if (Token::Match(tok->tokAt(-2), ")|]|%name%")) + continue; + else + globalNamespace = true; + } if (tok->next() && tok->next()->str() == "::") continue; if (Token::simpleMatch(tok->tokAt(-2), ":: template")) @@ -4074,13 +4150,13 @@ void Tokenizer::setVarIdPass1() } if (!scopeStack.top().isEnum || !(Token::Match(tok->previous(), "{|,") && Token::Match(tok->next(), ",|=|}"))) { - const std::map::const_iterator it = variableMap.find(tok->str()); - if (it != variableMap.end()) { + const std::map::const_iterator it = variableMap.find(tok->str(), globalNamespace); + if (it != variableMap.map(globalNamespace).end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); } } - } else if (Token::Match(tok, "::|. %name%")) { + } else if (Token::Match(tok, "::|. %name%") && Token::Match(tok->previous(), ")|]|>|%name%")) { // Don't set varid after a :: or . token tok = tok->next(); } else if (tok->str() == ":" && Token::Match(tok->tokAt(-2), "class %type%")) { @@ -4193,7 +4269,7 @@ static Token * matchMemberFunctionName(const Member &func, const std::list> structMembers; + std::map> structMembers; // Member functions and variables in this source std::list allMemberFunctions; @@ -4225,15 +4301,18 @@ void Tokenizer::setVarIdPass2() } Token* const tok1 = tok; - if (Token::Match(tok->previous(), "!!:: %name% :: ~| %name%")) + if (Token::Match(tok, "%name% :: ~| %name%")) tok = tok->next(); - else if (Token::Match(tok->previous(), "!!:: %name% <") && Token::Match(tok->next()->findClosingBracket(),"> :: ~| %name%")) + else if (Token::Match(tok, "%name% <") && Token::Match(tok->next()->findClosingBracket(),"> :: ~| %name%")) tok = tok->next()->findClosingBracket()->next(); else if (usingnamespaces.empty() || tok->varId() || !tok->isName() || tok->isStandardType() || tok->tokType() == Token::eKeyword || tok->tokType() == Token::eBoolean || Token::Match(tok->previous(), ".|namespace|class|struct|&|&&|*|> %name%") || Token::Match(tok->previous(), "%type%| %name% ( %type%|)") || Token::Match(tok, "public:|private:|protected:") || (!tok->next() && Token::Match(tok->previous(), "}|; %name%"))) continue; + if (tok->strAt(-1) == "::" && tok->tokAt(-2) && tok->tokAt(-2)->isName()) + continue; + while (Token::Match(tok, ":: ~| %name%")) { tok = tok->next(); if (tok->str() == "~") @@ -4257,7 +4336,7 @@ void Tokenizer::setVarIdPass2() std::list scopeInfo; // class members.. - std::map> varsByClass; + std::map> varsByClass; for (Token *tok = list.front(); tok; tok = tok->next()) { while (tok->str() == "}" && !scopeInfo.empty() && tok == scopeInfo.back().bodyEnd) scopeInfo.pop_back(); @@ -4288,7 +4367,7 @@ void Tokenizer::setVarIdPass2() for (const Token *it : classnameTokens) classname += (classname.empty() ? "" : " :: ") + it->str(); - std::map &thisClassVars = varsByClass[scopeName2 + classname]; + std::map &thisClassVars = varsByClass[scopeName2 + classname]; while (Token::Match(tokStart, ":|::|,|%name%")) { if (Token::Match(tokStart, "%name% <")) { tokStart = tokStart->next()->findClosingBracket(); @@ -4314,7 +4393,7 @@ void Tokenizer::setVarIdPass2() break; scopeName3.erase(pos + 4); } - const std::map& baseClassVars = varsByClass[baseClassName]; + const std::map& baseClassVars = varsByClass[baseClassName]; thisClassVars.insert(baseClassVars.begin(), baseClassVars.end()); } tokStart = tokStart->next(); @@ -4406,7 +4485,7 @@ void Tokenizer::setVarIdPass2() break; // set varid - const std::map::const_iterator varpos = thisClassVars.find(tok3->str()); + const std::map::const_iterator varpos = thisClassVars.find(tok3->str()); if (varpos != thisClassVars.end()) tok3->varId(varpos->second); @@ -12415,50 +12494,6 @@ void Tokenizer::simplifyNamespaceAliases() } } - -Tokenizer::VariableMap::VariableMap() : mVarId(0) {} - -void Tokenizer::VariableMap::enterScope() -{ - mScopeInfo.push(std::list>()); -} - -bool Tokenizer::VariableMap::leaveScope() -{ - if (mScopeInfo.empty()) - return false; - - for (const std::pair &outerVariable : mScopeInfo.top()) { - if (outerVariable.second != 0) - mVariableId[outerVariable.first] = outerVariable.second; - else - mVariableId.erase(outerVariable.first); - } - mScopeInfo.pop(); - return true; -} - -void Tokenizer::VariableMap::addVariable(const std::string &varname) -{ - if (mScopeInfo.empty()) { - mVariableId[varname] = ++mVarId; - return; - } - std::map::iterator it = mVariableId.find(varname); - if (it == mVariableId.end()) { - mScopeInfo.top().push_back(std::pair(varname, 0)); - mVariableId[varname] = ++mVarId; - return; - } - mScopeInfo.top().push_back(std::pair(varname, it->second)); - it->second = ++mVarId; -} - -bool Tokenizer::VariableMap::hasVariable(const std::string &varname) const -{ - return mVariableId.find(varname) != mVariableId.end(); -} - bool Tokenizer::hasIfdef(const Token *start, const Token *end) const { if (!mPreprocessor) diff --git a/lib/tokenize.h b/lib/tokenize.h index d12cb24b2..ebf2abfff 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -40,6 +40,7 @@ class Token; class TemplateSimplifier; class ErrorLogger; class Preprocessor; +class VariableMap; namespace simplecpp { class TokenList; @@ -59,33 +60,6 @@ class CPPCHECKLIB Tokenizer { friend class TestSimplifyTemplate; friend class TemplateSimplifier; - /** Class used in Tokenizer::setVarIdPass1 */ - class VariableMap { - private: - std::map mVariableId; - std::stack>> mScopeInfo; - mutable nonneg int mVarId; - public: - VariableMap(); - void enterScope(); - bool leaveScope(); - void addVariable(const std::string &varname); - bool hasVariable(const std::string &varname) const; - std::map::const_iterator find(const std::string &varname) const { - return mVariableId.find(varname); - } - std::map::const_iterator end() const { - return mVariableId.end(); - } - const std::map &map() const { - return mVariableId; - } - nonneg int *getVarId() const { - return &mVarId; - } - }; - - public: Tokenizer(); Tokenizer(const Settings * settings, ErrorLogger *errorLogger); @@ -798,17 +772,17 @@ private: void setVarIdClassDeclaration(const Token * const startToken, const VariableMap &variableMap, const nonneg int scopeStartVarId, - std::map>& structMembers); + std::map>& structMembers); void setVarIdStructMembers(Token **tok1, - std::map>& structMembers, + std::map>& structMembers, nonneg int *varId) const; void setVarIdClassFunction(const std::string &classname, Token * const startToken, const Token * const endToken, - const std::map &varlist, - std::map>& structMembers, + const std::map &varlist, + std::map>& structMembers, nonneg int *varId_); /** diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 88f3d68a7..ac85384ad 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -743,7 +743,7 @@ static void setTokenValue(Token* tok, ValueFlow::Value value, const Settings* se if (parent->isUnaryOp("&")) { pvalue.indirect++; setTokenValue(parent, pvalue, settings); - } else if (Token::Match(parent, ". %var%") && parent->astOperand1() == tok) { + } else if (Token::Match(parent, ". %var%") && parent->astOperand1() == tok && parent->astOperand2()) { if (parent->originalName() == "->" && pvalue.indirect > 0) pvalue.indirect--; setTokenValue(parent->astOperand2(), pvalue, settings); diff --git a/test/testvarid.cpp b/test/testvarid.cpp index d977d4afd..9bc608b2b 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -178,6 +178,7 @@ private: TEST_CASE(varid_for_auto_cpp17); TEST_CASE(varid_not); // #9689 'not x' TEST_CASE(varid_declInIfCondition); + TEST_CASE(varid_globalScope); TEST_CASE(varidclass1); TEST_CASE(varidclass2); @@ -2859,6 +2860,29 @@ private: "}")); } + void varid_globalScope() { + const char code1[] = "int a[5];\n" + "namespace Z { struct B { int a[5]; } b; }\n" + "void f() {\n" + " int a[5];\n" + " memset(a, 123, 5);\n" + " memset(::a, 123, 5);\n" + " memset(Z::b.a, 123, 5);\n" + " memset(::Z::b.a, 123, 5);\n" + "}"; + + const char exp1[] = "1: int a@1 [ 5 ] ;\n" + "2: namespace Z { struct B { int a@2 [ 5 ] ; } ; struct B b@3 ; }\n" + "3: void f ( ) {\n" + "4: int a@4 [ 5 ] ;\n" + "5: memset ( a@4 , 123 , 5 ) ;\n" + "6: memset ( :: a@1 , 123 , 5 ) ;\n" + "7: memset ( Z :: b@3 . a , 123 , 5 ) ;\n" + "8: memset ( :: Z :: b@3 . a , 123 , 5 ) ;\n" + "9: }\n"; + ASSERT_EQUALS(exp1, tokenize(code1)); + } + void varidclass1() { const std::string actual = tokenize( "class Fred\n"