diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 5603ee47f..a459d72d2 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -701,6 +701,96 @@ void TemplateSimplifier::useDefaultArgumentValues(const std::list } } +void TemplateSimplifier::simplifyTemplateAliases(std::list *templateInstantiations) +{ + std::list::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 aliasParameters; + TemplateSimplifier::getTemplateParametersInDeclaration(startToken->tokAt(3), aliasParameters); + std::map 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> 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(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) diff --git a/lib/templatesimplifier.h b/lib/templatesimplifier.h index 7645fb6d6..174d3a79b 100644 --- a/lib/templatesimplifier.h +++ b/lib/templatesimplifier.h @@ -101,6 +101,12 @@ public: static void useDefaultArgumentValues(const std::list &templates, std::list *templateInstantiations); + /** + * simplify template aliases + * @param templateInstantiations pointer to list of template instantiations + */ + static void simplifyTemplateAliases(std::list *templateInstantiations); + /** * Match template declaration/instantiation * @param instance template instantiation diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index ce3041b62..8d30585a8 100755 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -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; } diff --git a/test/testgarbage.cpp b/test/testgarbage.cpp index 24fb19c42..6e1a8339b 100644 --- a/test/testgarbage.cpp +++ b/test/testgarbage.cpp @@ -292,7 +292,7 @@ private: "void G( template 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 { = } > 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 struct tuple Args> tuple { } main() { foo(); }"), InternalError); - checkCode("( ) template < T1 = typename = unused> struct Args { } main ( ) { foo < int > ( ) ; }"); - checkCode("() template < T = typename = x > struct a {} { f () }"); + ASSERT_THROW(checkCode("( ) template < T1 = typename = unused> struct Args { } main ( ) { foo < int > ( ) ; }"), InternalError); + ASSERT_THROW(checkCode("() template < T = typename = x > struct a {} { f () }"), InternalError); checkCode("template < T = typename = > struct a { f }"); - checkCode("struct S { int i, j; }; " - "template 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 struct X {}; " + "X<&S::i, int> x = X<&S::i, int>(); " + "X<&S::j, int> y = X<&S::j, int>(); "), InternalError); checkCode("template struct A {}; " "template <> struct A {}; " "void foo(const void* f = 0) {}"); diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index c921de688..8bfbc4d12 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -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 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 : public B { } ;", tok("template<> class A : public B {};")); } + void templateAlias1() { + const char code[] = "template struct Foo {};\n" + "template using Bar = Foo;\n" + "Bar b;\n"; + + const char expected[] = "; Foo b ; struct Foo { } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void templateAlias2() { + const char code[] = "namespace A { template struct Foo {}; }\n" + "template using Bar = A::Foo;\n" + "Bar b;\n"; + + const char expected[] = "; A::Foo b ; struct A::Foo { } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + unsigned int instantiateMatch(const char code[], const std::size_t numberOfArguments, const char patternAfter[]) { Tokenizer tokenizer(&settings, this); diff --git a/test/testsimplifytypedef.cpp b/test/testsimplifytypedef.cpp index 471fe3ffe..567640d50 100644 --- a/test/testsimplifytypedef.cpp +++ b/test/testsimplifytypedef.cpp @@ -1183,8 +1183,8 @@ private: void simplifyTypedef39() { const char code[] = "typedef int A;\n" - "template ::value;"; - const char expected[] = "template < const int , int > :: value ;"; + "template struct S{};"; + const char expected[] = "template < const int , int > struct S { } ;"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 3c1dbd244..3466dcc81 100755 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -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 diff --git a/test/testvarid.cpp b/test/testvarid.cpp index d77aac5d8..98d2d2bcf 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -2112,10 +2112,10 @@ private: } void varid_templateUsing() { // #5781 #7273 - const char code[] = "template using X = Y;\n" + const char code[] = "template using X = Y;\n" "X x;"; - TODO_ASSERT_EQUALS("\nY x@1;\n", - "1: template < class T > using X ; X = Y < T > ;\n" + TODO_ASSERT_EQUALS("\nY 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;")); + ASSERT_EQUALS("1: template < int d , class A , class B > struct S { } ;\n", tokenize("template struct S {};")); - ASSERT_EQUALS("1: template < int d , typename A , typename B > ;\n", tokenize("template;")); + ASSERT_EQUALS("1: template < int d , typename A , typename B > struct S { } ;\n", tokenize("template struct S {};")); ASSERT_EQUALS("1: typename A a@1 ;\n", tokenize("typename A a;")); }