/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2016 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "platform.h" #include "settings.h" #include "templatesimplifier.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" struct InternalError; class TestSimplifyTemplate : public TestFixture { public: TestSimplifyTemplate() : TestFixture("TestSimplifyTemplate") { } private: Settings settings; void run() { settings.addEnabled("portability"); TEST_CASE(template1); TEST_CASE(template2); TEST_CASE(template3); TEST_CASE(template4); TEST_CASE(template5); TEST_CASE(template6); TEST_CASE(template7); TEST_CASE(template8); TEST_CASE(template9); TEST_CASE(template10); TEST_CASE(template11); TEST_CASE(template12); TEST_CASE(template13); TEST_CASE(template14); TEST_CASE(template15); // recursive templates TEST_CASE(template16); TEST_CASE(template17); TEST_CASE(template18); TEST_CASE(template19); TEST_CASE(template20); TEST_CASE(template21); TEST_CASE(template22); TEST_CASE(template23); TEST_CASE(template24); // #2648 - using sizeof in template parameter TEST_CASE(template25); // #2648 - another test for sizeof template parameter TEST_CASE(template26); // #2721 - passing 'char[2]' as template parameter TEST_CASE(template27); // #3350 - removing unused template in macro call TEST_CASE(template28); TEST_CASE(template30); // #3529 - template < template < .. TEST_CASE(template31); // #4010 - reference type TEST_CASE(template32); // #3818 - mismatching template not handled well TEST_CASE(template33); // #3818,#4544 - inner templates in template instantiation not handled well TEST_CASE(template34); // #3706 - namespace => hang TEST_CASE(template35); // #4074 - A<'x'> a; TEST_CASE(template36); // #4310 - passing unknown template instantiation as template argument TEST_CASE(template37); // #4544 - A a; TEST_CASE(template38); // #4832 - crash on C++11 right angle brackets TEST_CASE(template39); // #4742 - freeze TEST_CASE(template40); // #5055 - template specialization outside struct TEST_CASE(template41); // #4710 - const in instantiation not handled perfectly TEST_CASE(template42); // #4878 - variadic templates TEST_CASE(template43); // #5097 - assert due to '>>' not treated as end of template instantiation TEST_CASE(template44); // #5297 - TemplateSimplifier::simplifyCalculations not eager enough TEST_CASE(template45); // #5814 - syntax error reported for valid code TEST_CASE(template46); // #5816 - syntax error reported for valid code TEST_CASE(template47); // #6023 - syntax error reported for valid code TEST_CASE(template48); // #6134 - 100% CPU upon invalid code TEST_CASE(template49); // #6237 - template instantiation TEST_CASE(template50); // #4272 - simple partial specialization TEST_CASE(template51); // #6172 - crash upon valid code TEST_CASE(template52); // #6437 - crash upon valid code TEST_CASE(template53); // #4335 - bail out for valid code TEST_CASE(template54); // #6587 - memory corruption upon valid code TEST_CASE(template55); // #6604 - simplify "const const" to "const" in template instantiations TEST_CASE(template56); // #7117 - const ternary operator simplification as template parameter TEST_CASE(template57); // #7891 TEST_CASE(template58); // #6021 - use after free (deleted tokens in simplifyCalculations) TEST_CASE(template59); // #8051 - TemplateSimplifier::simplifyTemplateInstantiation failure TEST_CASE(template60); // handling of methods outside template definition TEST_CASE(template61); // daca2, kodi TEST_CASE(template_specialization_1); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_specialization_2); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) TEST_CASE(template_unhandled); TEST_CASE(template_default_parameter); TEST_CASE(template_default_type); TEST_CASE(template_typename); TEST_CASE(template_constructor); // #3152 - template constructor is removed TEST_CASE(syntax_error_templates_1); TEST_CASE(template_member_ptr); // Ticket #5786 - crash upon valid code TEST_CASE(template_namespace_1); TEST_CASE(template_namespace_2); TEST_CASE(template_namespace_3); TEST_CASE(template_namespace_4); TEST_CASE(template_namespace_5); // Test TemplateSimplifier::templateParameters TEST_CASE(templateParameters); TEST_CASE(templateNamePosition); TEST_CASE(expandSpecialized); TEST_CASE(templateAlias1); TEST_CASE(templateAlias2); TEST_CASE(templateAlias3); // #8315 // Test TemplateSimplifier::instantiateMatch TEST_CASE(instantiateMatch); } std::string tok(const char code[], bool simplify = true, bool debugwarnings = false, Settings::PlatformType type = Settings::Native) { errout.str(""); settings.debugwarnings = debugwarnings; settings.platform(type); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); if (simplify) tokenizer.simplifyTokenList2(); return tokenizer.tokens()->stringifyList(0, !simplify); } std::string tok(const char code[], const char filename[]) { errout.str(""); settings.debugwarnings = false; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); tokenizer.simplifyTokenList2(); return tokenizer.tokens()->stringifyList(0, false); } void template1() { const char code[] = "template void f(T val) { T a; }\n" "f(10);"; const char expected[] = "f ( 10 ) ; " "void f ( int val ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template2() { const char code[] = "template class Fred { T a; };\n" "Fred fred;"; const char expected[] = "Fred fred ; " "class Fred { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template3() { const char code[] = "template class Fred { T data[sz]; };\n" "Fred fred;"; const char expected[] = "Fred fred ; " "class Fred { float data [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template4() { const char code[] = "template class Fred { Fred(); };\n" "Fred fred;"; const char expected[] = "Fred fred ; " "class Fred { Fred ( ) ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template5() { const char code[] = "template class Fred { };\n" "template Fred::Fred() { }\n" "Fred fred;"; const char expected[] = "template < class T > Fred < T > :: Fred ( ) { } " // <- TODO: this should be removed "Fred fred ; " "class Fred { } ; " "Fred :: Fred ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template6() { const char code[] = "template class Fred { };\n" "Fred fred1;\n" "Fred fred2;"; const char expected[] = "Fred fred1 ; " "Fred fred2 ; " "class Fred { } ;"; ASSERT_EQUALS(expected, tok(code)); } void template7() { // A template class that is not used => no simplification { const char code[] = "template \n" "class ABC\n" "{\n" "public:\n" " typedef ABC m;\n" "};\n"; const char expected[] = "template < class T > class ABC { public: } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template class ABC {\n" "public:\n" " typedef std::vector type;\n" "};\n" "int main() {\n" " ABC::type v;\n" " v.push_back(4);\n" " return 0;\n" "}\n"; const char wanted[] = "template < typename T > class ABC { public: } ; " "int main ( ) { " "std :: vector < int > v ; " "v . push_back ( 4 ) ; " "return 0 ; " "}"; const char current[] = "template < typename T > class ABC { public: } ; " "int main ( ) { " "ABC < int > :: type v ; " "v . push_back ( 4 ) ; " "return 0 ; " "}"; TODO_ASSERT_EQUALS(wanted, current, tok(code)); } { const char code[] = "template class ABC {\n" "public:\n" " typedef std::vector type;\n" " void f()\n" " {\n" " ABC::type v;\n" " v.push_back(4);\n" " }\n" "};\n"; const char expected[] = "template < typename T > class ABC { " "public: void f ( ) { " "ABC < int > :: type v ; " "v . push_back ( 4 ) ; " "} " "} ;"; ASSERT_EQUALS(expected, tok(code)); } } // Template definitions but no usage => no expansion void template8() { const char code[] = "template class A;\n" "template class B;\n" "\n" "typedef A x;\n" "typedef B y;\n" "\n" "template class A {\n" " void f() {\n" " B a = B::g();\n" " T b = 0;\n" " if (b)\n" " b = 0;\n" " }\n" "};\n" "\n" "template inline B h() { return B(); }\n"; ASSERT_EQUALS("template < typename T > class A ; " "template < typename T > class B ; " "template < typename T > class A { void f ( ) { B < T > a ; a = B < T > :: g ( ) ; T b ; b = 0 ; } } ; " "template < typename T > B < T > h ( ) { return B < T > ( ) ; }", tok(code)); ASSERT_EQUALS("class A { template < typename T > int foo ( T d ) ; } ;", tok("class A{ template int foo(T d);};")); } void template9() { const char code[] = "template < typename T > class A { } ;\n" "\n" "void f ( ) {\n" " A < int > a ;\n" "}\n" "\n" "template < typename T >\n" "class B {\n" " void g ( ) {\n" " A < T > b = A < T > :: h ( ) ;\n" " }\n" "} ;\n"; // The expected result.. const char expected[] = "void f ( ) { A a ; } " "template < typename T > class B { void g ( ) { A < T > b ; b = A < T > :: h ( ) ; } } ; " "class A { } ;"; ASSERT_EQUALS(expected, tok(code)); } void template10() { const char code[] = "template T * foo()\n" "{ return new T[ui]; }\n" "\n" "void f ( )\n" "{\n" " foo<3,int>();\n" "}\n"; // The expected result.. const char expected[] = "void f ( ) " "{" " foo<3,int> ( ) ; " "} " "int * foo<3,int> ( ) { return new int [ 3 ] ; }"; ASSERT_EQUALS(expected, tok(code)); } void template11() { const char code[] = "template T * foo()\n" "{ return new T[ui]; }\n" "\n" "void f ( )\n" "{\n" " char * p = foo<3,char>();\n" "}\n"; // The expected result.. const char expected[] = "void f ( ) " "{" " char * p ; p = foo<3,char> ( ) ; " "} " "char * foo<3,char> ( ) { return new char [ 3 ] ; }"; ASSERT_EQUALS(expected, tok(code)); } void template12() { const char code[] = "template \n" "class A : public B\n" "{ };\n" "\n" "void f()\n" "{\n" " A<12,12,11> a;\n" "}\n"; // The expected result.. const char expected[] = "void f ( ) " "{" " A<12,12,11> a ; " "} " "class A<12,12,11> : public B < 12 , 12 , 0 > " "{ } ;"; ASSERT_EQUALS(expected, tok(code)); } void template13() { const char code[] = "class BB {};\n" "\n" "template \n" "class AA\n" "{\n" "public:\n" " static AA create(T* newObject);\n" " static int size();\n" "};\n" "\n" "class CC { public: CC(AA, int) {} };\n" "\n" "class XX {\n" " AA y;\n" "public:\n" " XX();\n" "};\n" "\n" "XX::XX():\n" " y(AA::create(new CC(AA(), 0)))\n" " {}\n" "\n" "int yy[AA::size()];"; // Just run it and check that there are not assertions. tok(code); } void template14() { const char code[] = "template <> void foo()\n" "{ x(); }\n" "\n" "int main()\n" "{\n" "foo();\n" "}\n"; // The expected result.. const char expected[] = "void foo ( ) " "{ x ( ) ; } " "int main ( ) " "{ foo ( ) ; }"; ASSERT_EQUALS(expected, tok(code)); } void template15() { // recursive templates #3130 etc const char code[] = "template void a()\n" "{\n" " a();\n" "}\n" "\n" "template <> void a<0>()\n" "{ }\n" "\n" "int main()\n" "{\n" " a<2>();\n" " return 0;\n" "}\n"; // The expected result.. const char expected[] = "void a<0> ( ) { } " "int main ( ) " "{ a<2> ( ) ; return 0 ; } " "void a<2> ( ) { a<1> ( ) ; } " "void a<1> ( ) { a<0> ( ) ; }"; ASSERT_EQUALS(expected, tok(code)); // #3130 const char code2[] = "template struct vec {\n" " vec() {}\n" " vec(const vec& v) {}\n" // <- never used don't instantiate "};\n" "\n" "vec<4> v;"; const char expected2[] = "vec<4> v ; " "struct vec<4> { " "vec<4> ( ) { } " "vec<4> ( const vec < 4 - 1 > & v ) { } " "} ;"; ASSERT_EQUALS(expected2, tok(code2)); } void template16() { const char code[] = "template void a()\n" "{ }\n" "\n" "template void b()\n" "{ a(); }\n" "\n" "int main()\n" "{\n" " b<2>();\n" " return 0;\n" "}\n"; const char expected[] = "int main ( ) { b<2> ( ) ; return 0 ; } " "void b<2> ( ) { a<2> ( ) ; } " "void a<2> ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template17() { const char code[] = "template\n" "class Fred\n" "{\n" " template\n" " static shared_ptr< Fred > CreateFred()\n" " {\n" " }\n" "};\n" "\n" "shared_ptr i;\n"; // Assert that there is no segmentation fault.. tok(code); } void template18() { const char code[] = "template class foo { T a; };\n" "foo *f;"; const char expected[] = "foo * f ; " "class foo { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template19() { const char code[] = "template T & foo()\n" "{ static T temp; return temp; }\n" "\n" "void f ( )\n" "{\n" " char p = foo();\n" "}\n"; // The expected result.. const char expected[] = "void f ( ) " "{" " char p ; p = foo ( ) ; " "} " "char & foo ( ) { static char temp ; return temp ; }"; ASSERT_EQUALS(expected, tok(code)); } void template20() { // Ticket #1788 - the destructor implementation is lost const char code[] = "template class A\n" "{\n" "public:\n" " ~A();\n" "};\n" "\n" "template A::~A()\n" "{\n" "}\n" "\n" "A a;\n"; // The expected result.. const char expected[] = "template < class T > A < T > :: ~ A ( ) { } " // <- TODO: this should be removed "A a ; " "class A { public: ~ A ( ) ; } ; " "A :: ~ A ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template21() { { const char code[] = "template struct Fred { T a; };\n" "Fred fred;"; const char expected[] = "Fred fred ; " "struct Fred { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template struct Fred { T data[sz]; };\n" "Fred fred;"; const char expected[] = "Fred fred ; " "struct Fred { float data [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template struct Fred { Fred(); };\n" "Fred fred;"; const char expected[] = "Fred fred ; " "struct Fred { Fred ( ) ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template struct Fred { };\n" "Fred fred1;\n" "Fred fred2;"; const char expected[] = "Fred fred1 ; " "Fred fred2 ; " "struct Fred { } ;"; ASSERT_EQUALS(expected, tok(code)); } } void template22() { const char code[] = "template struct Fred { T a; };\n" "Fred fred;"; const char expected[] = "Fred fred ; " "struct Fred { std :: string a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template23() { const char code[] = "template void foo() { }\n" "void bar() {\n" " std::cout << (foo());\n" "}"; const char expected[] = "void bar ( ) {" " std :: cout << ( foo ( ) ) ; " "} " "void foo ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template24() { // #2648 const char code[] = "template struct B\n" "{\n" " int a[n];\n" "};\n" "\n" "template class bitset: B\n" "{};\n" "\n" "bitset<1> z;"; const char expected[] = "bitset<1> z ; " "class bitset<1> : B<4> { } ; " "struct B<4> { int a [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template25() { const char code[] = "template struct B\n" "{\n" " int a[n];\n" "};\n" "\n" "template class bitset: B<((sizeof(int)) ? : 1)>\n" "{};\n" "\n" "bitset<1> z;"; const char actual[] = "template < int n > struct B { int a [ n ] ; } ; " "bitset<1> z ; " "class bitset<1> : B < 4 > { } ;"; const char expected[] = "bitset<1> z ; " "class bitset<1> : B < 4 > { } ; " "struct B < 4 > { int a [ 4 ] ; } ;"; TODO_ASSERT_EQUALS(expected, actual, tok(code)); } void template26() { // #2721 const char code[] = "template\n" "class A { public: T x; };\n" "\n" "template\n" "class C: public A {};\n" "\n" "C<2> a;\n"; // TODO: expand A also ASSERT_EQUALS("template < class T > class A { public: T x ; } ; C<2> a ; class C<2> : public A < char [ 2 ] > { } ;", tok(code)); } void template27() { // #3350 - template inside macro call const char code[] = "X(template class Fred);"; ASSERT_THROW(tok(code), InternalError); } void template28() { // #3226 - inner template const char code[] = "template class Fred {};\n" "Fred > x;\n"; ASSERT_EQUALS("Fred> x ; class Fred { } ; class Fred> { } ;", tok(code)); } void template30() { // #3529 - template < template < .. const char code[] = "template class A, class B> void f(){}"; ASSERT_EQUALS("template < template < class > class A , class B > void f ( ) { }", tok(code)); } void template31() { // #4010 - template reference type const char code[] = "template struct A{}; A a;"; ASSERT_EQUALS("A a ; struct A { } ;", tok(code)); // #7409 - rvalue const char code2[] = "template struct A{}; A a;"; ASSERT_EQUALS("A a ; struct A { } ;", tok(code2)); } void template32() { // #3818 - mismatching template not handled well const char code[] = "template struct A { };\n" "\n" "template \n" "struct B\n" "{\n" " public:\n" " A < int, Pair, int > a;\n" // mismatching parameters => don't instantiate "};\n" "\n" "B b;\n"; ASSERT_EQUALS("template < class T1 , class T2 , class T3 , class T4 > struct A { } ; " "B b ; " "struct B { public: A < int , Pair < int , int > , int > a ; } ;", tok(code)); } void template33() { { // #3818 - inner templates in template instantiation not handled well const char code[] = "template struct A { };\n" "template struct B { };\n" "template struct C { A > > ab; };\n" "C c;"; ASSERT_EQUALS("C c ; " "struct C { A>> ab ; } ; " "struct B> { } ; " // <- redundant.. but nevermind "struct A>> { } ;", tok(code)); } { // #4544 const char code[] = "struct A { };\n" "template struct B { };\n" "template struct C { };\n" "C< B > c;"; ASSERT_EQUALS("struct A { } ; " "template < class T > struct B { } ; " // <- redundant.. but nevermind "C> c ; struct C> { } ;", tok(code)); } } void template34() { // #3706 - namespace => hang const char code[] = "namespace abc {\n" "template struct X { void f(X &x) {} };\n" "}\n" "template <> int X::Y(0);"; ASSERT_EQUALS("namespace abc { " "template < typename T > struct X { void f ( X < T > & x ) { } } ; " "} " "template < > int X < int > :: Y ( 0 ) ;", tok(code)); } void template35() { // #4074 - "A<'x'> a;" is not recognized as template instantiation const char code[] = "template class A {};\n" "A <'x'> a;"; ASSERT_EQUALS("A<'x'> a ; class A<'x'> { } ;", tok(code)); } void template36() { // #4310 - Passing unknown template instantiation as template argument const char code[] = "template struct X { T t; };\n" "template struct Y { Foo < X< Bar > > _foo; };\n" // <- Bar is unknown "Y bar;"; ASSERT_EQUALS("Y bar ; " "struct Y { Foo < X> > _foo ; } ; " "struct X> { Bar < int > t ; } ;", tok(code)); } void template37() { // #4544 - A a; { const char code[] = "class A { };\n" "template class B {};\n" "B b1;\n" "B b2;"; ASSERT_EQUALS("class A { } ; B b1 ; B b2 ; class B { } ;", tok(code)); } { const char code[] = "struct A { };\n" "template class B {};\n" "B b1;\n" "B b2;"; ASSERT_EQUALS("struct A { } ; B b1 ; B b2 ; class B { } ;", tok(code)); } { const char code[] = "enum A { };\n" "template class B {};\n" "B b1;\n" "B b2;"; ASSERT_EQUALS("enum A { } ; B b1 ; B b2 ; class B { } ;", tok(code)); } } void template_unhandled() { // An unhandled template usage should be simplified.. ASSERT_EQUALS("x ( ) ;", tok("x();")); } void template38() { // #4832 - Crash on C++11 right angle brackets const char code[] = "template class A {\n" " T mT;\n" "public:\n" " void foo() {}\n" "};\n" "\n" "int main() {\n" " A> gna1;\n" " A gna2;\n" "}\n"; tok(code); // Don't crash or freeze } void template39() { // #4742 - Used to freeze in 1.60 const char code[] = "template struct vector {" " operator T() const;" "};" "void f() {" " vector> v;" " const vector vi = static_cast>(v);" "}"; tok(code); } void template40() { // #5055 - false negatives when there is template specialization outside struct const char code[] = "struct A {" " template struct X { T t; };" "};" "template<> struct A::X { int *t; };"; ASSERT_EQUALS("struct A { template < typename T > struct X { T t ; } ; } ;", tok(code)); } void template41() { // #4710 - const in template instantiation not handled perfectly const char code1[] = "template struct X { };\n" "void f(const X x) { }"; ASSERT_EQUALS("void f ( const X x ) { } struct X { } ;", tok(code1)); const char code2[] = "template T f(T t) { return t; }\n" "int x() { return f(123); }"; ASSERT_EQUALS("int x ( ) { return f ( 123 ) ; } int f ( int t ) { return t ; }", tok(code2)); } void template42() { // #4878 cpcheck aborts in ext-blocks.cpp (clang testcode) const char code[] = "template\n" "int f0(Args ...args) {\n" " return ^ {\n" " return sizeof...(Args);\n" " }() + ^ {\n" " return sizeof...(args);\n" " }();\n" "}"; tok(code); } void template43() { // #5097 - Assert due to '>>' in 'B>' not being treated as end of template instantation const char code[] = "template struct C { };" "template struct D { static int f() { return C::f(); } };" "template inline int f2() { return D::f(); }" "template int f1(int x, T *) { int id = f2(); return id; }" "template <> struct C < B < A >> {" " static int f() {" " return f1 < B < A >> (0, reinterpret_cast< B *>(E::Int(-1)));" " }" "};"; tok(code); // Don't assert } void template44() { // #5297 tok("template struct StackContainer {" " void foo(int i) {" " if (0 >= 1 && i<0) {}" " }" "};" "template class ZContainer : public StackContainer {};" "struct FGSTensor {};" "class FoldedZContainer : public ZContainer {};"); } void template45() { // #5814 tok("namespace Constants { const int fourtytwo = 42; } " "template struct TypeMath { " " static const int mult = sizeof(T) * U; " "}; " "template struct FOO { " " enum { value = TypeMath::something }; " "};"); ASSERT_EQUALS("", errout.str()); } void template46() { // #5816 tok("template struct A { static const int value = 0; }; " "template struct B { " " enum { value = A::value }; " "};"); ASSERT_EQUALS("", errout.str()); tok("template struct A {}; " "enum { e = sizeof(A) }; " "template struct B {};"); ASSERT_EQUALS("", errout.str()); tok("template struct A { static const int value = 0; }; " "template struct B { typedef int type; }; " "template struct C { " " enum { value = A::type, int>::value }; " "};"); ASSERT_EQUALS("", errout.str()); } void template47() { // #6023 tok("template > class C1 {}; " "class C2 : public C1 {};"); ASSERT_EQUALS("", errout.str()); } void template48() { // #6134 tok("template int f( { } ); " "int foo = f<1>(0);"); ASSERT_EQUALS("", errout.str()); } void template49() { // #6237 const char code[] = "template class Fred { void f(); void g(); };\n" "template void Fred::f() { }\n" "template void Fred::g() { }\n" "template void Fred::f();\n" "template void Fred::g();\n"; const char expected[] = "template < class T > void Fred < T > :: f ( ) { } " "template < class T > void Fred < T > :: g ( ) { } " "template void Fred :: f ( ) ; " "template void Fred :: g ( ) ; " "class Fred { void f ( ) ; void g ( ) ; } ; " "Fred :: f ( ) { } " "Fred :: g ( ) { } " "class Fred { void f ( ) ; void g ( ) ; } ; " "Fred :: f ( ) { } " "Fred :: g ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template50() { // #4272 const char code[] = "template class Fred { void f(); };\n" "template void Fred::f() { }\n" "template<> void Fred::f() { }\n" "template<> void Fred::g() { }\n"; const char expected[] = "template < class T > class Fred { void f ( ) ; } ; " "template < class T > void Fred < T > :: f ( ) { } " "template < > void Fred < float > :: f ( ) { } " "template < > void Fred < int > :: g ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template51() { // #6172 tok("template struct A { " " static void foo() { " " int i = N; " " } " "}; " "void bar() { " " A<0>::foo(); " "}"); } void template52() { // #6437 tok("template int sum() { " " return value + sum(); " "} " "template int calculate_value() { " " return sum(); " "} " "int value = calculate_value<1,1>();"); } void template53() { // #4335 tok("template struct Factorial { " " enum { value = N * Factorial::value }; " "};" "template <> struct Factorial<0> { " " enum { value = 1 }; " "};" "const int x = Factorial<4>::value;", /*simplify=*/true, /*debugwarnings=*/true); ASSERT_EQUALS("", errout.str()); } void template54() { // #6587 tok("template _Tp* fn(); " "template struct A { " " template ())> " " struct B { }; " "}; " "A a;"); } void template55() { // #6604 // Avoid constconstconst in macro instantiations ASSERT_EQUALS( "template < class T > class AtSmartPtr : public ConstCastHelper < AtSmartPtr < const T > , T > { " "friend struct ConstCastHelper < AtSmartPtr < const T > , T > ; " "AtSmartPtr ( const AtSmartPtr < T > & r ) ; " "} ;", tok("template class AtSmartPtr : public ConstCastHelper, T>\n" "{\n" " friend struct ConstCastHelper, T>;\n" " AtSmartPtr(const AtSmartPtr& r);\n" "};")); // Similar problem can also happen with ... ASSERT_EQUALS( "A a ( 0 ) ; struct A { " "A ( int * p ) { p ; } " "} ; " "struct A { " "A ( int * p ) { " "p ; " "} } ;", tok("template struct A\n" "{\n" " A(T* p) {\n" " (A*)(p);\n" " }\n" "};\n" "A a(0);")); } void template56() { // #7117 tok("template struct Foo { " " std::array mfoo; " "}; " "void foo() { " " Foo myFoo; " "}", /*simplify=*/true, /*debugwarnings=*/true); ASSERT_EQUALS("", errout.str()); } void template57() { // #7891 const char code[] = "template struct Test { Test(T); };\n" "Test test( 0 );"; const char exp [] = "Test test ( 0 ) ; " "struct Test { Test ( long ) ; } ;"; ASSERT_EQUALS(exp, tok(code)); } void template58() { // #6021 const char code[] = "template \n" "void TestArithmetic() {\n" " x(1 * CheckedNumeric());\n" "}\n" "void foo() {\n" " TestArithmetic();\n" "}"; const char exp[] = "void foo ( ) {" " TestArithmetic ( ) ; " "} " "void TestArithmetic ( ) {" " x ( CheckedNumeric < int > ( ) ) ; " "}"; ASSERT_EQUALS(exp, tok(code)); } void template59() { // #8051 const char code[] = "template\n" "struct Factorial {\n" " enum FacHelper { value = N * Factorial::value };\n" "};\n" "template <>\n" "struct Factorial<0> {\n" " enum FacHelper { value = 1 };\n" "};\n" "template\n" "int diagonalGroupTest() {\n" " return Factorial::value;\n" "}\n" "int main () {\n" " return diagonalGroupTest<4>();\n" "}"; const char exp[] = "struct Factorial<0> { enum FacHelper { value = 1 } ; } ; " "int main ( ) { return diagonalGroupTest<4> ( ) ; } " "int diagonalGroupTest<4> ( ) { return Factorial<4> :: value ; } " "struct Factorial<4> { enum FacHelper { value = 4 * Factorial<3> :: value } ; } ; " "struct Factorial<3> { enum FacHelper { value = 3 * Factorial<2> :: value } ; } ; " "struct Factorial<2> { enum FacHelper { value = 2 * Factorial<1> :: value } ; } ; " "struct Factorial<1> { enum FacHelper { value = Factorial < 0 > :: value } ; } ;"; ASSERT_EQUALS(exp, tok(code)); } void template60() { // Extracted from Clang testfile const char code[] = "template struct S { typedef int type; };\n" "template void f() {}\n" "template void h() { f::type(0)>(); }\n" "\n" "void j() { h(); }"; const char exp[] = "template < typename T > void f ( ) { } " // <- TODO: This template is not expanded "void j ( ) { h ( ) ; } " "void h ( ) { f < S :: type ( 0 ) > ( ) ; } " "struct S { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template61() { // hang in daca, code extracted from kodi const char code[] = "template struct Foo {};\n" "template struct Bar {\n" " void f1(Bar x) {}\n" " Foo> f2() { }\n" "};\n" "Bar c;"; const char exp[] = "Bar c ; " "struct Bar {" " void f1 ( Bar x ) { }" " Foo> f2 ( ) { } " "} ; " "struct Foo> { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template_specialization_1() { // #7868 - template specialization template struct S> {..}; const char code[] = "template struct C {};\n" "template struct S {a};\n" "template struct S> {b};\n" "S s;"; const char exp[] = "template < typename T > struct C { } ; template < typename T > struct S < C < T > > { b } ; S s ; struct S { a } ;"; ASSERT_EQUALS(exp, tok(code)); } void template_specialization_2() { // #7868 - template specialization template struct S> {..}; const char code[] = "template struct C {};\n" "template struct S {a};\n" "template struct S> {b};\n" "S> s;"; const char exp[] = "template < typename T > struct C { } ; template < typename T > struct S { a } ; S> s ; struct S> { b } ;"; ASSERT_EQUALS(exp, tok(code)); } void template_enum() { const char code1[] = "template \n" "struct Unconst {\n" " typedef T type;\n" "};\n" "template \n" "struct Unconst {\n" " typedef T type;\n" "};\n" "template \n" "struct Unconst {\n" " typedef T& type;\n" "};\n" "template \n" "struct Unconst {\n" " typedef T* type;\n" "};\n" "template \n" "struct type_equal {\n" " enum { value = 0 };\n" "};\n" "template \n" "struct type_equal {\n" " enum { value = 1 };\n" "};\n" "template\n" "struct template_is_const\n" "{\n" " enum {value = !type_equal::type>::value };\n" "};"; const char exp1[] = "template < class T > struct Unconst { } ; " "template < class T > struct Unconst < const T > { } ; " "template < class T > struct Unconst < const T & > { } ; " "template < class T > struct Unconst < T * const > { } ; " "template < class T1 , class T2 > struct type_equal { enum Anonymous0 { value = 0 } ; } ; " "template < class T > struct type_equal < T , T > { enum Anonymous1 { value = 1 } ; } ; " "template < class T > struct template_is_const { enum Anonymous2 { value = ! type_equal < T , Unconst < T > :: type > :: value } ; } ;"; ASSERT_EQUALS(exp1, tok(code1)); } void template_default_parameter() { { const char code[] = "template \n" "class A\n" "{ T ar[n]; };\n" "\n" "void f()\n" "{\n" " A a1;\n" " A a2;\n" "}\n"; // The expected result.. const char expected[] = "void f ( ) " "{" " A a1 ;" " A a2 ; " "} " "class A " "{ int ar [ 2 ] ; } ; " "class A " "{ int ar [ 3 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template \n" "class A\n" "{ T ar[n1+n2]; };\n" "\n" "void f()\n" "{\n" " A a1;\n" " A a2;\n" "}\n"; // The expected result.. const char expected[] = "void f ( ) " "{" " A a1 ;" " A a2 ; " "} " "class A " "{ int ar [ 5 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template \n" "class A\n" "{ T ar[n]; };\n" "\n" "void f()\n" "{\n" " A a1;\n" " A a2;\n" "}\n"; const char wanted[] = "template < class T , int n >" " class A" " { T ar [ n ] ; } ;" " void f ( )" " {" " A a1 ;" " A a2 ;" " }" " class A" " { int ar [ 2 ] ; }" " class A" " { int ar [ 3 ] ; }"; const char current[] = "void f ( ) " "{ " "A < int , ( int ) 2 > a1 ; " "A a2 ; " "} " "class A " "{ int ar [ 3 ] ; } ;"; TODO_ASSERT_EQUALS(wanted, current, tok(code)); } { const char code[] = "class A { }; " "template class B { }; " "template> class C { }; " "template> class D { };"; ASSERT_EQUALS("class A { } ; " "template < class T > class B { } ; " "template < class T1 , class T2 = B < T1 > > class C { } ; " "template < class T1 = A , typename T2 = B < A > > class D { } ;", tok(code)); } { // #7548 const char code[] = "template class DefaultMemory {}; " "template > class thv_table_c {}; " "thv_table_c id_table_m;"; const char exp [] = "template < class T , class U > class DefaultMemory { } ; " "thv_table_c> id_table_m ; " "class thv_table_c> { } ;"; const char curr[] = "template < class T , class U > class DefaultMemory { } ; " "thv_table_c> id_table_m ; " "class thv_table_c> { } ;"; TODO_ASSERT_EQUALS(exp, curr, tok(code)); } } void template_default_type() { const char code[] = "template \n" "class A\n" "{\n" "public:\n" " void foo() {\n" " int a;\n" " a = static_cast(a);\n" " }\n" "};\n" "\n" "template \n" "class B\n" "{\n" "protected:\n" " A a;\n" "};\n" "\n" "class C\n" " : public B\n" "{\n" "};\n"; tok(code); //ASSERT_EQUALS("[file1.cpp:15]: (error) Internal error: failed to instantiate template. The checking continues anyway.\n", errout.str()); ASSERT_EQUALS("", errout.str()); } void template_typename() { { const char code[] = "template \n" "void foo(typename T::t *)\n" "{ }"; // The expected result.. const char expected[] = "template < class T > void foo ( T :: t * ) { }"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "void f() {\n" " x(sizeof typename);\n" " type = 0;\n" "}"; ASSERT_EQUALS("void f ( ) { x ( sizeof ( typename ) ) ; type = 0 ; }", tok(code)); } } void template_constructor() { // #3152 - if template constructor is removed then there might be // "no constructor" false positives const char code[] = "class Fred {\n" " template explicit Fred(T t) { }\n" "}"; ASSERT_EQUALS("class Fred { template < class T > explicit Fred ( T t ) { } }", tok(code)); // #3532 const char code2[] = "class Fred {\n" " template Fred(T t) { }\n" "}"; ASSERT_EQUALS("class Fred { template < class T > Fred ( T t ) { } }", tok(code2)); } void syntax_error_templates_1() { // ok code.. using ">" for a comparison tok("xz> xyz;\n"); ASSERT_EQUALS("", errout.str()); // ok code tok("template operator<(T a, T b) { }\n"); ASSERT_EQUALS("", errout.str()); // ok code (ticket #1984) tok("void f(a) int a;\n" "{ ;x" ASSERT_THROW(tok("x xyz;\n"), InternalError); // bad code ASSERT_THROW(tok("typedef\n" " typename boost::mpl::if_c<\n" " _visitableIndex < boost::mpl::size< typename _Visitables::ConcreteVisitables >::value\n" " , ConcreteVisitable\n" " , Dummy< _visitableIndex >\n" " >::type ConcreteVisitableOrDummy;\n"), InternalError); // code is ok, don't show syntax error tok("struct A {int a;int b};\n" "class Fred {" "public:\n" " Fred() : a({1,2}) {\n" " for (int i=0;i<6;i++);\n" // <- no syntax error " }\n" "private:\n" " A a;\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void template_member_ptr() { // Ticket #5786 tok("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); tok("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); tok("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); tok("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); } void template_namespace_1() { // #6570 const char code[] = "namespace {\n" " template void Fred(T value) { }\n" "}\n" "Fred(123);"; ASSERT_EQUALS("namespace { } " "Fred ( 123 ) ; " "void Fred ( int value ) { }", tok(code)); } void template_namespace_2() { // #8283 const char code[] = "namespace X {\n" " template struct S { };\n" "}\n" "X::S s;"; ASSERT_EQUALS("X::S s ; " "struct X::S { } ;", tok(code)); } void template_namespace_3() { const char code[] = "namespace test16 {\n" " template struct foo {\n" " static void *bar();\n" " };\n" " void *test() { return foo::bar(); }\n" "}"; ASSERT_EQUALS("namespace test16 {" " void * test ( ) {" " return test16::foo :: bar ( ) ;" " } " "} " "struct test16::foo {" " static void * bar ( ) ; " "} ;", tok(code)); } void template_namespace_4() { const char code[] = "namespace foo {\n" " template class A { void dostuff() {} };\n" " struct S : public A {\n" " void f() {\n" " A::dostuff();\n" " }\n" " };\n" "}"; ASSERT_EQUALS("namespace foo {" " struct S : public foo::A {" " void f ( ) {" " foo::A :: dostuff ( ) ;" " }" " } ; " "} " "class foo::A { void dostuff ( ) { } } ;", tok(code)); } void template_namespace_5() { const char code[] = "template struct S {};\n" "namespace X { S s; }"; ASSERT_EQUALS("namespace X { S s ; } struct S { } ;", tok(code)); } unsigned int templateParameters(const char code[]) { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp", ""); return TemplateSimplifier::templateParameters(tokenizer.tokens()->next()); } void templateParameters() { // Test that the function TemplateSimplifier::templateParameters works ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(0U, templateParameters("X>x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(0U, templateParameters("X<...> x;")); ASSERT_EQUALS(0U, templateParameters("X x;")); // Invalid syntax ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(0U, templateParameters("X x;")); // Invalid syntax ASSERT_EQUALS(2U, templateParameters("X x;")); ASSERT_EQUALS(2U, templateParameters("X x;")); ASSERT_EQUALS(3U, templateParameters("X x;")); TODO_ASSERT_EQUALS(1U, 0U, templateParameters("X x;")); // Mishandled valid syntax TODO_ASSERT_EQUALS(2U, 0U, templateParameters("X x;")); // Mishandled valid syntax ASSERT_EQUALS(2U, templateParameters("X<1, T> x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(2U, templateParameters("X=0> x;")); ASSERT_EQUALS(3U, templateParameters("X=0, i - 2> x;")); } // Helper function to unit test TemplateSimplifier::getTemplateNamePosition int templateNamePositionHelper(const char code[], unsigned offset = 0, bool onlyCreateTokens = false) { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); if (onlyCreateTokens) tokenizer.createTokens(istr, "test.cpp"); else tokenizer.tokenize(istr, "test.cpp", emptyString); const Token *_tok = tokenizer.tokens(); for (unsigned i = 0 ; i < offset ; ++i) _tok = _tok->next(); return TemplateSimplifier::getTemplateNamePosition(_tok); } void templateNamePosition() { // Template class ASSERT_EQUALS(2, templateNamePositionHelper("template class A {};", 4)); ASSERT_EQUALS(2, templateNamePositionHelper("template struct A {};", 4)); ASSERT_EQUALS(2, templateNamePositionHelper("template class A : B {};", 4)); ASSERT_EQUALS(2, templateNamePositionHelper("template struct A : B {};", 4)); // Template function definitions ASSERT_EQUALS(2, templateNamePositionHelper("template unsigned foo() { return 0; }", 4)); ASSERT_EQUALS(3, templateNamePositionHelper("template unsigned* foo() { return 0; }", 4)); ASSERT_EQUALS(3, templateNamePositionHelper("template const unsigned foo() { return 0; }", 4)); ASSERT_EQUALS(4, templateNamePositionHelper("template const unsigned& foo() { return 0; }", 4)); // Class template members ASSERT_EQUALS(4, templateNamePositionHelper("class A { template unsigned foo(); }; " "template unsigned A::foo() { return 0; }", 19)); ASSERT_EQUALS(5, templateNamePositionHelper("class A { template const unsigned foo(); }; " "template const unsigned A::foo() { return 0; }", 20)); TODO_ASSERT_EQUALS(7, -1, templateNamePositionHelper("class A { class B { template const unsigned foo(); }; } ; " "template const unsigned A::B::foo() { return 0; }", 25)); // Template class member ASSERT_EQUALS(6, templateNamePositionHelper("template class A { A(); }; " "template A::A() {}", 18)); ASSERT_EQUALS(8, templateNamePositionHelper("template class A { A(); }; " "template A::A() {}", 24)); ASSERT_EQUALS(7, templateNamePositionHelper("template class A { unsigned foo(); }; " "template unsigned A::foo() { return 0; }", 19)); ASSERT_EQUALS(9, templateNamePositionHelper("template class A { unsigned foo(); }; " "template unsigned A::foo() { return 0; }", 25)); ASSERT_EQUALS(9, templateNamePositionHelper("template class A { unsigned foo(); }; " "template unsigned A::foo() { return 0; }", 25, /*onlyCreateTokens=*/true)); ASSERT_EQUALS(12, templateNamePositionHelper("template class v {}; " "template class A { unsigned foo(); }; " "template<> unsigned A >::foo() { return 0; }", 30, /*onlyCreateTokens=*/true)); } void expandSpecialized() { ASSERT_EQUALS("class A { } ;", tok("template<> class A {};")); 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)); } void templateAlias3() { // #8315 const char code[] = "template struct Tag {};\n" "template using SPtr = std::shared_ptr)>;\n" "SPtr<0> s;"; const char expected[] = "; std :: shared_ptr < void ( Tag<0> ) > s ; struct Tag<0> { } ;"; ASSERT_EQUALS(expected, tok(code)); } unsigned int instantiateMatch(const char code[], const std::size_t numberOfArguments, const char patternAfter[]) { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp", ""); return TemplateSimplifier::instantiateMatch(tokenizer.tokens(), numberOfArguments, patternAfter); } void instantiateMatch() { // Ticket #8175 ASSERT_EQUALS(false, instantiateMatch("ConvertHelper < From, To > c ;", 2, ":: %name% (")); ASSERT_EQUALS(true, instantiateMatch("ConvertHelper < From, To > :: Create ( ) ;", 2, ":: %name% (")); ASSERT_EQUALS(false, instantiateMatch("integral_constant < bool, sizeof ( ConvertHelper < From, To > :: Create ( ) ) > ;", 2, ":: %name% (")); ASSERT_EQUALS(false, instantiateMatch("integral_constant < bool, sizeof ( ns :: ConvertHelper < From, To > :: Create ( ) ) > ;", 2, ":: %name% (")); } }; REGISTER_TEST(TestSimplifyTemplate)