From d58d4273f94833ee2fcc057a235aa904ee811071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 11 May 2019 13:00:03 +0200 Subject: [PATCH] Cleaning up unsimplified templates --- lib/templatesimplifier.cpp | 33 +++++++++++++++++++++++++++++++++ lib/templatesimplifier.h | 6 ++++++ lib/tokenize.cpp | 2 ++ test/testautovariables.cpp | 5 +++-- test/testcondition.cpp | 3 --- test/testconstructors.cpp | 10 ---------- test/testgarbage.cpp | 2 +- test/testnullpointer.cpp | 15 --------------- test/testother.cpp | 14 ++++---------- test/testsimplifytemplate.cpp | 33 ++++++++++++++++++++++++++++++--- test/testsimplifytokens.cpp | 2 ++ test/testsimplifytypedef.cpp | 6 +++++- test/testsimplifyusing.cpp | 6 +++++- test/testsymboldatabase.cpp | 1 + test/testtokenize.cpp | 4 ++++ test/testutils.h | 1 + test/testvarid.cpp | 2 ++ 17 files changed, 99 insertions(+), 46 deletions(-) diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index d6b844179..2ddad9ba5 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -212,6 +212,39 @@ void TemplateSimplifier::cleanupAfterSimplify() } } +void TemplateSimplifier::removeTemplates() +{ + for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { + if (!Token::simpleMatch(tok, "template <")) + continue; + if (tok->previous() && !Token::Match(tok->previous(), "[;}]")) + continue; + Token *endToken = tok; + while (nullptr != (endToken = endToken->next())) { + if (endToken->str() == ";") + break; + if (Token::Match(endToken, "[})]]")) { + endToken = nullptr; + break; + } + if (Token::Match(endToken, "[<([]") && endToken->link()) + endToken = endToken->link(); + else if (endToken->str() == "{") { + endToken = endToken->link(); + break; + } + } + if (!endToken) + continue; + Token::eraseTokens(tok, endToken); + tok = endToken; + tok->deletePrevious(); + if (tok->str() == "}") { + tok->str(";"); + tok->link(nullptr); + } + } +} void TemplateSimplifier::checkComplicatedSyntaxErrorsInTemplates() { diff --git a/lib/templatesimplifier.h b/lib/templatesimplifier.h index 0f09b7b53..f973ada52 100644 --- a/lib/templatesimplifier.h +++ b/lib/templatesimplifier.h @@ -53,6 +53,12 @@ public: */ void cleanupAfterSimplify(); + /** + * Remove templates, can be used after the simplifications to remove + * the templates that failed to be expanded. + */ + void removeTemplates(); + /** */ void checkComplicatedSyntaxErrorsInTemplates(); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index c9bc35977..99a5b518c 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -4442,6 +4442,8 @@ bool Tokenizer::simplifyTokenList1(const char FileName[]) // this function will just fix so that the syntax is corrected. validate(); // #6847 - invalid code mTemplateSimplifier->cleanupAfterSimplify(); + if (!mSettings->checkUnusedTemplates) + mTemplateSimplifier->removeTemplates(); } // Simplify pointer to standard types (C only) diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index 0f99a5472..fdfddabe9 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -1867,10 +1867,11 @@ private: "}\n" "auto g() {\n" " std::vector v;\n" - " return by_value(v.begin());\n" + " return by_value(v.front());\n" "}\n"); - ASSERT_EQUALS( + TODO_ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:7] -> [test.cpp:3] -> [test.cpp:3] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", + "", errout.str()); check("auto by_ref(int& x) {\n" diff --git a/test/testcondition.cpp b/test/testcondition.cpp index 540c72dec..833fb4a14 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -2079,9 +2079,6 @@ private: check("void f1(const std::string &s) { if(s.empty()) if(s.size() > 42) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); - check("template void f1(const T &s) { if(s.size() > 42) if(s.empty()) {}} "); - ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); - check("void f2(const std::wstring &s) { if(s.empty()) if(s.size() > 42) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index fcce2e1fa..83e94e6a3 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -347,16 +347,6 @@ private: " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); - - check("template struct A {\n" - " A() : x(0) { }\n" - " A(const T & t) : x(t.x) { }\n" - "private:\n" - " int x;\n" - " int y;\n" - "};"); - ASSERT_EQUALS("[test.cpp:2]: (warning) Member variable 'A::y' is not initialized in the constructor.\n" - "[test.cpp:3]: (warning) Member variable 'A::y' is not initialized in the constructor.\n", errout.str()); } void simple7() { // ticket #4531 diff --git a/test/testgarbage.cpp b/test/testgarbage.cpp index df970843c..a06270a07 100644 --- a/test/testgarbage.cpp +++ b/test/testgarbage.cpp @@ -1365,7 +1365,7 @@ private: ); // #3449 - ASSERT_EQUALS("template < typename T > struct A ;\n" + ASSERT_EQUALS(";\n" "struct B { template < typename T > struct C } ;\n" "{ } ;", checkCode("template struct A;\n" diff --git a/test/testnullpointer.cpp b/test/testnullpointer.cpp index 6922e11df..d77aea52f 100644 --- a/test/testnullpointer.cpp +++ b/test/testnullpointer.cpp @@ -67,7 +67,6 @@ private: TEST_CASE(nullpointer24); // #5082 fp: chained assignment TEST_CASE(nullpointer25); // #5061 TEST_CASE(nullpointer26); // #3589 - TEST_CASE(nullpointer27); // #6568 TEST_CASE(nullpointer28); // #6491 TEST_CASE(nullpointer30); // #6392 TEST_CASE(nullpointer31); // #8482 @@ -1322,20 +1321,6 @@ private: ASSERT_EQUALS("", errout.str()); } - void nullpointer27() { // #6568 - check("template\n" - "class Foo {\n" - " Foo& operator = ( Type* );\n" - "};\n" - "template\n" - "Foo& Foo::operator = ( Type* pointer_ ) {\n" - " pointer_=NULL;\n" - " *pointer_=0;\n" - " return *this;\n" - "}"); - ASSERT_EQUALS("[test.cpp:8]: (error) Null pointer dereference: pointer_\n", errout.str()); - } - void nullpointer28() { // #6491 check("typedef struct { int value; } S;\n" "int f(const S *s) { \n" diff --git a/test/testother.cpp b/test/testother.cpp index 3eab8035e..ece8f30fa 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -5005,11 +5005,6 @@ private: ASSERT_EQUALS("", errout.str()); } - check("template void foo(unsigned int x) {\n" - "if (x <= 0);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); - // #8836 check("uint32_t value = 0xFUL;\n" "void f() {\n" @@ -7457,12 +7452,11 @@ private: } void forwardAndUsed() { - check("template\n" - "void f(T && t) {\n" - " g(std::forward(t));\n" - " T s = t;\n" + check("void f(Foo && foo) {\n" + " g(std::forward(foo));\n" + " Foo s = foo;\n" "}"); - ASSERT_EQUALS("[test.cpp:4]: (warning) Access of forwarded variable 't'.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (warning) Access of forwarded variable 'foo'.\n", errout.str()); } void funcArgNamesDifferent() { diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 6351f1d51..335dd7ae7 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -38,6 +38,7 @@ private: void run() OVERRIDE { settings.addEnabled("portability"); + settings.checkUnusedTemplates = true; TEST_CASE(template1); TEST_CASE(template2); @@ -191,6 +192,8 @@ private: TEST_CASE(templateTypeDeduction2); TEST_CASE(simplifyTemplateArgs); + + TEST_CASE(removeTemplates); } std::string tok(const char code[], bool debugwarnings = false, Settings::PlatformType type = Settings::Native) { @@ -386,7 +389,8 @@ private: // The expected result.. const char expected[] = "class A ; " - "void f ( ) { A 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)); @@ -1385,6 +1389,7 @@ private: "template C3::C3(const C3 &v) { C1 c1; }\n" "C3 c3;"; const char exp[] = "struct C1 ; " + "template < class T > void f ( ) { x = y ? ( C1 < int > :: allocate ( 1 ) ) : 0 ; } " "class C3 ; " "C3 c3 ; " "class C3 { } ; " @@ -2392,6 +2397,7 @@ private: "template class Fred {};\n" "ObjectCache _cache;"; const char exp[] = "class ObjectCache ; " + "template < typename T > class Fred { } ; " "ObjectCache _cache ; " "class ObjectCache { } ;"; ASSERT_EQUALS(exp, tok(code)); @@ -2499,7 +2505,8 @@ private: "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 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)); } @@ -2739,7 +2746,7 @@ private: const char code[] = "class Fred {\n" " template explicit Fred(T t) { }\n" "}"; - ASSERT_EQUALS("class Fred { }", tok(code)); + ASSERT_EQUALS("class Fred { template < class T > explicit Fred ( T t ) { } }", tok(code)); // #3532 const char code2[] = "class Fred {\n" @@ -3562,6 +3569,26 @@ private: ASSERT_EQUALS("foo = false ; foo ;", tok("template foo = N; foo < ( 1 - 1) ? true : false >;")); } + std::string removeTemplates(const char code[]) { + Settings settings2; + settings2.checkUnusedTemplates = false; + + errout.str(""); + + Tokenizer tokenizer(&settings2, this); + + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + return tokenizer.tokens()->stringifyList(0, true); + } + + void removeTemplates() { + // #9057 + ASSERT_EQUALS(";", removeTemplates("template using foo = enable_if_t() && sizeof(int) == 4, int>;\n" + "template > struct bar {};\n" + "template using baz = bar;\n")); + } }; REGISTER_TEST(TestSimplifyTemplate) diff --git a/test/testsimplifytokens.cpp b/test/testsimplifytokens.cpp index a2398605a..200fb160f 100644 --- a/test/testsimplifytokens.cpp +++ b/test/testsimplifytokens.cpp @@ -43,7 +43,9 @@ private: LOAD_LIB_2(settings_std.library, "std.cfg"); LOAD_LIB_2(settings_windows.library, "windows.cfg"); settings0.addEnabled("portability"); + settings0.checkUnusedTemplates = true; settings1.addEnabled("style"); + settings1.checkUnusedTemplates = true; settings_windows.addEnabled("portability"); // Make sure the Tokenizer::simplifyTokenList works. diff --git a/test/testsimplifytypedef.cpp b/test/testsimplifytypedef.cpp index 4ed3a6d67..da14bfb2f 100644 --- a/test/testsimplifytypedef.cpp +++ b/test/testsimplifytypedef.cpp @@ -41,6 +41,9 @@ private: void run() OVERRIDE { settings0.addEnabled("style"); settings2.addEnabled("style"); + settings0.checkUnusedTemplates = true; + settings1.checkUnusedTemplates = true; + settings2.checkUnusedTemplates = true; TEST_CASE(simplifyTypedef1) TEST_CASE(simplifyTypedef2) @@ -2527,7 +2530,8 @@ private: "template struct c; " "template struct d { enum { e = c::f }; };"; const char exp [] = "class a ; " - "template < long , class > struct c ;"; + "template < long , class > struct c ; " + "template < int g > struct d { enum Anonymous0 { e = c < g , int ( a :: * ) > :: f } ; } ;"; ASSERT_EQUALS(exp, tok(code, false)); } diff --git a/test/testsimplifyusing.cpp b/test/testsimplifyusing.cpp index 4df2dc7ff..88623ca9e 100644 --- a/test/testsimplifyusing.cpp +++ b/test/testsimplifyusing.cpp @@ -41,6 +41,9 @@ private: void run() OVERRIDE { settings0.addEnabled("style"); settings2.addEnabled("style"); + settings0.checkUnusedTemplates = true; + settings1.checkUnusedTemplates = true; + settings2.checkUnusedTemplates = true; TEST_CASE(simplifyUsing1); TEST_CASE(simplifyUsing2); @@ -489,7 +492,8 @@ private: "class c { " "int i ; i = 0 ; " "c ( ) { i -- ; } " - "} ;"; + "} ; " + "template < class T > class s { } ;"; ASSERT_EQUALS(exp, tok(code, true, Settings::Win64)); } diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 91918f14c..7dcd216cd 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -106,6 +106,7 @@ private: void run() OVERRIDE { LOAD_LIB_2(settings1.library, "std.cfg"); + settings1.checkUnusedTemplates = true; settings2.platform(Settings::Unspecified); TEST_CASE(array); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 2c51bd322..01e365d87 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -47,6 +47,10 @@ private: void run() OVERRIDE { LOAD_LIB_2(settings_windows.library, "windows.cfg"); + settings0.checkUnusedTemplates = true; + settings1.checkUnusedTemplates = true; + settings2.checkUnusedTemplates = true; + TEST_CASE(tokenize1); TEST_CASE(tokenize2); TEST_CASE(tokenize3); diff --git a/test/testutils.h b/test/testutils.h index 5ce5a7d3f..e40e4a3d5 100644 --- a/test/testutils.h +++ b/test/testutils.h @@ -32,6 +32,7 @@ private: public: explicit givenACodeSampleToTokenize(const char sample[], bool createOnly = false, bool cpp = true) : _tokenizer(&_settings, 0) { + _settings.checkUnusedTemplates = true; std::istringstream iss(sample); if (createOnly) _tokenizer.list.createTokens(iss, cpp ? "test.cpp" : "test.c"); diff --git a/test/testvarid.cpp b/test/testvarid.cpp index 82d7588c3..3beafda5e 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -199,6 +199,7 @@ private: errout.str(""); Settings settings; + settings.checkUnusedTemplates = true; settings.platform(Settings::Unix64); settings.standards.c = Standards::C89; settings.standards.cpp = Standards::CPP11; @@ -218,6 +219,7 @@ private: errout.str(""); Settings settings; + settings.checkUnusedTemplates = true; settings.platform(Settings::Unix64); settings.standards.c = Standards::C89; settings.standards.cpp = Standards::CPP11;