template simplifier: improve namespace matching

* out of line member functions are a namespace
* template<...> and *_cast<> can't be instantiations
* refactor code to use less function parameters
* fix instantiation scopes
* use full name with namespace when available
* fallback to just matching names when full name doesn't match
This commit is contained in:
Robert Reif 2019-01-14 14:36:23 -05:00 committed by Daniel Marjamäki
parent 37f2aa0e2d
commit 6ef8dad459
2 changed files with 147 additions and 51 deletions

View File

@ -63,8 +63,17 @@ namespace {
}; };
} }
TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s, const std::string &n, const Token *nt, const Token *pe) : TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s) :
token(tok), scope(s), name(n), fullName(s.empty() ? n : (s + " :: " + name)), nameToken(nt), paramEnd(pe), flags(0) token(tok), scope(s), name(tok ? tok->str() : ""), fullName(s.empty() ? name : (s + " :: " + name)),
nameToken(nullptr), paramEnd(nullptr), flags(0)
{
if (token)
token->templateSimplifierPointer(this);
}
TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s, const Token *nt, const Token *pe) :
token(tok), scope(s), name(nt->str()), fullName(s.empty() ? name : (s + " :: " + name)),
nameToken(nt), paramEnd(pe), flags(0)
{ {
// only set flags for declaration // only set flags for declaration
if (token && nameToken && paramEnd) { if (token && nameToken && paramEnd) {
@ -474,12 +483,45 @@ static std::string getScopeName(const std::list<ScopeInfo2> &scopeInfo)
return ret; return ret;
} }
static void setScopeInfo(const Token *tok, std::list<ScopeInfo2> *scopeInfo) static void setScopeInfo(Token *tok, std::list<ScopeInfo2> *scopeInfo)
{ {
while (tok->str() == "}" && !scopeInfo->empty() && tok == scopeInfo->back().bodyEnd) while (tok->str() == "}" && !scopeInfo->empty() && tok == scopeInfo->back().bodyEnd)
scopeInfo->pop_back(); scopeInfo->pop_back();
if (!Token::Match(tok, "namespace|class|struct|union %name% {|:|::")) if (!Token::Match(tok, "namespace|class|struct|union %name% {|:|::")) {
// check for member function
if (tok->str() == "{") {
Token *tok1 = tok;
while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept"))
tok1 = tok1->previous();
if (tok1->strAt(-1) == ")") {
tok1 = tok1->linkAt(-1);
if (Token::Match(tok1->previous(), "throw|noexcept")) {
tok1 = tok1->previous();
while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept"))
tok1 = tok1->previous();
if (tok1->strAt(-1) != ")")
return;
} else if (Token::Match(tok->tokAt(-2), ":|, %name%")) {
tok1 = tok1->tokAt(-2);
if (tok1->strAt(-1) != ")")
return;
}
if (tok1->strAt(-1) == ">")
tok1 = tok1->previous()->findOpeningBracket();
if (Token::Match(tok1->tokAt(-3), "%name% :: %name%")) {
tok1 = tok1->tokAt(-2);
std::string scope = tok1->strAt(-1);
while (Token::Match(tok1->tokAt(-2), ":: %name%")) {
scope = tok1->strAt(-3) + " :: " + scope;
tok1 = tok1->tokAt(-2);
}
scopeInfo->emplace_back(scope, tok->link());
}
}
}
return; return;
}
tok = tok->next(); tok = tok->next();
std::string classname = tok->str(); std::string classname = tok->str();
while (Token::Match(tok, "%name% :: %name%")) { while (Token::Match(tok, "%name% :: %name%")) {
@ -501,7 +543,7 @@ bool TemplateSimplifier::getTemplateDeclarations()
bool codeWithTemplates = false; bool codeWithTemplates = false;
std::list<ScopeInfo2> scopeInfo; std::list<ScopeInfo2> scopeInfo;
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok, "}|namespace|class|struct|union")) { if (Token::Match(tok, "{|}|namespace|class|struct|union")) {
setScopeInfo(tok, &scopeInfo); setScopeInfo(tok, &scopeInfo);
continue; continue;
} }
@ -527,14 +569,14 @@ bool TemplateSimplifier::getTemplateDeclarations()
else if (tok2->str() == ";") { else if (tok2->str() == ";") {
const int namepos = getTemplateNamePosition(parmEnd, true); const int namepos = getTemplateNamePosition(parmEnd, true);
if (namepos > 0) if (namepos > 0)
mTemplateForwardDeclarations.emplace_back(tok, getScopeName(scopeInfo), parmEnd->strAt(namepos), parmEnd->tokAt(namepos), parmEnd); mTemplateForwardDeclarations.emplace_back(tok, getScopeName(scopeInfo), parmEnd->tokAt(namepos), parmEnd);
break; break;
} }
// Implementation => add to mTemplateDeclarations // Implementation => add to mTemplateDeclarations
else if (tok2->str() == "{") { else if (tok2->str() == "{") {
const int namepos = getTemplateNamePosition(parmEnd, false); const int namepos = getTemplateNamePosition(parmEnd, false);
if (namepos > 0) if (namepos > 0)
mTemplateDeclarations.emplace_back(tok, getScopeName(scopeInfo), parmEnd->strAt(namepos), parmEnd->tokAt(namepos), parmEnd); mTemplateDeclarations.emplace_back(tok, getScopeName(scopeInfo), parmEnd->tokAt(namepos), parmEnd);
break; break;
} }
} }
@ -549,7 +591,7 @@ void TemplateSimplifier::getTemplateInstantiations()
const Token *skip = nullptr; const Token *skip = nullptr;
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok, "}|namespace|class|struct|union")) { if (Token::Match(tok, "{|}|namespace|class|struct|union")) {
setScopeInfo(tok, &scopeList); setScopeInfo(tok, &scopeList);
continue; continue;
} }
@ -576,13 +618,13 @@ void TemplateSimplifier::getTemplateInstantiations()
} else if (Token::Match(tok->previous(), "(|{|}|;|=|>|<<|:|.|*|& %name% ::|<") || } else if (Token::Match(tok->previous(), "(|{|}|;|=|>|<<|:|.|*|& %name% ::|<") ||
Token::Match(tok->previous(), "%type% %name% ::|<") || Token::Match(tok->previous(), "%type% %name% ::|<") ||
Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% ::|<")) { Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% ::|<")) {
std::string scopeName = getScopeName(scopeList); std::string scopeName = getScopeName(scopeList);
while (Token::Match(tok, "%name% :: %name%")) { while (Token::Match(tok, "%name% :: %name%")) {
scopeName += (scopeName.empty() ? "" : " :: ") + tok->str(); scopeName += (scopeName.empty() ? "" : " :: ") + tok->str();
tok = tok->tokAt(2); tok = tok->tokAt(2);
} }
if (!Token::Match(tok, "%name% <")) if (!Token::Match(tok, "%name% <") ||
Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast"))
continue; continue;
if (tok == skip) { if (tok == skip) {
@ -599,7 +641,7 @@ void TemplateSimplifier::getTemplateInstantiations()
for (; tok2 && tok2 != tok; tok2 = tok2->previous()) { for (; tok2 && tok2 != tok; tok2 = tok2->previous()) {
if (Token::Match(tok2, ", %name% <") && if (Token::Match(tok2, ", %name% <") &&
templateParameters(tok2->tokAt(2))) { templateParameters(tok2->tokAt(2))) {
mTemplateInstantiations.emplace_back(tok2->next(), getScopeName(scopeList), tok2->strAt(1), tok2->tokAt(1)); mTemplateInstantiations.emplace_back(tok2->next(), getScopeName(scopeList));
} else if (Token::Match(tok2->next(), "class|struct")) } else if (Token::Match(tok2->next(), "class|struct"))
const_cast<Token *>(tok2)->deleteNext(); const_cast<Token *>(tok2)->deleteNext();
} }
@ -609,13 +651,13 @@ void TemplateSimplifier::getTemplateInstantiations()
const std::string scopeName1(scopeName); const std::string scopeName1(scopeName);
while (true) { while (true) {
const std::string fullName = scopeName + (scopeName.empty()?"":" :: ") + tok->str(); const std::string fullName = scopeName + (scopeName.empty()?"":" :: ") + tok->str();
const std::list<TokenAndName>::const_iterator it = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindName(fullName)); const std::list<TokenAndName>::const_iterator it = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindFullName(fullName));
if (it != mTemplateDeclarations.end()) { if (it != mTemplateDeclarations.end()) {
mTemplateInstantiations.emplace_back(tok, getScopeName(scopeList), fullName, tok); mTemplateInstantiations.emplace_back(tok, scopeName);
break; break;
} else { } else {
if (scopeName.empty()) { if (scopeName.empty()) {
mTemplateInstantiations.emplace_back(tok, getScopeName(scopeList), scopeName1 + (scopeName1.empty()?"":" :: ") + tok->str(), tok); mTemplateInstantiations.emplace_back(tok, getScopeName(scopeList));
break; break;
} }
const std::string::size_type pos = scopeName.rfind(" :: "); const std::string::size_type pos = scopeName.rfind(" :: ");
@ -862,7 +904,7 @@ void TemplateSimplifier::simplifyTemplateAliases()
mTemplateInstantiations.end(), mTemplateInstantiations.end(),
FindToken(tok1)); FindToken(tok1));
if (it != mTemplateInstantiations.end()) if (it != mTemplateInstantiations.end())
mTemplateInstantiations.emplace_back(tok2, it->scope, it->name, it->nameToken); mTemplateInstantiations.emplace_back(tok2, it->scope);
} }
continue; continue;
} }
@ -1134,7 +1176,7 @@ void TemplateSimplifier::expandTemplate(
} }
// check if type is instantiated // check if type is instantiated
for (const auto & inst : mTemplateInstantiations) { for (const auto & inst : mTemplateInstantiations) {
if (Token::simpleMatch(inst.nameToken, name.c_str())) { if (Token::simpleMatch(inst.token, name.c_str())) {
// use the instantiated name // use the instantiated name
dst->insertToken(name, "", true); dst->insertToken(name, "", true);
start = closing; start = closing;
@ -1142,10 +1184,18 @@ void TemplateSimplifier::expandTemplate(
} }
} }
// just copy the token if it wasn't instantiated // just copy the token if it wasn't instantiated
if (start != closing) if (start != closing) {
dst->insertToken(start->str(), "", true); dst->insertToken(start->str(), start->originalName(), true);
} else dst->previous()->isSigned(start->isSigned());
dst->insertToken(start->str(), "", true); dst->previous()->isUnsigned(start->isUnsigned());
dst->previous()->isLong(start->isLong());
}
} else {
dst->insertToken(start->str(), start->originalName(), true);
dst->previous()->isSigned(start->isSigned());
dst->previous()->isUnsigned(start->isUnsigned());
dst->previous()->isLong(start->isLong());
}
} }
if (start->link()) { if (start->link()) {
if (Token::Match(start, "[|{|(")) { if (Token::Match(start, "[|{|(")) {
@ -1170,12 +1220,12 @@ void TemplateSimplifier::expandTemplate(
if (Token::Match(start, "template !!<")) { if (Token::Match(start, "template !!<")) {
if (start->strAt(-1) == "extern") if (start->strAt(-1) == "extern")
start = start->previous(); start = start->previous();
mExplicitInstantiationsToDelete.emplace_back(start, "", "", nullptr); mExplicitInstantiationsToDelete.emplace_back(start, "");
} }
} }
for (Token *tok3 = mTokenList.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { for (Token *tok3 = mTokenList.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) {
if (Token::Match(tok3, "}|namespace|class|struct|union")) { if (Token::Match(tok3, "{|}|namespace|class|struct|union")) {
setScopeInfo(tok3, &scopeInfo); setScopeInfo(tok3, &scopeInfo);
continue; continue;
} }
@ -1353,7 +1403,9 @@ void TemplateSimplifier::expandTemplate(
if (copy) if (copy)
mTokenList.addtoken(tok3, tok3->linenr(), tok3->fileIndex()); mTokenList.addtoken(tok3, tok3->linenr(), tok3->fileIndex());
if (Token::Match(tok3, "%type% <") && Token::Match(tok3->next()->findClosingBracket(), ">|>>")) { if (Token::Match(tok3, "%type% <") &&
!Token::Match(tok3, "template|static_cast|const_cast|reinterpret_cast|dynamic_cast") &&
Token::Match(tok3->next()->findClosingBracket(), ">|>>")) {
const Token *closingBracket = tok3->next()->findClosingBracket(); const Token *closingBracket = tok3->next()->findClosingBracket();
if (Token::simpleMatch(closingBracket->next(), "&")) { if (Token::simpleMatch(closingBracket->next(), "&")) {
int num = 0; int num = 0;
@ -1369,10 +1421,17 @@ void TemplateSimplifier::expandTemplate(
continue; continue;
} }
std::string name = tok3->str(); std::string scope;
for (const Token *prev = tok3->tokAt(-2); Token::Match(prev, "%name% ::"); prev = prev->tokAt(-2)) for (const Token *prev = tok3->tokAt(-2); Token::Match(prev, "%name% ::"); prev = prev->tokAt(-2)) {
name = prev->str() + " :: " + name; if (scope.empty())
mTemplateInstantiations.emplace_back(mTokenList.back(), getScopeName(scopeInfo), name, tok3); scope = prev->str();
else
scope = prev->str() + " :: " + scope;
}
if (copy)
mTemplateInstantiations.emplace_back(mTokenList.back(), scope);
else if (!inTemplateDefinition)
mTemplateInstantiations.emplace_back(tok3, scope);
} }
// link() newly tokens manually // link() newly tokens manually
@ -1796,7 +1855,7 @@ std::string TemplateSimplifier::getNewName(
else if (indentlevel > 0 && Token::Match(tok3, "> [,>]")) else if (indentlevel > 0 && Token::Match(tok3, "> [,>]"))
--indentlevel; --indentlevel;
if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) { if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) {
mTypesUsedInTemplateInstantiation.emplace_back(tok3, "", "", nullptr); mTypesUsedInTemplateInstantiation.emplace_back(tok3, "");
} }
const bool constconst = tok3->str() == "const" && tok3->strAt(1) == "const"; const bool constconst = tok3->str() == "const" && tok3->strAt(1) == "const";
if (!constconst) { if (!constconst) {
@ -1853,12 +1912,21 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
} }
} }
// simplifyCalculations can erase an instantiation
if (!instantiation.token)
continue;
// already simplified // already simplified
if (!Token::Match(instantiation.token, "%name% <")) if (!Token::Match(instantiation.token, "%name% <"))
continue; continue;
if (instantiation.name != templateDeclaration.name) if (instantiation.fullName != templateDeclaration.fullName) {
continue; // FIXME: fallback to not matching scopes until using namespace and type deduction work
// names must match
if (instantiation.name != templateDeclaration.name)
continue;
}
if (!matchSpecialization(templateDeclaration.nameToken, instantiation.token, specializations)) if (!matchSpecialization(templateDeclaration.nameToken, instantiation.token, specializations))
continue; continue;
@ -1909,15 +1977,16 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
// New classname/funcname.. // New classname/funcname..
const std::string newName(templateDeclaration.name + " < " + typeForNewName + " >"); const std::string newName(templateDeclaration.name + " < " + typeForNewName + " >");
const std::string newFullName(templateDeclaration.scope + (templateDeclaration.scope.empty() ? "" : " :: ") + newName);
if (expandedtemplates.find(newName) == expandedtemplates.end()) { if (expandedtemplates.find(newFullName) == expandedtemplates.end()) {
expandedtemplates.insert(newName); expandedtemplates.insert(newFullName);
expandTemplate(templateDeclaration, instantiation, typeParametersInDeclaration, newName, !specialized); expandTemplate(templateDeclaration, instantiation, typeParametersInDeclaration, newName, !specialized);
instantiated = true; instantiated = true;
} }
// Replace all these template usages.. // Replace all these template usages..
replaceTemplateUsage(tok2, instantiation.name, typeStringsUsedInTemplateInstantiation, newName); replaceTemplateUsage(instantiation, typeStringsUsedInTemplateInstantiation, newName);
} }
// process uninstantiated templates // process uninstantiated templates
@ -1976,14 +2045,16 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
// New classname/funcname.. // New classname/funcname..
const std::string newName(templateDeclaration.name + " < " + typeForNewName + " >"); const std::string newName(templateDeclaration.name + " < " + typeForNewName + " >");
if (expandedtemplates.find(newName) == expandedtemplates.end()) { const std::string newFullName(templateDeclaration.scope + (templateDeclaration.scope.empty() ? "" : " :: ") + newName);
expandedtemplates.insert(newName);
if (expandedtemplates.find(newFullName) == expandedtemplates.end()) {
expandedtemplates.insert(newFullName);
expandTemplate(templateDeclaration, templateDeclaration, typeParametersInDeclaration, newName, false); expandTemplate(templateDeclaration, templateDeclaration, typeParametersInDeclaration, newName, false);
instantiated = true; instantiated = true;
} }
// Replace all these template usages.. // Replace all these template usages..
replaceTemplateUsage(startToken, templateDeclaration.name, typeStringsUsedInTemplateInstantiation, newName); replaceTemplateUsage(templateDeclaration, typeStringsUsedInTemplateInstantiation, newName);
} }
// Template has been instantiated .. then remove the template declaration // Template has been instantiated .. then remove the template declaration
@ -2002,26 +2073,40 @@ static bool matchTemplateParameters(const Token *nameTok, const std::list<std::s
} }
void TemplateSimplifier::replaceTemplateUsage( void TemplateSimplifier::replaceTemplateUsage(
Token * const instantiationToken, const TokenAndName &instantiation,
const std::string &templateName,
const std::list<std::string> &typeStringsUsedInTemplateInstantiation, const std::list<std::string> &typeStringsUsedInTemplateInstantiation,
const std::string &newName) const std::string &newName)
{ {
std::list<ScopeInfo2> scopeInfo; std::list<ScopeInfo2> scopeInfo;
std::list< std::pair<Token *, Token *> > removeTokens; std::list< std::pair<Token *, Token *> > removeTokens;
for (Token *nameTok = instantiationToken; nameTok; nameTok = nameTok->next()) { for (Token *nameTok = mTokenList.front(); nameTok; nameTok = nameTok->next()) {
if (Token::Match(nameTok, "}|namespace|class|struct|union")) { if (Token::Match(nameTok, "{|}|namespace|class|struct|union")) {
setScopeInfo(nameTok, &scopeInfo); setScopeInfo(nameTok, &scopeInfo);
continue; continue;
} }
if (!Token::Match(nameTok, "%name% <")) if (!Token::Match(nameTok, "%name% <") ||
continue; Token::Match(nameTok, "template|const_cast|dynamic_cast|reinterpret_cast|static_cast"))
if (!matchTemplateParameters(nameTok, typeStringsUsedInTemplateInstantiation))
continue; continue;
// FIXME Proper name matching std::set<TemplateSimplifier::TokenAndName*> & pointers = nameTok->templateSimplifierPointers();
const std::string lastName(templateName.find(' ') == std::string::npos ? templateName : templateName.substr(templateName.rfind(' ') + 1));
if (lastName != nameTok->str()) // check if instantiation matches token instantiation from pointer
if (pointers.size()) {
// check full name
if (instantiation.fullName != (*pointers.begin())->fullName) {
// FIXME: fallback to just matching name
if (nameTok->str() != instantiation.name)
continue;
}
}
// no pointer available look at tokens directly
else {
// FIXME: fallback to just matching name
if (nameTok->str() != instantiation.name)
continue;
}
if (!matchTemplateParameters(nameTok, typeStringsUsedInTemplateInstantiation))
continue; continue;
// match parameters // match parameters

View File

@ -69,7 +69,20 @@ public:
* Token and its full scopename * Token and its full scopename
*/ */
struct TokenAndName { struct TokenAndName {
TokenAndName(Token *tok, const std::string &s, const std::string &n, const Token *nt, const Token *pe = nullptr); /**
* Constructor used for instantiations.
* \param tok template instantiation name token "name<...>"
* \param scope full qualification of template
*/
TokenAndName(Token *tok, const std::string &s);
/**
* Constructor used for declarations.
* \param tok template declaration token "template < ... >"
* \param scope full qualification of template
* \param nt template name token "template < ... > class name"
* \param pe template parameter end token ">"
*/
TokenAndName(Token *tok, const std::string &s, const Token *nt, const Token *pe);
TokenAndName(const TokenAndName& otherTok); TokenAndName(const TokenAndName& otherTok);
~TokenAndName(); ~TokenAndName();
@ -279,13 +292,11 @@ private:
/** /**
* Replace all matching template usages 'Foo < int >' => 'Foo<int>' * Replace all matching template usages 'Foo < int >' => 'Foo<int>'
* @param instantiationToken Template instantiation token * @param instantiation Template instantiation information.
* @param templateName full template name with scope info
* @param typeStringsUsedInTemplateInstantiation template parameters. list of token strings. * @param typeStringsUsedInTemplateInstantiation template parameters. list of token strings.
* @param newName The new type name * @param newName The new type name
*/ */
void replaceTemplateUsage(Token *const instantiationToken, void replaceTemplateUsage(const TokenAndName &instantiation,
const std::string &templateName,
const std::list<std::string> &typeStringsUsedInTemplateInstantiation, const std::list<std::string> &typeStringsUsedInTemplateInstantiation,
const std::string &newName); const std::string &newName);