Cleaning up unsimplified templates

This commit is contained in:
Daniel Marjamäki 2019-05-11 13:00:03 +02:00
parent 8298937077
commit d58d4273f9
17 changed files with 99 additions and 46 deletions

View File

@ -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() void TemplateSimplifier::checkComplicatedSyntaxErrorsInTemplates()
{ {

View File

@ -53,6 +53,12 @@ public:
*/ */
void cleanupAfterSimplify(); void cleanupAfterSimplify();
/**
* Remove templates, can be used after the simplifications to remove
* the templates that failed to be expanded.
*/
void removeTemplates();
/** /**
*/ */
void checkComplicatedSyntaxErrorsInTemplates(); void checkComplicatedSyntaxErrorsInTemplates();

View File

@ -4442,6 +4442,8 @@ bool Tokenizer::simplifyTokenList1(const char FileName[])
// this function will just fix so that the syntax is corrected. // this function will just fix so that the syntax is corrected.
validate(); // #6847 - invalid code validate(); // #6847 - invalid code
mTemplateSimplifier->cleanupAfterSimplify(); mTemplateSimplifier->cleanupAfterSimplify();
if (!mSettings->checkUnusedTemplates)
mTemplateSimplifier->removeTemplates();
} }
// Simplify pointer to standard types (C only) // Simplify pointer to standard types (C only)

View File

@ -1867,10 +1867,11 @@ private:
"}\n" "}\n"
"auto g() {\n" "auto g() {\n"
" std::vector<int> v;\n" " std::vector<int> v;\n"
" return by_value(v.begin());\n" " return by_value(v.front());\n"
"}\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", "[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()); errout.str());
check("auto by_ref(int& x) {\n" check("auto by_ref(int& x) {\n"

View File

@ -2079,9 +2079,6 @@ private:
check("void f1(const std::string &s) { if(s.empty()) if(s.size() > 42) {}} "); 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()); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str());
check("template<class T> 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) {}} "); 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()); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str());

View File

@ -347,16 +347,6 @@ private:
" int x;\n" " int x;\n"
"};"); "};");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
check("template <class T> struct A {\n"
" A<T>() : x(0) { }\n"
" A<T>(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 void simple7() { // ticket #4531

View File

@ -1365,7 +1365,7 @@ private:
); );
// #3449 // #3449
ASSERT_EQUALS("template < typename T > struct A ;\n" ASSERT_EQUALS(";\n"
"struct B { template < typename T > struct C } ;\n" "struct B { template < typename T > struct C } ;\n"
"{ } ;", "{ } ;",
checkCode("template<typename T> struct A;\n" checkCode("template<typename T> struct A;\n"

View File

@ -67,7 +67,6 @@ private:
TEST_CASE(nullpointer24); // #5082 fp: chained assignment TEST_CASE(nullpointer24); // #5082 fp: chained assignment
TEST_CASE(nullpointer25); // #5061 TEST_CASE(nullpointer25); // #5061
TEST_CASE(nullpointer26); // #3589 TEST_CASE(nullpointer26); // #3589
TEST_CASE(nullpointer27); // #6568
TEST_CASE(nullpointer28); // #6491 TEST_CASE(nullpointer28); // #6491
TEST_CASE(nullpointer30); // #6392 TEST_CASE(nullpointer30); // #6392
TEST_CASE(nullpointer31); // #8482 TEST_CASE(nullpointer31); // #8482
@ -1322,20 +1321,6 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void nullpointer27() { // #6568
check("template<class Type>\n"
"class Foo {\n"
" Foo<Type>& operator = ( Type* );\n"
"};\n"
"template<class Type>\n"
"Foo<Type>& Foo<Type>::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 void nullpointer28() { // #6491
check("typedef struct { int value; } S;\n" check("typedef struct { int value; } S;\n"
"int f(const S *s) { \n" "int f(const S *s) { \n"

View File

@ -5005,11 +5005,6 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
check("template<int n> 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 // #8836
check("uint32_t value = 0xFUL;\n" check("uint32_t value = 0xFUL;\n"
"void f() {\n" "void f() {\n"
@ -7457,12 +7452,11 @@ private:
} }
void forwardAndUsed() { void forwardAndUsed() {
check("template<typename T>\n" check("void f(Foo && foo) {\n"
"void f(T && t) {\n" " g(std::forward<Foo>(foo));\n"
" g(std::forward<T>(t));\n" " Foo s = foo;\n"
" T s = t;\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() { void funcArgNamesDifferent() {

View File

@ -38,6 +38,7 @@ private:
void run() OVERRIDE { void run() OVERRIDE {
settings.addEnabled("portability"); settings.addEnabled("portability");
settings.checkUnusedTemplates = true;
TEST_CASE(template1); TEST_CASE(template1);
TEST_CASE(template2); TEST_CASE(template2);
@ -191,6 +192,8 @@ private:
TEST_CASE(templateTypeDeduction2); TEST_CASE(templateTypeDeduction2);
TEST_CASE(simplifyTemplateArgs); TEST_CASE(simplifyTemplateArgs);
TEST_CASE(removeTemplates);
} }
std::string tok(const char code[], bool debugwarnings = false, Settings::PlatformType type = Settings::Native) { std::string tok(const char code[], bool debugwarnings = false, Settings::PlatformType type = Settings::Native) {
@ -386,7 +389,8 @@ private:
// The expected result.. // The expected result..
const char expected[] = "class A<int> ; " const char expected[] = "class A<int> ; "
"void f ( ) { A<int> a ; } ; " "void f ( ) { A<int> a ; } "
"template < typename T > class B { void g ( ) { A < T > b ; b = A < T > :: h ( ) ; } } ; "
"class A<int> { } ;"; "class A<int> { } ;";
ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS(expected, tok(code));
@ -1385,6 +1389,7 @@ private:
"template <class T, unsigned S> C3<T, S>::C3(const C3<T, S> &v) { C1<T *> c1; }\n" "template <class T, unsigned S> C3<T, S>::C3(const C3<T, S> &v) { C1<T *> c1; }\n"
"C3<int,6> c3;"; "C3<int,6> c3;";
const char exp[] = "struct C1<int*> ; " const char exp[] = "struct C1<int*> ; "
"template < class T > void f ( ) { x = y ? ( C1 < int > :: allocate ( 1 ) ) : 0 ; } "
"class C3<int,6> ; " "class C3<int,6> ; "
"C3<int,6> c3 ; " "C3<int,6> c3 ; "
"class C3<int,6> { } ; " "class C3<int,6> { } ; "
@ -2392,6 +2397,7 @@ private:
"template <typename T> class Fred {};\n" "template <typename T> class Fred {};\n"
"ObjectCache<Fred> _cache;"; "ObjectCache<Fred> _cache;";
const char exp[] = "class ObjectCache<Fred> ; " const char exp[] = "class ObjectCache<Fred> ; "
"template < typename T > class Fred { } ; "
"ObjectCache<Fred> _cache ; " "ObjectCache<Fred> _cache ; "
"class ObjectCache<Fred> { } ;"; "class ObjectCache<Fred> { } ;";
ASSERT_EQUALS(exp, tok(code)); ASSERT_EQUALS(exp, tok(code));
@ -2499,7 +2505,8 @@ private:
"template < class T > struct Unconst < const T & > { } ; " "template < class T > struct Unconst < const T & > { } ; "
"template < class T > struct Unconst < T * const > { } ; " "template < class T > struct Unconst < T * const > { } ; "
"template < class T1 , class T2 > struct type_equal { enum Anonymous0 { value = 0 } ; } ; " "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)); ASSERT_EQUALS(exp1, tok(code1));
} }
@ -2739,7 +2746,7 @@ private:
const char code[] = "class Fred {\n" const char code[] = "class Fred {\n"
" template<class T> explicit Fred(T t) { }\n" " template<class T> 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 // #3532
const char code2[] = "class Fred {\n" const char code2[] = "class Fred {\n"
@ -3562,6 +3569,26 @@ private:
ASSERT_EQUALS("foo<false> = false ; foo<false> ;", tok("template<bool N> foo = N; foo < ( 1 - 1) ? true : false >;")); ASSERT_EQUALS("foo<false> = false ; foo<false> ;", tok("template<bool N> 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 <typename T> using foo = enable_if_t<is_same<T, int>() && sizeof(int) == 4, int>;\n"
"template <typename T, typename U = foo<T>> struct bar {};\n"
"template <typename T> using baz = bar<T>;\n"));
}
}; };
REGISTER_TEST(TestSimplifyTemplate) REGISTER_TEST(TestSimplifyTemplate)

View File

@ -43,7 +43,9 @@ private:
LOAD_LIB_2(settings_std.library, "std.cfg"); LOAD_LIB_2(settings_std.library, "std.cfg");
LOAD_LIB_2(settings_windows.library, "windows.cfg"); LOAD_LIB_2(settings_windows.library, "windows.cfg");
settings0.addEnabled("portability"); settings0.addEnabled("portability");
settings0.checkUnusedTemplates = true;
settings1.addEnabled("style"); settings1.addEnabled("style");
settings1.checkUnusedTemplates = true;
settings_windows.addEnabled("portability"); settings_windows.addEnabled("portability");
// Make sure the Tokenizer::simplifyTokenList works. // Make sure the Tokenizer::simplifyTokenList works.

View File

@ -41,6 +41,9 @@ private:
void run() OVERRIDE { void run() OVERRIDE {
settings0.addEnabled("style"); settings0.addEnabled("style");
settings2.addEnabled("style"); settings2.addEnabled("style");
settings0.checkUnusedTemplates = true;
settings1.checkUnusedTemplates = true;
settings2.checkUnusedTemplates = true;
TEST_CASE(simplifyTypedef1) TEST_CASE(simplifyTypedef1)
TEST_CASE(simplifyTypedef2) TEST_CASE(simplifyTypedef2)
@ -2527,7 +2530,8 @@ private:
"template <long, class> struct c; " "template <long, class> struct c; "
"template <int g> struct d { enum { e = c<g, b>::f }; };"; "template <int g> struct d { enum { e = c<g, b>::f }; };";
const char exp [] = "class a ; " 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)); ASSERT_EQUALS(exp, tok(code, false));
} }

View File

@ -41,6 +41,9 @@ private:
void run() OVERRIDE { void run() OVERRIDE {
settings0.addEnabled("style"); settings0.addEnabled("style");
settings2.addEnabled("style"); settings2.addEnabled("style");
settings0.checkUnusedTemplates = true;
settings1.checkUnusedTemplates = true;
settings2.checkUnusedTemplates = true;
TEST_CASE(simplifyUsing1); TEST_CASE(simplifyUsing1);
TEST_CASE(simplifyUsing2); TEST_CASE(simplifyUsing2);
@ -489,7 +492,8 @@ private:
"class c { " "class c { "
"int i ; i = 0 ; " "int i ; i = 0 ; "
"c ( ) { i -- ; } " "c ( ) { i -- ; } "
"} ;"; "} ; "
"template < class T > class s { } ;";
ASSERT_EQUALS(exp, tok(code, true, Settings::Win64)); ASSERT_EQUALS(exp, tok(code, true, Settings::Win64));
} }

View File

@ -106,6 +106,7 @@ private:
void run() OVERRIDE { void run() OVERRIDE {
LOAD_LIB_2(settings1.library, "std.cfg"); LOAD_LIB_2(settings1.library, "std.cfg");
settings1.checkUnusedTemplates = true;
settings2.platform(Settings::Unspecified); settings2.platform(Settings::Unspecified);
TEST_CASE(array); TEST_CASE(array);

View File

@ -47,6 +47,10 @@ private:
void run() OVERRIDE { void run() OVERRIDE {
LOAD_LIB_2(settings_windows.library, "windows.cfg"); LOAD_LIB_2(settings_windows.library, "windows.cfg");
settings0.checkUnusedTemplates = true;
settings1.checkUnusedTemplates = true;
settings2.checkUnusedTemplates = true;
TEST_CASE(tokenize1); TEST_CASE(tokenize1);
TEST_CASE(tokenize2); TEST_CASE(tokenize2);
TEST_CASE(tokenize3); TEST_CASE(tokenize3);

View File

@ -32,6 +32,7 @@ private:
public: public:
explicit givenACodeSampleToTokenize(const char sample[], bool createOnly = false, bool cpp = true) explicit givenACodeSampleToTokenize(const char sample[], bool createOnly = false, bool cpp = true)
: _tokenizer(&_settings, 0) { : _tokenizer(&_settings, 0) {
_settings.checkUnusedTemplates = true;
std::istringstream iss(sample); std::istringstream iss(sample);
if (createOnly) if (createOnly)
_tokenizer.list.createTokens(iss, cpp ? "test.cpp" : "test.c"); _tokenizer.list.createTokens(iss, cpp ? "test.cpp" : "test.c");

View File

@ -199,6 +199,7 @@ private:
errout.str(""); errout.str("");
Settings settings; Settings settings;
settings.checkUnusedTemplates = true;
settings.platform(Settings::Unix64); settings.platform(Settings::Unix64);
settings.standards.c = Standards::C89; settings.standards.c = Standards::C89;
settings.standards.cpp = Standards::CPP11; settings.standards.cpp = Standards::CPP11;
@ -218,6 +219,7 @@ private:
errout.str(""); errout.str("");
Settings settings; Settings settings;
settings.checkUnusedTemplates = true;
settings.platform(Settings::Unix64); settings.platform(Settings::Unix64);
settings.standards.c = Standards::C89; settings.standards.c = Standards::C89;
settings.standards.cpp = Standards::CPP11; settings.standards.cpp = Standards::CPP11;