Fixed #9860 (unused template not removed properly by default)

This commit is contained in:
Daniel Marjamäki 2020-11-22 16:43:36 +01:00
parent 28bc823264
commit 2cd8ea83a7
13 changed files with 126 additions and 78 deletions

View File

@ -293,18 +293,14 @@ void Token::deleteThis()
takeData(mNext);
mNext->link(nullptr); // mark as unlinked
deleteNext();
} else if (mPrevious && mPrevious->mPrevious) { // Copy previous to this and delete previous
} else if (mPrevious) { // Copy previous to this and delete previous
takeData(mPrevious);
Token* toDelete = mPrevious;
mPrevious = mPrevious->mPrevious;
mPrevious->mNext = this;
delete toDelete;
mPrevious->link(nullptr);
deletePrevious();
} else {
// We are the last token in the list, we can't delete
// ourselves, so just make us empty
str("");
str(";");
}
}

View File

@ -4365,7 +4365,7 @@ bool Tokenizer::simplifyTokenList1(const char FileName[])
reportUnknownMacros();
simplifyHeaders();
simplifyHeadersAndUnusedTemplates();
// Remove __asm..
simplifyAsm();
@ -5027,7 +5027,7 @@ void Tokenizer::dump(std::ostream &out) const
list.front()->printValueFlow(true, out);
}
void Tokenizer::simplifyHeaders()
void Tokenizer::simplifyHeadersAndUnusedTemplates()
{
if (mSettings->checkHeaders && mSettings->checkUnusedTemplates)
// Full analysis. All information in the headers are kept.
@ -5053,7 +5053,13 @@ void Tokenizer::simplifyHeaders()
// functions and types to keep
std::set<std::string> keep;
for (const Token *tok = list.front(); tok; tok = tok->next()) {
if (!tok->isName())
if (isCPP() && Token::simpleMatch(tok, "template <")) {
const Token *closingBracket = tok->next()->findClosingBracket();
if (Token::Match(closingBracket, "> class|struct %name% {"))
tok = closingBracket->linkAt(3);
}
if (!tok->isName() || tok->isKeyword())
continue;
if (!checkHeaders && tok->fileIndex() != 0)
@ -5088,49 +5094,42 @@ void Tokenizer::simplifyHeaders()
}
}
if (Token::Match(tok, "[;{}]")) {
if (!tok->previous() || Token::Match(tok->previous(), "[;{}]")) {
// Remove unused function declarations
if (isIncluded && removeUnusedIncludedFunctions) {
while (1) {
Token *start = tok->next();
Token *start = tok;
while (start && functionStart.find(start->str()) != functionStart.end())
start = start->next();
if (Token::Match(start, "%name% (") && Token::Match(start->linkAt(1), ") const| ;") && keep.find(start->str()) == keep.end())
if (Token::Match(start, "%name% (") && Token::Match(start->linkAt(1), ") const| ;") && keep.find(start->str()) == keep.end()) {
Token::eraseTokens(tok, start->linkAt(1)->tokAt(2));
else
tok->deleteThis();
} else
break;
}
}
if (isIncluded && removeUnusedIncludedClasses) {
if (Token::Match(tok, "[;{}] class|struct %name% [:{]") && keep.find(tok->strAt(2)) == keep.end()) {
if (Token::Match(tok, "class|struct %name% [:{]") && keep.find(tok->strAt(1)) == keep.end()) {
// Remove this class/struct
const Token *endToken = tok->tokAt(3);
const Token *endToken = tok->tokAt(2);
if (endToken->str() == ":") {
endToken = endToken->next();
while (Token::Match(endToken, "%name%|,"))
endToken = endToken->next();
}
if (endToken && endToken->str() == "{" && Token::simpleMatch(endToken->link(), "} ;"))
if (endToken && endToken->str() == "{" && Token::simpleMatch(endToken->link(), "} ;")) {
Token::eraseTokens(tok, endToken->link()->next());
tok->deleteThis();
}
}
}
if (removeUnusedTemplates || (isIncluded && removeUnusedIncludedTemplates)) {
if (Token::Match(tok->next(), "template < %name%")) {
const Token *tok2 = tok->tokAt(3);
while (Token::Match(tok2, "%name% %name% [,=>]") || Token::Match(tok2, "typename|class ... %name% [,>]")) {
if (Token::Match(tok2, "typename|class ..."))
tok2 = tok2->tokAt(3);
else
tok2 = tok2->tokAt(2);
if (Token::Match(tok2, "= %name% [,>]"))
tok2 = tok2->tokAt(2);
if (tok2->str() == ",")
tok2 = tok2->next();
}
if (Token::Match(tok2, "> class|struct %name% [;:{]") && keep.find(tok2->strAt(2)) == keep.end()) {
const Token *endToken = tok2->tokAt(3);
if (Token::Match(tok, "template < %name%")) {
const Token *closingBracket = tok->next()->findClosingBracket();
if (Token::Match(closingBracket, "> class|struct %name% [;:{]") && keep.find(closingBracket->strAt(2)) == keep.end()) {
const Token *endToken = closingBracket->tokAt(3);
if (endToken->str() == ":") {
endToken = endToken->next();
while (Token::Match(endToken, "%name%|,"))
@ -5138,11 +5137,14 @@ void Tokenizer::simplifyHeaders()
}
if (endToken && endToken->str() == "{")
endToken = endToken->link()->next();
if (endToken && endToken->str() == ";")
if (endToken && endToken->str() == ";") {
Token::eraseTokens(tok, endToken);
} else if (Token::Match(tok2, "> %type% %name% (") && Token::simpleMatch(tok2->linkAt(3), ") {") && keep.find(tok2->strAt(2)) == keep.end()) {
const Token *endToken = tok2->linkAt(3)->linkAt(1)->next();
tok->deleteThis();
}
} else if (Token::Match(closingBracket, "> %type% %name% (") && Token::simpleMatch(closingBracket->linkAt(3), ") {") && keep.find(closingBracket->strAt(2)) == keep.end()) {
const Token *endToken = closingBracket->linkAt(3)->linkAt(1)->next();
Token::eraseTokens(tok, endToken);
tok->deleteThis();
}
}
}

View File

@ -169,7 +169,7 @@ public:
* - All executable code.
* - Unused types/variables/etc
*/
void simplifyHeaders();
void simplifyHeadersAndUnusedTemplates();
/**
* Deletes dead code between 'begin' and 'end'.

View File

@ -31,11 +31,11 @@ public:
private:
Settings settings;
void check(const char code[], bool showAll = false) {
void check(const char code[], bool inconclusive = false) {
// Clear the error buffer..
errout.str("");
settings.inconclusive = showAll;
settings.inconclusive = inconclusive;
// Tokenize..
Tokenizer tokenizer(&settings, this);
@ -348,16 +348,6 @@ private:
" int x;\n"
"};");
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

View File

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

View File

@ -6101,10 +6101,14 @@ private:
ASSERT_EQUALS("", errout.str());
}
{
Settings keepTemplates;
keepTemplates.checkUnusedTemplates = true;
check("template<int n> void foo(unsigned int x) {\n"
"if (x <= 0);\n"
"}\n");
"}\n", &keepTemplates);
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"
@ -8743,11 +8747,14 @@ private:
}
void forwardAndUsed() {
Settings keepTemplates;
keepTemplates.checkUnusedTemplates = true;
check("template<typename T>\n"
"void f(T && t) {\n"
" g(std::forward<T>(t));\n"
" T s = t;\n"
"}");
"}", &keepTemplates);
ASSERT_EQUALS("[test.cpp:4]: (warning) Access of forwarded variable 't'.\n", errout.str());
}

View File

@ -41,6 +41,9 @@ private:
void run() OVERRIDE {
settings.addEnabled("portability");
// If there are unused templates, keep those
settings.checkUnusedTemplates = true;
TEST_CASE(template1);
TEST_CASE(template2);
TEST_CASE(template3);
@ -458,7 +461,8 @@ private:
// The expected result..
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> { } ;";
ASSERT_EQUALS(expected, tok(code));
@ -1472,6 +1476,7 @@ private:
"template <class T, unsigned S> C3<T, S>::C3(const C3<T, S> &v) { C1<T *> c1; }\n"
"C3<int,6> c3;";
const char exp[] = "struct C1<int*> ; "
"template < class T > void f ( ) { x = y ? ( C1 < int > :: allocate ( 1 ) ) : 0 ; } "
"class C3<int,6> ; "
"C3<int,6> c3 ; "
"class C3<int,6> { } ; "
@ -2549,8 +2554,8 @@ private:
"template <typename T> class Fred {};\n"
"ObjectCache<Fred> _cache;";
const char exp[] = "class ObjectCache<Fred> ; "
"ObjectCache<Fred> _cache ; "
"class ObjectCache<Fred> { } ;";
"template < typename T > class Fred { } ; "
"ObjectCache<Fred> _cache ; class ObjectCache<Fred> { } ;";
ASSERT_EQUALS(exp, tok(code));
}
@ -4092,7 +4097,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));
}
@ -4372,7 +4378,7 @@ private:
const char code[] = "class Fred {\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
const char code2[] = "class Fred {\n"

View File

@ -46,6 +46,12 @@ private:
settings1.addEnabled("style");
settings_windows.addEnabled("portability");
// If there are unused templates, keep those
settings0.checkUnusedTemplates = true;
settings1.checkUnusedTemplates = true;
settings_std.checkUnusedTemplates = true;
settings_windows.checkUnusedTemplates = true;
// Make sure the Tokenizer::simplifyTokenList works.
// The order of the simplifications is important. So this test
// case shall make sure the simplifications are done in the
@ -2883,7 +2889,7 @@ private:
{
const char code[] = "namespace std { }";
ASSERT_EQUALS("", tok(code));
ASSERT_EQUALS(";", tok(code));
}
{

View File

@ -42,6 +42,11 @@ private:
settings0.addEnabled("style");
settings2.addEnabled("style");
// If there are unused templates, keep those
settings0.checkUnusedTemplates = true;
settings1.checkUnusedTemplates = true;
settings2.checkUnusedTemplates = true;
TEST_CASE(simplifyTypedef1);
TEST_CASE(simplifyTypedef2);
TEST_CASE(simplifyTypedef3);
@ -1797,7 +1802,7 @@ private:
void simplifyTypedef75() { // ticket #2426
const char code[] = "typedef _Packed struct S { long l; };";
ASSERT_EQUALS("", tok(code, true, Settings::Native, false));
ASSERT_EQUALS(";", tok(code, true, Settings::Native, false));
ASSERT_EQUALS("", errout.str());
}
@ -2047,7 +2052,7 @@ private:
const char code[] = "typedef long Long;\n"
"namespace NS {\n"
"}";
ASSERT_EQUALS("", tok(code));
ASSERT_EQUALS(";", tok(code));
ASSERT_EQUALS("", errout.str());
}
@ -2532,7 +2537,8 @@ private:
"template <long, class> struct c; "
"template <int g> struct d { enum { e = c<g, b>::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));
}

View File

@ -42,6 +42,11 @@ private:
settings0.addEnabled("style");
settings2.addEnabled("style");
// If there are unused templates, keep those
settings0.checkUnusedTemplates = true;
settings1.checkUnusedTemplates = true;
settings2.checkUnusedTemplates = true;
TEST_CASE(simplifyUsing1);
TEST_CASE(simplifyUsing2);
TEST_CASE(simplifyUsing3);
@ -492,7 +497,8 @@ private:
"class c { "
"int i ; i = 0 ; "
"c ( ) { i -- ; } "
"} ;";
"} ; "
"template < class T > class s { } ;";
ASSERT_EQUALS(exp, tok(code, true, Settings::Win64));
}

View File

@ -115,6 +115,10 @@ private:
LOAD_LIB_2(settings1.library, "std.cfg");
settings2.platform(Settings::Unspecified);
// If there are unused templates, keep those
settings1.checkUnusedTemplates = true;
settings2.checkUnusedTemplates = true;
TEST_CASE(array);
TEST_CASE(stlarray1);
TEST_CASE(stlarray2);

View File

@ -47,6 +47,12 @@ private:
void run() OVERRIDE {
LOAD_LIB_2(settings_windows.library, "windows.cfg");
// If there are unused templates, keep those
settings0.checkUnusedTemplates = true;
settings1.checkUnusedTemplates = true;
settings2.checkUnusedTemplates = true;
settings_windows.checkUnusedTemplates = true;
TEST_CASE(tokenize1);
TEST_CASE(tokenize2);
TEST_CASE(tokenize4);
@ -95,7 +101,8 @@ private:
TEST_CASE(longtok);
TEST_CASE(removeUnusedTemplates);
TEST_CASE(simplifyHeadersAndUnusedTemplates1);
TEST_CASE(simplifyHeadersAndUnusedTemplates2);
TEST_CASE(simplifyCasts1);
TEST_CASE(simplifyCasts2);
@ -1028,7 +1035,7 @@ private:
}
void removeUnusedTemplates() {
void simplifyHeadersAndUnusedTemplates1() {
Settings s;
s.checkUnusedTemplates = false;
ASSERT_EQUALS(";",
@ -1044,6 +1051,22 @@ private:
"}", s));
}
void simplifyHeadersAndUnusedTemplates2() {
const char code[] = "; template< typename T, u_int uBAR = 0 >\n"
"class Foo {\n"
"public:\n"
" void FooBar() {\n"
" new ( (uBAR ? uBAR : sizeof(T))) T;\n"
" }\n"
"};";
Settings s;
s.checkUnusedTemplates = false;
ASSERT_EQUALS(";", tokenizeAndStringify(code, s));
s.checkUnusedTemplates = true;
ASSERT_THROW(tokenizeAndStringify(code, s), InternalError);
}
// Dont remove "(int *)"..
void simplifyCasts1() {
@ -7467,11 +7490,11 @@ private:
}
void simplifyEmptyNamespaces() {
ASSERT_EQUALS("", tokenizeAndStringify("namespace { }"));
ASSERT_EQUALS("", tokenizeAndStringify("namespace foo { }"));
ASSERT_EQUALS("", tokenizeAndStringify("namespace foo { namespace { } }"));
ASSERT_EQUALS("", tokenizeAndStringify("namespace { namespace { } }")); // Ticket #9512
ASSERT_EQUALS("", tokenizeAndStringify("namespace foo { namespace bar { } }"));
ASSERT_EQUALS(";", tokenizeAndStringify("namespace { }"));
ASSERT_EQUALS(";", tokenizeAndStringify("namespace foo { }"));
ASSERT_EQUALS(";", tokenizeAndStringify("namespace foo { namespace { } }"));
ASSERT_EQUALS(";", tokenizeAndStringify("namespace { namespace { } }")); // Ticket #9512
ASSERT_EQUALS(";", tokenizeAndStringify("namespace foo { namespace bar { } }"));
}
void prepareTernaryOpForAST() {
@ -8625,12 +8648,12 @@ private:
}
}
std::string checkHeaders(const char code[], bool f) {
std::string checkHeaders(const char code[], bool checkHeadersFlag) {
// Clear the error buffer..
errout.str("");
Settings settings;
settings.checkHeaders = f;
settings.checkHeaders = checkHeadersFlag;
// Raw tokens..
std::vector<std::string> files(1, "test.cpp");
@ -8670,12 +8693,11 @@ private:
"5: } ; void A :: g<int> ( int x ) { a = 2 ; }\n",
checkHeaders(code, true));
ASSERT_EQUALS("\n\n##file 1\n"
"1: struct A {\n"
"2: int a ; a = 1 ;\n"
"3: void f ( ) ;\n"
ASSERT_EQUALS("\n\n##file 1\n\n"
"1:\n"
"|\n"
"4:\n"
"5: } ;\n",
"5: ;\n",
checkHeaders(code, false));
}
};

View File

@ -221,6 +221,7 @@ private:
settings.platform(Settings::Unix64);
settings.standards.c = Standards::C89;
settings.standards.cpp = Standards::CPP11;
settings.checkUnusedTemplates = true;
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
@ -239,6 +240,7 @@ private:
settings.platform(Settings::Unix64);
settings.standards.c = Standards::C89;
settings.standards.cpp = Standards::CPP11;
settings.checkUnusedTemplates = true;
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
@ -257,6 +259,7 @@ private:
settings.platform(Settings::Unix64);
settings.standards.c = Standards::C89;
settings.standards.cpp = Standards::CPP11;
settings.checkUnusedTemplates = true;
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);