Fixed #9155 (Syntax error on valid C++ code) (#1880)

Refactored simplifyTemplateAliases to iterate over template type aliases
rather than instantiations. This fixed template type aliases that were
not templates.

Don't instantiate templates in template type aliases. They will get
instantiated once the type alias is instantiated. This required
increasing the template simplifier pass count to 3 so one of the
existing tests continued to work.
This commit is contained in:
IOBYTE 2019-06-12 01:44:48 -04:00 committed by Daniel Marjamäki
parent 4939e0c308
commit 2a4b28c267
3 changed files with 80 additions and 103 deletions

View File

@ -771,7 +771,7 @@ void TemplateSimplifier::getTemplateInstantiations()
const Token *tok2 = Token::findmatch(tok, "{|;");
if (tok2 && tok2->str() == "{")
tok = tok2->link();
else if (!isUsing && tok2 && tok2->str() == ";")
else if (tok2 && tok2->str() == ";")
tok = const_cast<Token *>(tok2);
}
} else if (Token::Match(tok, "template using %name% <")) {
@ -1150,39 +1150,13 @@ void TemplateSimplifier::useDefaultArgumentValues(TokenAndName &declaration)
void TemplateSimplifier::simplifyTemplateAliases()
{
std::list<TokenAndName>::iterator it1, it2, it3;
for (it1 = mTemplateInstantiations.begin(); it1 != mTemplateInstantiations.end();) {
it3 = it1;
TokenAndName &templateAlias = *it1;
++it1;
Token *startToken = templateAlias.token;
if (!startToken)
continue;
while (Token::Match(startToken->tokAt(-2), "%name% :: %name%"))
startToken = startToken->tokAt(-2);
if (!(Token::Match(startToken->tokAt(-4), "> using %name% = %name% ::|<") ||
Token::Match(startToken->tokAt(-5), "> using %name% = typename %name% ::|<")))
continue;
const bool hasTypename(startToken->strAt(-1) == "typename");
for (std::list<TokenAndName>::iterator it1 = mTemplateDeclarations.begin(); it1 != mTemplateDeclarations.end();) {
TokenAndName &aliasDeclaration = *it1;
// Get start token for alias
startToken = startToken->tokAt(hasTypename ? -6 : -5);
while (Token::Match(startToken, "%name%|<|>|>>|,"))
startToken = startToken->previous();
// handle case where 'template' is first token
if (!startToken) {
if (!Token::simpleMatch(mTokenList.front(), "template <"))
continue;
} else if (!Token::Match(startToken, "[;{}] template <"))
if (!aliasDeclaration.isAlias()) {
++it1;
continue;
std::list<TokenAndName>::iterator it5 = std::find_if(mTemplateDeclarations.begin(),
mTemplateDeclarations.end(),
FindToken(!startToken ? mTokenList.front() : startToken->next()));
if (it5 == mTemplateDeclarations.end())
continue;
TokenAndName &aliasDeclaration = *it5;
}
// alias parameters..
std::vector<const Token *> aliasParameters;
@ -1192,15 +1166,19 @@ void TemplateSimplifier::simplifyTemplateAliases()
aliasParameterNames[aliasParameters[argnr]->str()] = argnr;
// Look for alias usages..
const Token *endToken = nullptr;
for (it2 = it1; it2 != mTemplateInstantiations.end(); ++it2) {
bool found = false;
for (std::list<TokenAndName>::iterator it2 = mTemplateInstantiations.begin(); it2 != mTemplateInstantiations.end();) {
TokenAndName &aliasUsage = *it2;
if (!aliasUsage.token || aliasUsage.fullName != aliasDeclaration.fullName)
if (!aliasUsage.token || aliasUsage.fullName != aliasDeclaration.fullName) {
++it2;
continue;
}
// don't recurse
if (aliasDeclaration.isAliasToken(aliasUsage.token))
if (aliasDeclaration.isAliasToken(aliasUsage.token)) {
++it2;
continue;
}
std::vector<std::pair<Token *, Token *>> args;
Token *tok2 = aliasUsage.token->tokAt(2);
@ -1221,37 +1199,32 @@ void TemplateSimplifier::simplifyTemplateAliases()
break;
}
}
if (!tok2 || tok2->str() != ">" || args.size() != aliasParameters.size())
if (!tok2 || tok2->str() != ">" || args.size() != aliasParameters.size()) {
++it2;
continue;
// Replace template alias code..
aliasUsage.name = templateAlias.name;
aliasUsage.fullName = templateAlias.fullName;
aliasUsage.scope = templateAlias.scope;
const Token *temp = aliasDeclaration.aliasStartToken();
if (temp->str() == "typename")
temp = temp->next();
std::stack<Token *> links;
while (temp && temp != templateAlias.token) {
aliasUsage.token->insertToken(temp->str(), "", true);
Token * previous = aliasUsage.token->previous();
if (Token::Match(previous, "(|[|{"))
links.push(previous);
else if (!links.empty() && Token::Match(previous, ")|]|}")) {
Token::createMutualLinks(links.top(), previous);
links.pop();
}
temp = temp->next();
}
aliasUsage.token->str(templateAlias.token->str());
tok2 = aliasUsage.token->next(); // the '<'
const Token * const endToken1 = templateAlias.token->next()->findClosingBracket();
const Token * const endToken2 = TokenList::copyTokens(tok2, templateAlias.token->tokAt(2), endToken1->previous(), false)->next();
for (const Token *tok1 = templateAlias.token->next(); tok2 != endToken2; tok1 = tok1->next(), tok2 = tok2->next()) {
if (!tok2->isName())
// copy template-id from declaration to after instantiation
Token * dst = aliasUsage.token->next()->findClosingBracket();
Token * end = TokenList::copyTokens(dst, aliasDeclaration.aliasStartToken(), aliasDeclaration.aliasEndToken()->previous(), false)->next();
// replace parameters
for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) {
if (!tok1->isName())
continue;
if (aliasParameterNames.find(tok1->str()) != aliasParameterNames.end()) {
const unsigned int argnr = aliasParameterNames[tok1->str()];
const Token * const fromStart = args[argnr].first;
const Token * const fromEnd = args[argnr].second->previous();
TokenList::copyTokens(tok1, fromStart, fromEnd, true);
tok1->deleteThis();
} else if (tok1->str() == "typename")
tok1->deleteThis();
}
// add new instantiations
for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) {
if (!tok1->isName())
continue;
if (aliasParameterNames.find(tok2->str()) == aliasParameterNames.end()) {
// Create template instance..
@ -1262,53 +1235,32 @@ void TemplateSimplifier::simplifyTemplateAliases()
if (it != mTemplateInstantiations.end())
addInstantiation(tok2, it->scope);
}
continue;
}
const unsigned int argnr = aliasParameterNames[tok2->str()];
const Token * const fromStart = args[argnr].first;
const Token * const fromEnd = args[argnr].second->previous();
Token * const destToken = tok2;
tok2 = TokenList::copyTokens(tok2, fromStart, fromEnd, true);
if (tok2 == destToken->next())
tok2 = destToken;
destToken->deleteThis();
}
endToken = endToken1->next();
while (endToken->str() != ";")
endToken = endToken->next();
// erase the instantiation tokens
eraseTokens(aliasUsage.token->previous(), dst->next());
found = true;
// Remove alias usage code (parameters)
Token::eraseTokens(tok2->previous(), args.back().second);
// erase this instantiation
it2 = mTemplateInstantiations.erase(it2);
}
if (endToken) {
// Remove all template instantiations in template alias
for (const Token *tok = aliasDeclaration.paramEnd->tokAt(4); tok != endToken; tok = tok->next()) {
if (!Token::Match(tok, "%name% <"))
continue;
std::list<TokenAndName>::iterator it = std::find_if(mTemplateInstantiations.begin(),
mTemplateInstantiations.end(),
FindToken(tok));
if (it == mTemplateInstantiations.end())
continue;
std::list<TokenAndName>::iterator next = it;
++next;
if (it == it1)
it1 = next;
mTemplateInstantiations.erase(it,next);
}
if (startToken)
eraseTokens(startToken, endToken->next() ? endToken->next() : endToken);
if (found) {
Token *end = const_cast<Token *>(aliasDeclaration.aliasEndToken());
// remove declaration tokens
if (aliasDeclaration.token->previous())
eraseTokens(aliasDeclaration.token->previous(), end->next() ? end->next() : end);
else {
eraseTokens(mTokenList.front(), endToken->next() ? endToken->next() : endToken);
eraseTokens(mTokenList.front(), end->next() ? end->next() : end);
deleteToken(mTokenList.front());
}
// remove declaration
mTemplateDeclarations.erase(it5);
it1 = mTemplateDeclarations.erase(it1);
} else
mTemplateInstantiations.erase(it3);
++it1;
}
}
@ -3298,7 +3250,7 @@ void TemplateSimplifier::simplifyTemplates(
}
}
// TODO: 2 is not the ideal number of loops.
// TODO: 3 is not the ideal number of loops.
// We should loop until the number of declarations is 0 but we can't
// do that until we instantiate unintstantiated templates with their symbolic types.
// That will allow the uninstantiated template code to be removed from the symbol database.
@ -3306,7 +3258,7 @@ void TemplateSimplifier::simplifyTemplates(
// the uninstantiated template code in the symbol database can't be removed until #8768
// is fixed.
for (int i = 0; i < 2; ++i) {
for (int i = 0; i < 3; ++i) {
if (i) {
// it may take more than one pass to simplify type aliases
while (mTokenizer->simplifyUsing())

View File

@ -151,6 +151,7 @@ private:
TEST_CASE(template111); // crash
TEST_CASE(template112); // #9146 syntax error
TEST_CASE(template113);
TEST_CASE(template114); // #9155
TEST_CASE(template_specialization_1); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_specialization_2); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template)
@ -188,6 +189,7 @@ private:
TEST_CASE(templateAlias2);
TEST_CASE(templateAlias3); // #8315
TEST_CASE(templateAlias4); // #9070
TEST_CASE(templateAlias5);
// Test TemplateSimplifier::instantiateMatch
TEST_CASE(instantiateMatch);
@ -2555,7 +2557,7 @@ private:
"template < typename > struct c ; "
"struct e<int> ; "
"} "
"e<int> foo ; "
"e<int> :: g foo ; "
"struct e<int> { "
"bool h ; h = a < b<c<int>::g> > :: h ; "
"} ; "
@ -2680,6 +2682,20 @@ private:
}
}
void template114() { // #9155
const char code[] = "template <typename a, a> struct b {};\n"
"template <typename> struct c;\n"
"template <typename> struct d : b<bool, std::is_polymorphic<int>{}> {};\n"
"template <bool> struct e;\n"
"template <typename a> using f = typename e<c<d<a>>::g>::h;";
const char exp[] = "template < typename a , a > struct b { } ; "
"template < typename > struct c ; "
"template < typename > struct d : b < bool , std :: is_polymorphic < int > { } > { } ; "
"template < bool > struct e ; "
"template < typename a > using f = typename e < c < d < a > > :: g > :: h ;";
ASSERT_EQUALS(exp, tok(code));
}
void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..};
const char code[] = "template <typename T> struct C {};\n"
"template <typename T> struct S {a};\n"
@ -3678,6 +3694,16 @@ private:
ASSERT_EQUALS(expected, tok(code));
}
void templateAlias5() {
const char code[] = "template<typename T> using A = int;\n"
"template<typename T> using B = T;\n"
"A<char> a;\n"
"B<char> b;";
const char expected[] = "int a ; "
"char b ;";
ASSERT_EQUALS(expected, tok(code));
}
unsigned int instantiateMatch(const char code[], const std::size_t numberOfArguments, const char patternAfter[]) {
Tokenizer tokenizer(&settings, this);

View File

@ -4858,7 +4858,6 @@ private:
Tokenizer tokenizer(&settings0, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
ASSERT(nullptr != Token::findsimplematch(tokenizer.tokens(), "> > ;")->link());
ASSERT(nullptr != Token::findsimplematch(tokenizer.tokens(), "> ;")->link());
}