Fixed #6218 (Template type aliasing misdetection)
This commit is contained in:
parent
caf9f22015
commit
fc1ac180e6
|
@ -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[])
|
bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::size_t numberOfArguments, const char patternAfter[])
|
||||||
{
|
{
|
||||||
// if (!Token::simpleMatch(instance, (name + " <").c_str()))
|
// if (!Token::simpleMatch(instance, (name + " <").c_str()))
|
||||||
|
@ -1555,6 +1645,8 @@ void TemplateSimplifier::simplifyTemplates(
|
||||||
// Template arguments with default values
|
// Template arguments with default values
|
||||||
TemplateSimplifier::useDefaultArgumentValues(templates, &templateInstantiations);
|
TemplateSimplifier::useDefaultArgumentValues(templates, &templateInstantiations);
|
||||||
|
|
||||||
|
TemplateSimplifier::simplifyTemplateAliases(&templateInstantiations);
|
||||||
|
|
||||||
// expand templates
|
// expand templates
|
||||||
//bool done = false;
|
//bool done = false;
|
||||||
//while (!done)
|
//while (!done)
|
||||||
|
|
|
@ -101,6 +101,12 @@ public:
|
||||||
static void useDefaultArgumentValues(const std::list<TokenAndName> &templates,
|
static void useDefaultArgumentValues(const std::list<TokenAndName> &templates,
|
||||||
std::list<TokenAndName> *templateInstantiations);
|
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
|
* Match template declaration/instantiation
|
||||||
* @param instance template instantiation
|
* @param instance template instantiation
|
||||||
|
|
|
@ -5605,6 +5605,8 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co
|
||||||
syntaxError(nullptr); // #7043 invalid code
|
syntaxError(nullptr); // #7043 invalid code
|
||||||
if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|)|public:|protected:|private:"))
|
if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|)|public:|protected:|private:"))
|
||||||
continue;
|
continue;
|
||||||
|
if (Token::simpleMatch(tok, "template <"))
|
||||||
|
continue;
|
||||||
|
|
||||||
Token *type0 = tok;
|
Token *type0 = tok;
|
||||||
if (!Token::Match(type0, "::|extern| %type%"))
|
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())
|
if ((list.back()->str()==")" || list.back()->str()=="}") && list.back()->previous() && list.back()->previous()->isControlFlowKeyword())
|
||||||
return list.back()->previous();
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -292,7 +292,7 @@ private:
|
||||||
"void G( template <typename T> class (j) ) {}";
|
"void G( template <typename T> class (j) ) {}";
|
||||||
|
|
||||||
// don't segfault..
|
// don't segfault..
|
||||||
checkCode(code);
|
ASSERT_THROW(checkCode(code), InternalError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wrong_syntax3() { // #3544
|
void wrong_syntax3() { // #3544
|
||||||
|
@ -402,7 +402,7 @@ private:
|
||||||
|
|
||||||
void garbageCode7() {
|
void garbageCode7() {
|
||||||
checkCode("1 (int j) { return return (c) * sizeof } y[1];");
|
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
|
void garbageCode8() { // #5604
|
||||||
|
@ -602,7 +602,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void garbageCode45() { // #6608
|
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
|
void garbageCode46() { // #6705
|
||||||
|
@ -863,7 +863,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void garbageCode115() { // #5506
|
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
|
void garbageCode116() { // #5356
|
||||||
|
@ -1008,13 +1008,13 @@ private:
|
||||||
void garbageCode134() {
|
void garbageCode134() {
|
||||||
// Ticket #5605, #5759, #5762, #5774, #5823, #6059
|
// 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);
|
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 > ( ) ; }");
|
ASSERT_THROW(checkCode("( ) template < T1 = typename = unused> struct Args { } main ( ) { foo < int > ( ) ; }"), InternalError);
|
||||||
checkCode("() template < T = typename = x > struct a {} { f <int> () }");
|
ASSERT_THROW(checkCode("() template < T = typename = x > struct a {} { f <int> () }"), InternalError);
|
||||||
checkCode("template < T = typename = > struct a { f <int> }");
|
checkCode("template < T = typename = > struct a { f <int> }");
|
||||||
checkCode("struct S { int i, j; }; "
|
ASSERT_THROW(checkCode("struct S { int i, j; }; "
|
||||||
"template<int S::*p, typename U> struct X {}; "
|
"template<int S::*p, typename U> struct X {}; "
|
||||||
"X<&S::i, int> x = X<&S::i, int>(); "
|
"X<&S::i, int> x = X<&S::i, int>(); "
|
||||||
"X<&S::j, int> y = X<&S::j, int>(); ");
|
"X<&S::j, int> y = X<&S::j, int>(); "), InternalError);
|
||||||
checkCode("template <typename T> struct A {}; "
|
checkCode("template <typename T> struct A {}; "
|
||||||
"template <> struct A<void> {}; "
|
"template <> struct A<void> {}; "
|
||||||
"void foo(const void* f = 0) {}");
|
"void foo(const void* f = 0) {}");
|
||||||
|
|
|
@ -118,6 +118,9 @@ private:
|
||||||
|
|
||||||
TEST_CASE(expandSpecialized);
|
TEST_CASE(expandSpecialized);
|
||||||
|
|
||||||
|
TEST_CASE(templateAlias1);
|
||||||
|
TEST_CASE(templateAlias2);
|
||||||
|
|
||||||
// Test TemplateSimplifier::instantiateMatch
|
// Test TemplateSimplifier::instantiateMatch
|
||||||
TEST_CASE(instantiateMatch);
|
TEST_CASE(instantiateMatch);
|
||||||
}
|
}
|
||||||
|
@ -683,7 +686,7 @@ private:
|
||||||
void template27() {
|
void template27() {
|
||||||
// #3350 - template inside macro call
|
// #3350 - template inside macro call
|
||||||
const char code[] = "X(template<class T> class Fred);";
|
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() {
|
void template28() {
|
||||||
|
@ -1540,6 +1543,26 @@ private:
|
||||||
ASSERT_EQUALS("class A<int> : public B { } ;", tok("template<> class A<int> : public B {};"));
|
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[]) {
|
unsigned int instantiateMatch(const char code[], const std::size_t numberOfArguments, const char patternAfter[]) {
|
||||||
Tokenizer tokenizer(&settings, this);
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
|
||||||
|
|
|
@ -1183,8 +1183,8 @@ private:
|
||||||
|
|
||||||
void simplifyTypedef39() {
|
void simplifyTypedef39() {
|
||||||
const char code[] = "typedef int A;\n"
|
const char code[] = "typedef int A;\n"
|
||||||
"template <const A, volatile A>::value;";
|
"template <const A, volatile A> struct S{};";
|
||||||
const char expected[] = "template < const int , int > :: value ;";
|
const char expected[] = "template < const int , int > struct S { } ;";
|
||||||
ASSERT_EQUALS(expected, tok(code, false));
|
ASSERT_EQUALS(expected, tok(code, false));
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -757,7 +757,7 @@ private:
|
||||||
|
|
||||||
// #4245 - segfault
|
// #4245 - segfault
|
||||||
void tokenize26() {
|
void tokenize26() {
|
||||||
tokenizeAndStringify("class x { protected : template < int y = } ;");
|
ASSERT_THROW(tokenizeAndStringify("class x { protected : template < int y = } ;"), InternalError); // Garbage code
|
||||||
}
|
}
|
||||||
|
|
||||||
void tokenize27() {
|
void tokenize27() {
|
||||||
|
@ -3938,7 +3938,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void vardecl23() { // #4276 - segmentation fault
|
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
|
void vardecl24() { // #4187 - variable declaration within lambda function
|
||||||
|
|
|
@ -2112,10 +2112,10 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void varid_templateUsing() { // #5781 #7273
|
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;";
|
"X<int> x;";
|
||||||
TODO_ASSERT_EQUALS("\nY<int> x@1;\n",
|
TODO_ASSERT_EQUALS("\nY<int,4> x@1;\n",
|
||||||
"1: template < class T > using X ; X = Y < T > ;\n"
|
"1: template < class T > using X = Y < T , 4 > ;\n"
|
||||||
"2: X < int > x@1 ;\n",
|
"2: X < int > x@1 ;\n",
|
||||||
tokenize(code));
|
tokenize(code));
|
||||||
}
|
}
|
||||||
|
@ -2139,9 +2139,9 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void varid_typename() {
|
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;"));
|
ASSERT_EQUALS("1: typename A a@1 ;\n", tokenize("typename A a;"));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue