/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2024 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 "errortypes.h" #include "fixture.h" #include "helpers.h" #include "platform.h" #include "settings.h" #include "templatesimplifier.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include #include class TestSimplifyTemplate : public TestFixture { public: TestSimplifyTemplate() : TestFixture("TestSimplifyTemplate") {} private: // If there are unused templates, keep those const Settings settings = settingsBuilder().severity(Severity::portability).build(); void run() override { 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(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(template62); // #8314 - inner template instantiation TEST_CASE(template63); // #8576 - qualified type TEST_CASE(template64); // #8683 TEST_CASE(template65); // #8321 TEST_CASE(template66); // #8725 TEST_CASE(template67); // #8122 TEST_CASE(template68); // union TEST_CASE(template69); // #8791 TEST_CASE(template70); // #5289 TEST_CASE(template71); // #8821 TEST_CASE(template72); TEST_CASE(template73); TEST_CASE(template74); TEST_CASE(template75); TEST_CASE(template76); TEST_CASE(template77); TEST_CASE(template78); TEST_CASE(template79); // #5133 TEST_CASE(template80); TEST_CASE(template81); TEST_CASE(template82); // #8603 TEST_CASE(template83); // #8867 TEST_CASE(template84); // #8880 TEST_CASE(template85); // #8902 crash TEST_CASE(template86); // crash TEST_CASE(template87); TEST_CASE(template88); // #6183 TEST_CASE(template89); // #8917 TEST_CASE(template90); // crash TEST_CASE(template91); TEST_CASE(template92); TEST_CASE(template93); // crash TEST_CASE(template94); // #8927 crash TEST_CASE(template95); // #7417 TEST_CASE(template96); // #7854 TEST_CASE(template97); TEST_CASE(template98); // #8959 TEST_CASE(template99); // #8960 TEST_CASE(template100); // #8967 TEST_CASE(template101); // #8968 TEST_CASE(template102); // #9005 TEST_CASE(template103); TEST_CASE(template104); // #9021 TEST_CASE(template105); // #9076 TEST_CASE(template106); TEST_CASE(template107); // #8663 TEST_CASE(template108); // #9109 TEST_CASE(template109); // #9144 TEST_CASE(template110); TEST_CASE(template111); // crash TEST_CASE(template112); // #9146 syntax error TEST_CASE(template113); TEST_CASE(template114); // #9155 TEST_CASE(template115); // #9153 TEST_CASE(template116); // #9178 TEST_CASE(template117); TEST_CASE(template118); TEST_CASE(template119); // #9186 TEST_CASE(template120); TEST_CASE(template121); // #9193 TEST_CASE(template122); // #9147 TEST_CASE(template123); // #9183 TEST_CASE(template124); // #9197 TEST_CASE(template125); TEST_CASE(template126); // #9217 TEST_CASE(template127); // #9225 TEST_CASE(template128); // #9224 TEST_CASE(template129); TEST_CASE(template130); // #9246 TEST_CASE(template131); // #9249 TEST_CASE(template132); // #9250 TEST_CASE(template133); TEST_CASE(template134); TEST_CASE(template135); TEST_CASE(template136); // #9287 TEST_CASE(template137); // #9288 TEST_CASE(template138); TEST_CASE(template139); TEST_CASE(template140); TEST_CASE(template141); // #9337 TEST_CASE(template142); // #9338 TEST_CASE(template143); TEST_CASE(template144); // #9046 TEST_CASE(template145); // syntax error TEST_CASE(template146); // syntax error TEST_CASE(template147); // syntax error TEST_CASE(template148); // syntax error TEST_CASE(template149); // unknown macro TEST_CASE(template150); // syntax error TEST_CASE(template151); // crash TEST_CASE(template152); // #9467 TEST_CASE(template153); // #9483 TEST_CASE(template154); // #9495 TEST_CASE(template155); // #9539 TEST_CASE(template156); TEST_CASE(template157); // #9854 TEST_CASE(template158); // daca crash TEST_CASE(template159); // #9886 TEST_CASE(template160); TEST_CASE(template161); TEST_CASE(template162); TEST_CASE(template163); // #9685 syntax error TEST_CASE(template164); // #9394 TEST_CASE(template165); // #10032 syntax error TEST_CASE(template166); // #10081 hang TEST_CASE(template167); TEST_CASE(template168); TEST_CASE(template169); TEST_CASE(template170); // crash TEST_CASE(template171); // crash TEST_CASE(template172); // #10258 crash TEST_CASE(template173); // #10332 crash TEST_CASE(template174); // #10506 hang TEST_CASE(template175); // #10908 TEST_CASE(template176); // #11146 TEST_CASE(template177); TEST_CASE(template178); 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_specialization_3); 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_forward_declared_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_CASE(template_namespace_6); TEST_CASE(template_namespace_7); // #8768 TEST_CASE(template_namespace_8); TEST_CASE(template_namespace_9); TEST_CASE(template_namespace_10); TEST_CASE(template_namespace_11); // #7145 TEST_CASE(template_pointer_type); TEST_CASE(template_array_type); // Test TemplateSimplifier::templateParameters TEST_CASE(templateParameters); TEST_CASE(templateNamePosition); TEST_CASE(findTemplateDeclarationEnd); TEST_CASE(getTemplateParametersInDeclaration); TEST_CASE(expandSpecialized1); TEST_CASE(expandSpecialized2); TEST_CASE(expandSpecialized3); // #8671 TEST_CASE(expandSpecialized4); TEST_CASE(expandSpecialized5); // #10494 TEST_CASE(templateAlias1); TEST_CASE(templateAlias2); TEST_CASE(templateAlias3); // #8315 TEST_CASE(templateAlias4); // #9070 TEST_CASE(templateAlias5); // Test TemplateSimplifier::instantiateMatch TEST_CASE(instantiateMatchTest); TEST_CASE(templateParameterWithoutName); // #8602 Template default parameter without name yields syntax error TEST_CASE(templateTypeDeduction1); // #8962 TEST_CASE(templateTypeDeduction2); TEST_CASE(templateTypeDeduction3); TEST_CASE(templateTypeDeduction4); // #9983 TEST_CASE(templateTypeDeduction5); TEST_CASE(simplifyTemplateArgs1); TEST_CASE(simplifyTemplateArgs2); TEST_CASE(simplifyTemplateArgs3); TEST_CASE(template_variadic_1); // #9144 TEST_CASE(template_variadic_2); // #4349 TEST_CASE(template_variadic_3); // #6172 TEST_CASE(template_variadic_4); TEST_CASE(template_variable_1); TEST_CASE(template_variable_2); TEST_CASE(template_variable_3); TEST_CASE(template_variable_4); TEST_CASE(simplifyDecltype); TEST_CASE(castInExpansion); TEST_CASE(fold_expression_1); TEST_CASE(fold_expression_2); TEST_CASE(fold_expression_3); TEST_CASE(fold_expression_4); TEST_CASE(concepts1); TEST_CASE(requires1); TEST_CASE(requires2); TEST_CASE(requires3); TEST_CASE(requires4); TEST_CASE(requires5); TEST_CASE(explicitBool1); TEST_CASE(explicitBool2); } #define tok(...) tok_(__FILE__, __LINE__, __VA_ARGS__) template std::string tok_(const char* file, int line, const char (&code)[size], bool debugwarnings = false, Platform::Type type = Platform::Type::Native) { const Settings settings1 = settingsBuilder(settings).library("std.cfg").debugwarnings(debugwarnings).platform(type).build(); SimpleTokenizer tokenizer(settings1, *this); ASSERT_LOC(tokenizer.tokenize(code), file, line); return tokenizer.tokens()->stringifyList(nullptr, true); } void template1() { const char code[] = "template T f(T val) { T a; }\n" "f(10);"; const char expected[] = "int f ( int val ) ; " "f ( 10 ) ; " "int f ( int val ) { int a ; }"; ASSERT_EQUALS(expected, tok(code)); } void template2() { const char code[] = "template class Fred { T a; };\n" "Fred fred;"; const char expected[] = "class Fred ; " "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[] = "class Fred ; " "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[] = "class Fred ; " "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[] = "class Fred ; " "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[] = "class Fred ; " "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[] = "class ABC ; " "int main ( ) { " "std :: vector < int > v ; " "v . push_back ( 4 ) ; " "return 0 ; " "} " "class ABC { public: } ;"; const char current[] = "class ABC ; " "int main ( ) { " "ABC :: type v ; " "v . push_back ( 4 ) ; " "return 0 ; " "} " "class ABC { public: } ;"; 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 ; if ( 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[] = "class A ; " "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[] = "int * foo<3,int> ( ) ; " "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[] = "char * foo<3,char> ( ) ; " "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"; const char expected[] = "class A<12,12,11> ; " "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" "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()];"; const char expected[] = "class BB { } ; " "class AA ; " "class AA ; " "class CC { public: CC ( AA , int ) { } } ; " "class XX { " "AA y ; " "public: " "XX ( ) ; " "} ; " "XX :: XX ( ) : " "y ( AA :: create ( new CC ( AA ( ) , 0 ) ) ) " "{ } " "int yy [ AA :: size ( ) ] ; " "class AA { " "public: " "static AA create ( BB * newObject ) ; " "static int size ( ) ; " "} ; " "class AA { " "public: " "static AA create ( CC * newObject ) ; " "static int size ( ) ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void template14() { const char code[] = "template <> void foo()\n" "{ x(); }\n" "\n" "int main()\n" "{\n" "foo();\n" "}\n"; const char expected[] = "void foo ( ) ; " "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> ( ) ; " "void a<2> ( ) ; " "void a<1> ( ) ; " "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[] = "struct vec<4> ; " "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[] = "void a<2> ( ) ; " "void b<2> ( ) ; " "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"; const char expected[] = "template < class T > " "class Fred " "{ " "template < class T > " "static shared_ptr < Fred < T > > CreateFred ( ) " "{ " "} " "} ; " "shared_ptr < int > i ;"; ASSERT_EQUALS(expected, tok(code)); } void template18() { const char code[] = "template class foo { T a; };\n" "foo *f;"; const char expected[] = "class foo ; " "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[] = "char & foo ( ) ; " "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 { public: ~A(); };\n" "template A::~A() {}\n" "A a;\n"; // The expected result.. const char expected[] = "class A ; " "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[] = "struct Fred ; " "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[] = "struct Fred ; " "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[] = "struct Fred ; " "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[] = "struct Fred ; " "Fred fred1 ; " "Fred fred2 ; " "struct Fred { } ;"; ASSERT_EQUALS(expected, tok(code)); } } void template22() { const char code[] = "template struct Fred { T a; };\n" "Fred<:string> fred;"; const char expected[] = "struct Fred<:string> ; " "Fred<:string> fred ; " "struct Fred<:string> { 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 foo ( ) ; " "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[] = "struct B<4> ; " "class bitset<1> ; " "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 expected[] = "struct B<4> ; " "class bitset<1> ; " "bitset<1> z ; " "class bitset<1> : B<4> { } ; " "struct B<4> { int a [ 4 ] ; } ;"; ASSERT_EQUALS(expected, 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"; ASSERT_EQUALS("class A ; class C<2> ; C<2> a ; class C<2> : public A { } ; class A { public: char [ 2 ] x ; } ;", tok(code)); } void template27() { // #3350 - template inside macro call const char code[] = "X(template class Fred);"; ASSERT_THROW_INTERNAL(tok(code), SYNTAX); } void template28() { // #3226 - inner template const char code[] = "template class Fred {};\n" "Fred > x;\n"; ASSERT_EQUALS("class Fred ; " "class Fred> ; " "Fred> x ; " "class Fred { } ; " "class Fred> { } ;", tok(code)); } void template30() { // #3529 - template < template < .. const char code[] = "template