Fixed #6218 (Template type aliasing misdetection)

This commit is contained in:
Daniel Marjamäki 2017-12-29 22:47:07 +01:00
parent caf9f22015
commit fc1ac180e6
8 changed files with 175 additions and 20 deletions

View File

@ -701,6 +701,96 @@ void TemplateSimplifier::useDefaultArgumentValues(const std::list<TokenAndName>
}
}
void TemplateSimplifier::simplifyTemplateAliases(std::list<TemplateSimplifier::TokenAndName> *templateInstantiations)
{
std::list<TemplateSimplifier::TokenAndName>::iterator it1, it2;
for (it1 = templateInstantiations->begin(); it1 != templateInstantiations->end();) {
TemplateSimplifier::TokenAndName &templateAlias = *it1;
++it1;
Token *startToken = templateAlias.token;
while (Token::Match(startToken->tokAt(-2), "%name% :: %name%"))
startToken = startToken->tokAt(-2);
if (!Token::Match(startToken->tokAt(-4), "> using %name% = %name% ::|<"))
continue;
const std::string aliasName(startToken->strAt(-2));
const Token * const aliasToken1 = startToken;
// Get start token for alias
startToken = startToken->tokAt(-5);
while (Token::Match(startToken, "%name%|<|>|>>|,"))
startToken = startToken->previous();
if (!Token::Match(startToken, "[;{}] template <"))
continue;
// alias parameters..
std::vector<const Token *> aliasParameters;
TemplateSimplifier::getTemplateParametersInDeclaration(startToken->tokAt(3), aliasParameters);
std::map<std::string, unsigned int> aliasParameterNames;
for (unsigned int argnr = 0; argnr < aliasParameters.size(); ++argnr)
aliasParameterNames[aliasParameters[argnr]->str()] = argnr;
// Look for alias usages..
const Token *endToken = nullptr;
for (it2 = it1; it2 != templateInstantiations->end(); ++it2) {
TemplateSimplifier::TokenAndName &aliasUsage = *it2;
if (aliasUsage.name != aliasName)
continue;
std::vector<std::pair<Token *, Token *>> args;
Token *tok2 = aliasUsage.token->tokAt(2);
while (tok2) {
Token * const start = tok2;
while (tok2 && !Token::Match(tok2, "[,>;{}]"))
tok2 = tok2->next();
args.push_back(std::pair<Token *, Token *>(start, tok2));
if (tok2 && tok2->str() == ",") {
tok2 = tok2->next();
} else {
break;
}
}
if (!tok2 || tok2->str() != ">" || args.size() != aliasParameters.size())
continue;
// Replace template alias code..
aliasUsage.name = templateAlias.name;
if (aliasUsage.name.find(" ") == std::string::npos) {
aliasUsage.token->str(templateAlias.token->str());
} else {
tok2 = Tokenizer::copyTokens(aliasUsage.token, aliasToken1, templateAlias.token, true);
aliasUsage.token->deleteThis();
aliasUsage.token = tok2;
}
tok2 = aliasUsage.token->next(); // the '<'
Token *tok1 = templateAlias.token->tokAt(2);
while (tok1 && tok1->str() != ";") {
Token *fromStart, *fromEnd;
if (aliasParameterNames.find(tok1->str()) != aliasParameterNames.end()) {
const unsigned int argnr = aliasParameterNames[tok1->str()];
fromStart = args[argnr].first;
fromEnd = args[argnr].second->previous();
} else {
fromStart = fromEnd = tok1;
}
if (tok2->next() == fromStart)
tok2 = fromEnd;
else
tok2 = Tokenizer::copyTokens(tok2, fromStart, fromEnd, true);
tok1 = tok1->next();
}
endToken = tok1;
Token::eraseTokens(tok2, args.back().second->next());
}
if (endToken) {
Token::eraseTokens(startToken, endToken);
it2 = it1;
--it2;
templateInstantiations->erase(it2,it1);
}
}
}
bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::size_t numberOfArguments, const char patternAfter[])
{
// if (!Token::simpleMatch(instance, (name + " <").c_str()))
@ -1555,6 +1645,8 @@ void TemplateSimplifier::simplifyTemplates(
// Template arguments with default values
TemplateSimplifier::useDefaultArgumentValues(templates, &templateInstantiations);
TemplateSimplifier::simplifyTemplateAliases(&templateInstantiations);
// expand templates
//bool done = false;
//while (!done)

View File

@ -101,6 +101,12 @@ public:
static void useDefaultArgumentValues(const std::list<TokenAndName> &templates,
std::list<TokenAndName> *templateInstantiations);
/**
* simplify template aliases
* @param templateInstantiations pointer to list of template instantiations
*/
static void simplifyTemplateAliases(std::list<TokenAndName> *templateInstantiations);
/**
* Match template declaration/instantiation
* @param instance template instantiation

View File

@ -5605,6 +5605,8 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co
syntaxError(nullptr); // #7043 invalid code
if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|)|public:|protected:|private:"))
continue;
if (Token::simpleMatch(tok, "template <"))
continue;
Token *type0 = tok;
if (!Token::Match(type0, "::|extern| %type%"))
@ -8362,6 +8364,38 @@ const Token * Tokenizer::findGarbageCode() const
if ((list.back()->str()==")" || list.back()->str()=="}") && list.back()->previous() && list.back()->previous()->isControlFlowKeyword())
return list.back()->previous();
// Garbage templates..
if (isCPP()) {
for (const Token *tok = tokens(); tok; tok = tok->next()) {
if (!Token::Match(tok, "template <"))
continue;
if (tok->previous() && !Token::Match(tok->previous(), "[:;{}]"))
return tok;
const Token *tok1 = tok;
tok = tok->tokAt(2);
while (Token::Match(tok,"%name%|*|,|.|(")) {
if (tok->str() == "(")
tok = tok->link();
tok = tok->next();
}
if (tok && tok->str() == "=") {
while (tok && !Token::Match(tok, "[;{}]") && !Token::Match(tok, ">|>> %name%")) {
if (tok->str() == "(")
tok = tok->link();
tok = tok->next();
}
}
if (!tok)
return tok1;
if (Token::Match(tok->previous(), "template <"))
continue;
if (!Token::Match(tok, ">|>>"))
return tok1;
if (!Token::Match(tok, ">|>> %name%"))
return tok->next() ? tok->next() : tok1;
}
}
return nullptr;
}

View File

@ -292,7 +292,7 @@ private:
"void G( template <typename T> class (j) ) {}";
// don't segfault..
checkCode(code);
ASSERT_THROW(checkCode(code), InternalError);
}
void wrong_syntax3() { // #3544
@ -402,7 +402,7 @@ private:
void garbageCode7() {
checkCode("1 (int j) { return return (c) * sizeof } y[1];");
checkCode("foo(Args&&...) fn void = { } auto template<typename... bar(Args&&...)");
ASSERT_THROW(checkCode("foo(Args&&...) fn void = { } auto template<typename... bar(Args&&...)"), InternalError);
}
void garbageCode8() { // #5604
@ -602,7 +602,7 @@ private:
}
void garbageCode45() { // #6608
checkCode("struct true template < > { = } > struct Types \"s\" ; static_assert < int > ;");
ASSERT_THROW(checkCode("struct true template < > { = } > struct Types \"s\" ; static_assert < int > ;"), InternalError);
}
void garbageCode46() { // #6705
@ -863,7 +863,7 @@ private:
}
void garbageCode115() { // #5506
checkCode("A template < int { int = -1 ; } template < int N > struct B { int [ A < N > :: zero ] ; } ; B < 0 > b ;");
ASSERT_THROW(checkCode("A template < int { int = -1 ; } template < int N > struct B { int [ A < N > :: zero ] ; } ; B < 0 > b ;"), InternalError);
}
void garbageCode116() { // #5356
@ -1008,13 +1008,13 @@ private:
void garbageCode134() {
// Ticket #5605, #5759, #5762, #5774, #5823, #6059
ASSERT_THROW(checkCode("foo() template<typename T1 = T2 = typename = unused, T5 = = unused> struct tuple Args> tuple<Args...> { } main() { foo<int,int,int,int,int,int>(); }"), InternalError);
checkCode("( ) template < T1 = typename = unused> struct Args { } main ( ) { foo < int > ( ) ; }");
checkCode("() template < T = typename = x > struct a {} { f <int> () }");
ASSERT_THROW(checkCode("( ) template < T1 = typename = unused> struct Args { } main ( ) { foo < int > ( ) ; }"), InternalError);
ASSERT_THROW(checkCode("() template < T = typename = x > struct a {} { f <int> () }"), InternalError);
checkCode("template < T = typename = > struct a { f <int> }");
checkCode("struct S { int i, j; }; "
"template<int S::*p, typename U> struct X {}; "
"X<&S::i, int> x = X<&S::i, int>(); "
"X<&S::j, int> y = X<&S::j, int>(); ");
ASSERT_THROW(checkCode("struct S { int i, j; }; "
"template<int S::*p, typename U> struct X {}; "
"X<&S::i, int> x = X<&S::i, int>(); "
"X<&S::j, int> y = X<&S::j, int>(); "), InternalError);
checkCode("template <typename T> struct A {}; "
"template <> struct A<void> {}; "
"void foo(const void* f = 0) {}");

View File

@ -118,6 +118,9 @@ private:
TEST_CASE(expandSpecialized);
TEST_CASE(templateAlias1);
TEST_CASE(templateAlias2);
// Test TemplateSimplifier::instantiateMatch
TEST_CASE(instantiateMatch);
}
@ -683,7 +686,7 @@ private:
void template27() {
// #3350 - template inside macro call
const char code[] = "X(template<class T> class Fred);";
ASSERT_EQUALS("X ( template < class T > class Fred ) ;", tok(code));
ASSERT_THROW(tok(code), InternalError);
}
void template28() {
@ -1540,6 +1543,26 @@ private:
ASSERT_EQUALS("class A<int> : public B { } ;", tok("template<> class A<int> : public B {};"));
}
void templateAlias1() {
const char code[] = "template<class T, int N> struct Foo {};\n"
"template<class T> using Bar = Foo<T,3>;\n"
"Bar<int> b;\n";
const char expected[] = "; Foo<int,3> b ; struct Foo<int,3> { } ;";
ASSERT_EQUALS(expected, tok(code));
}
void templateAlias2() {
const char code[] = "namespace A { template<class T, int N> struct Foo {}; }\n"
"template<class T> using Bar = A::Foo<T,3>;\n"
"Bar<int> b;\n";
const char expected[] = "; A::Foo<int,3> b ; struct A::Foo<int,3> { } ;";
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

@ -1183,8 +1183,8 @@ private:
void simplifyTypedef39() {
const char code[] = "typedef int A;\n"
"template <const A, volatile A>::value;";
const char expected[] = "template < const int , int > :: value ;";
"template <const A, volatile A> struct S{};";
const char expected[] = "template < const int , int > struct S { } ;";
ASSERT_EQUALS(expected, tok(code, false));
ASSERT_EQUALS("", errout.str());
}

View File

@ -757,7 +757,7 @@ private:
// #4245 - segfault
void tokenize26() {
tokenizeAndStringify("class x { protected : template < int y = } ;");
ASSERT_THROW(tokenizeAndStringify("class x { protected : template < int y = } ;"), InternalError); // Garbage code
}
void tokenize27() {
@ -3938,7 +3938,7 @@ private:
}
void vardecl23() { // #4276 - segmentation fault
tokenizeAndStringify("class a { protected : template < class int x = 1 ; public : int f ( ) ; }");
ASSERT_THROW(tokenizeAndStringify("class a { protected : template < class int x = 1 ; public : int f ( ) ; }"), InternalError);
}
void vardecl24() { // #4187 - variable declaration within lambda function

View File

@ -2112,10 +2112,10 @@ private:
}
void varid_templateUsing() { // #5781 #7273
const char code[] = "template<class T> using X = Y<T>;\n"
const char code[] = "template<class T> using X = Y<T,4>;\n"
"X<int> x;";
TODO_ASSERT_EQUALS("\nY<int> x@1;\n",
"1: template < class T > using X ; X = Y < T > ;\n"
TODO_ASSERT_EQUALS("\nY<int,4> x@1;\n",
"1: template < class T > using X = Y < T , 4 > ;\n"
"2: X < int > x@1 ;\n",
tokenize(code));
}
@ -2139,9 +2139,9 @@ private:
}
void varid_typename() {
ASSERT_EQUALS("1: template < int d , class A , class B > ;\n", tokenize("template<int d, class A, class B>;"));
ASSERT_EQUALS("1: template < int d , class A , class B > struct S { } ;\n", tokenize("template<int d, class A, class B> struct S {};"));
ASSERT_EQUALS("1: template < int d , typename A , typename B > ;\n", tokenize("template<int d, typename A, typename B>;"));
ASSERT_EQUALS("1: template < int d , typename A , typename B > struct S { } ;\n", tokenize("template<int d, typename A, typename B> struct S {};"));
ASSERT_EQUALS("1: typename A a@1 ;\n", tokenize("typename A a;"));
}