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[])
|
||||
{
|
||||
// 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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }; "
|
||||
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>(); ");
|
||||
"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) {}");
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;"));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue