Skip to content

Commit 2cd8ea8

Browse files
committed
Fixed #9860 (unused template not removed properly by default)
1 parent 28bc823 commit 2cd8ea8

13 files changed

Lines changed: 126 additions & 78 deletions

lib/token.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -293,18 +293,14 @@ void Token::deleteThis()
293293
takeData(mNext);
294294
mNext->link(nullptr); // mark as unlinked
295295
deleteNext();
296-
} else if (mPrevious && mPrevious->mPrevious) { // Copy previous to this and delete previous
296+
} else if (mPrevious) { // Copy previous to this and delete previous
297297
takeData(mPrevious);
298-
299-
Token* toDelete = mPrevious;
300-
mPrevious = mPrevious->mPrevious;
301-
mPrevious->mNext = this;
302-
303-
delete toDelete;
298+
mPrevious->link(nullptr);
299+
deletePrevious();
304300
} else {
305301
// We are the last token in the list, we can't delete
306302
// ourselves, so just make us empty
307-
str("");
303+
str(";");
308304
}
309305
}
310306

lib/tokenize.cpp

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4365,7 +4365,7 @@ bool Tokenizer::simplifyTokenList1(const char FileName[])
43654365

43664366
reportUnknownMacros();
43674367

4368-
simplifyHeaders();
4368+
simplifyHeadersAndUnusedTemplates();
43694369

43704370
// Remove __asm..
43714371
simplifyAsm();
@@ -5027,7 +5027,7 @@ void Tokenizer::dump(std::ostream &out) const
50275027
list.front()->printValueFlow(true, out);
50285028
}
50295029

5030-
void Tokenizer::simplifyHeaders()
5030+
void Tokenizer::simplifyHeadersAndUnusedTemplates()
50315031
{
50325032
if (mSettings->checkHeaders && mSettings->checkUnusedTemplates)
50335033
// Full analysis. All information in the headers are kept.
@@ -5053,7 +5053,13 @@ void Tokenizer::simplifyHeaders()
50535053
// functions and types to keep
50545054
std::set<std::string> keep;
50555055
for (const Token *tok = list.front(); tok; tok = tok->next()) {
5056-
if (!tok->isName())
5056+
if (isCPP() && Token::simpleMatch(tok, "template <")) {
5057+
const Token *closingBracket = tok->next()->findClosingBracket();
5058+
if (Token::Match(closingBracket, "> class|struct %name% {"))
5059+
tok = closingBracket->linkAt(3);
5060+
}
5061+
5062+
if (!tok->isName() || tok->isKeyword())
50575063
continue;
50585064

50595065
if (!checkHeaders && tok->fileIndex() != 0)
@@ -5088,61 +5094,57 @@ void Tokenizer::simplifyHeaders()
50885094
}
50895095
}
50905096

5091-
if (Token::Match(tok, "[;{}]")) {
5097+
if (!tok->previous() || Token::Match(tok->previous(), "[;{}]")) {
50925098
// Remove unused function declarations
50935099
if (isIncluded && removeUnusedIncludedFunctions) {
50945100
while (1) {
5095-
Token *start = tok->next();
5101+
Token *start = tok;
50965102
while (start && functionStart.find(start->str()) != functionStart.end())
50975103
start = start->next();
5098-
if (Token::Match(start, "%name% (") && Token::Match(start->linkAt(1), ") const| ;") && keep.find(start->str()) == keep.end())
5104+
if (Token::Match(start, "%name% (") && Token::Match(start->linkAt(1), ") const| ;") && keep.find(start->str()) == keep.end()) {
50995105
Token::eraseTokens(tok, start->linkAt(1)->tokAt(2));
5100-
else
5106+
tok->deleteThis();
5107+
} else
51015108
break;
51025109
}
51035110
}
51045111

51055112
if (isIncluded && removeUnusedIncludedClasses) {
5106-
if (Token::Match(tok, "[;{}] class|struct %name% [:{]") && keep.find(tok->strAt(2)) == keep.end()) {
5113+
if (Token::Match(tok, "class|struct %name% [:{]") && keep.find(tok->strAt(1)) == keep.end()) {
51075114
// Remove this class/struct
5108-
const Token *endToken = tok->tokAt(3);
5115+
const Token *endToken = tok->tokAt(2);
51095116
if (endToken->str() == ":") {
51105117
endToken = endToken->next();
51115118
while (Token::Match(endToken, "%name%|,"))
51125119
endToken = endToken->next();
51135120
}
5114-
if (endToken && endToken->str() == "{" && Token::simpleMatch(endToken->link(), "} ;"))
5121+
if (endToken && endToken->str() == "{" && Token::simpleMatch(endToken->link(), "} ;")) {
51155122
Token::eraseTokens(tok, endToken->link()->next());
5123+
tok->deleteThis();
5124+
}
51165125
}
51175126
}
51185127

51195128
if (removeUnusedTemplates || (isIncluded && removeUnusedIncludedTemplates)) {
5120-
if (Token::Match(tok->next(), "template < %name%")) {
5121-
const Token *tok2 = tok->tokAt(3);
5122-
while (Token::Match(tok2, "%name% %name% [,=>]") || Token::Match(tok2, "typename|class ... %name% [,>]")) {
5123-
if (Token::Match(tok2, "typename|class ..."))
5124-
tok2 = tok2->tokAt(3);
5125-
else
5126-
tok2 = tok2->tokAt(2);
5127-
if (Token::Match(tok2, "= %name% [,>]"))
5128-
tok2 = tok2->tokAt(2);
5129-
if (tok2->str() == ",")
5130-
tok2 = tok2->next();
5131-
}
5132-
if (Token::Match(tok2, "> class|struct %name% [;:{]") && keep.find(tok2->strAt(2)) == keep.end()) {
5133-
const Token *endToken = tok2->tokAt(3);
5129+
if (Token::Match(tok, "template < %name%")) {
5130+
const Token *closingBracket = tok->next()->findClosingBracket();
5131+
if (Token::Match(closingBracket, "> class|struct %name% [;:{]") && keep.find(closingBracket->strAt(2)) == keep.end()) {
5132+
const Token *endToken = closingBracket->tokAt(3);
51345133
if (endToken->str() == ":") {
51355134
endToken = endToken->next();
51365135
while (Token::Match(endToken, "%name%|,"))
51375136
endToken = endToken->next();
51385137
}
51395138
if (endToken && endToken->str() == "{")
51405139
endToken = endToken->link()->next();
5141-
if (endToken && endToken->str() == ";")
5140+
if (endToken && endToken->str() == ";") {
51425141
Token::eraseTokens(tok, endToken);
5143-
} else if (Token::Match(tok2, "> %type% %name% (") && Token::simpleMatch(tok2->linkAt(3), ") {") && keep.find(tok2->strAt(2)) == keep.end()) {
5144-
const Token *endToken = tok2->linkAt(3)->linkAt(1)->next();
5142+
tok->deleteThis();
5143+
}
5144+
} else if (Token::Match(closingBracket, "> %type% %name% (") && Token::simpleMatch(closingBracket->linkAt(3), ") {") && keep.find(closingBracket->strAt(2)) == keep.end()) {
5145+
const Token *endToken = closingBracket->linkAt(3)->linkAt(1)->next();
51455146
Token::eraseTokens(tok, endToken);
5147+
tok->deleteThis();
51465148
}
51475149
}
51485150
}

lib/tokenize.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class CPPCHECKLIB Tokenizer {
169169
* - All executable code.
170170
* - Unused types/variables/etc
171171
*/
172-
void simplifyHeaders();
172+
void simplifyHeadersAndUnusedTemplates();
173173

174174
/**
175175
* Deletes dead code between 'begin' and 'end'.

test/testconstructors.cpp

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ class TestConstructors : public TestFixture {
3131
private:
3232
Settings settings;
3333

34-
void check(const char code[], bool showAll = false) {
34+
void check(const char code[], bool inconclusive = false) {
3535
// Clear the error buffer..
3636
errout.str("");
3737

38-
settings.inconclusive = showAll;
38+
settings.inconclusive = inconclusive;
3939

4040
// Tokenize..
4141
Tokenizer tokenizer(&settings, this);
@@ -348,16 +348,6 @@ class TestConstructors : public TestFixture {
348348
" int x;\n"
349349
"};");
350350
ASSERT_EQUALS("", errout.str());
351-
352-
check("template <class T> struct A {\n"
353-
" A<T>() : x(0) { }\n"
354-
" A<T>(const T & t) : x(t.x) { }\n"
355-
"private:\n"
356-
" int x;\n"
357-
" int y;\n"
358-
"};");
359-
ASSERT_EQUALS("[test.cpp:2]: (warning) Member variable 'A::y' is not initialized in the constructor.\n"
360-
"[test.cpp:3]: (warning) Member variable 'A::y' is not initialized in the constructor.\n", errout.str());
361351
}
362352

363353
void simple7() { // ticket #4531

test/testgarbage.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1378,7 +1378,7 @@ class TestGarbage : public TestFixture {
13781378
);
13791379

13801380
// #3449
1381-
ASSERT_EQUALS("template < typename T > struct A ;\n"
1381+
ASSERT_EQUALS(";\n"
13821382
"struct B { template < typename T > struct C } ;\n"
13831383
"{ } ;",
13841384
checkCode("template<typename T> struct A;\n"

test/testother.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6101,10 +6101,14 @@ class TestOther : public TestFixture {
61016101
ASSERT_EQUALS("", errout.str());
61026102
}
61036103

6104-
check("template<int n> void foo(unsigned int x) {\n"
6105-
"if (x <= 0);\n"
6106-
"}\n");
6107-
ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str());
6104+
{
6105+
Settings keepTemplates;
6106+
keepTemplates.checkUnusedTemplates = true;
6107+
check("template<int n> void foo(unsigned int x) {\n"
6108+
"if (x <= 0);\n"
6109+
"}\n", &keepTemplates);
6110+
ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str());
6111+
}
61086112

61096113
// #8836
61106114
check("uint32_t value = 0xFUL;\n"
@@ -8743,11 +8747,14 @@ class TestOther : public TestFixture {
87438747
}
87448748

87458749
void forwardAndUsed() {
8750+
Settings keepTemplates;
8751+
keepTemplates.checkUnusedTemplates = true;
8752+
87468753
check("template<typename T>\n"
87478754
"void f(T && t) {\n"
87488755
" g(std::forward<T>(t));\n"
87498756
" T s = t;\n"
8750-
"}");
8757+
"}", &keepTemplates);
87518758
ASSERT_EQUALS("[test.cpp:4]: (warning) Access of forwarded variable 't'.\n", errout.str());
87528759
}
87538760

test/testsimplifytemplate.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ class TestSimplifyTemplate : public TestFixture {
4141
void run() OVERRIDE {
4242
settings.addEnabled("portability");
4343

44+
// If there are unused templates, keep those
45+
settings.checkUnusedTemplates = true;
46+
4447
TEST_CASE(template1);
4548
TEST_CASE(template2);
4649
TEST_CASE(template3);
@@ -458,7 +461,8 @@ class TestSimplifyTemplate : public TestFixture {
458461

459462
// The expected result..
460463
const char expected[] = "class A<int> ; "
461-
"void f ( ) { A<int> a ; } ; "
464+
"void f ( ) { A<int> a ; } "
465+
"template < typename T > class B { void g ( ) { A < T > b ; b = A < T > :: h ( ) ; } } ; "
462466
"class A<int> { } ;";
463467

464468
ASSERT_EQUALS(expected, tok(code));
@@ -1472,6 +1476,7 @@ class TestSimplifyTemplate : public TestFixture {
14721476
"template <class T, unsigned S> C3<T, S>::C3(const C3<T, S> &v) { C1<T *> c1; }\n"
14731477
"C3<int,6> c3;";
14741478
const char exp[] = "struct C1<int*> ; "
1479+
"template < class T > void f ( ) { x = y ? ( C1 < int > :: allocate ( 1 ) ) : 0 ; } "
14751480
"class C3<int,6> ; "
14761481
"C3<int,6> c3 ; "
14771482
"class C3<int,6> { } ; "
@@ -2549,8 +2554,8 @@ class TestSimplifyTemplate : public TestFixture {
25492554
"template <typename T> class Fred {};\n"
25502555
"ObjectCache<Fred> _cache;";
25512556
const char exp[] = "class ObjectCache<Fred> ; "
2552-
"ObjectCache<Fred> _cache ; "
2553-
"class ObjectCache<Fred> { } ;";
2557+
"template < typename T > class Fred { } ; "
2558+
"ObjectCache<Fred> _cache ; class ObjectCache<Fred> { } ;";
25542559
ASSERT_EQUALS(exp, tok(code));
25552560
}
25562561

@@ -4092,7 +4097,8 @@ class TestSimplifyTemplate : public TestFixture {
40924097
"template < class T > struct Unconst < const T & > { } ; "
40934098
"template < class T > struct Unconst < T * const > { } ; "
40944099
"template < class T1 , class T2 > struct type_equal { enum Anonymous0 { value = 0 } ; } ; "
4095-
"template < class T > struct type_equal < T , T > { enum Anonymous1 { value = 1 } ; } ;";
4100+
"template < class T > struct type_equal < T , T > { enum Anonymous1 { value = 1 } ; } ; "
4101+
"template < class T > struct template_is_const { enum Anonymous2 { value = ! type_equal < T , Unconst < T > :: type > :: value } ; } ;";
40964102
ASSERT_EQUALS(exp1, tok(code1));
40974103
}
40984104

@@ -4372,7 +4378,7 @@ class TestSimplifyTemplate : public TestFixture {
43724378
const char code[] = "class Fred {\n"
43734379
" template<class T> explicit Fred(T t) { }\n"
43744380
"}";
4375-
ASSERT_EQUALS("class Fred { }", tok(code));
4381+
ASSERT_EQUALS("class Fred { template < class T > explicit Fred ( T t ) { } }", tok(code));
43764382

43774383
// #3532
43784384
const char code2[] = "class Fred {\n"

test/testsimplifytokens.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ class TestSimplifyTokens : public TestFixture {
4646
settings1.addEnabled("style");
4747
settings_windows.addEnabled("portability");
4848

49+
// If there are unused templates, keep those
50+
settings0.checkUnusedTemplates = true;
51+
settings1.checkUnusedTemplates = true;
52+
settings_std.checkUnusedTemplates = true;
53+
settings_windows.checkUnusedTemplates = true;
54+
4955
// Make sure the Tokenizer::simplifyTokenList works.
5056
// The order of the simplifications is important. So this test
5157
// case shall make sure the simplifications are done in the
@@ -2883,7 +2889,7 @@ class TestSimplifyTokens : public TestFixture {
28832889
{
28842890
const char code[] = "namespace std { }";
28852891

2886-
ASSERT_EQUALS("", tok(code));
2892+
ASSERT_EQUALS(";", tok(code));
28872893
}
28882894

28892895
{

test/testsimplifytypedef.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ class TestSimplifyTypedef : public TestFixture {
4242
settings0.addEnabled("style");
4343
settings2.addEnabled("style");
4444

45+
// If there are unused templates, keep those
46+
settings0.checkUnusedTemplates = true;
47+
settings1.checkUnusedTemplates = true;
48+
settings2.checkUnusedTemplates = true;
49+
4550
TEST_CASE(simplifyTypedef1);
4651
TEST_CASE(simplifyTypedef2);
4752
TEST_CASE(simplifyTypedef3);
@@ -1797,7 +1802,7 @@ class TestSimplifyTypedef : public TestFixture {
17971802

17981803
void simplifyTypedef75() { // ticket #2426
17991804
const char code[] = "typedef _Packed struct S { long l; };";
1800-
ASSERT_EQUALS("", tok(code, true, Settings::Native, false));
1805+
ASSERT_EQUALS(";", tok(code, true, Settings::Native, false));
18011806
ASSERT_EQUALS("", errout.str());
18021807
}
18031808

@@ -2047,7 +2052,7 @@ class TestSimplifyTypedef : public TestFixture {
20472052
const char code[] = "typedef long Long;\n"
20482053
"namespace NS {\n"
20492054
"}";
2050-
ASSERT_EQUALS("", tok(code));
2055+
ASSERT_EQUALS(";", tok(code));
20512056
ASSERT_EQUALS("", errout.str());
20522057
}
20532058

@@ -2532,7 +2537,8 @@ class TestSimplifyTypedef : public TestFixture {
25322537
"template <long, class> struct c; "
25332538
"template <int g> struct d { enum { e = c<g, b>::f }; };";
25342539
const char exp [] = "class a ; "
2535-
"template < long , class > struct c ;";
2540+
"template < long , class > struct c ; "
2541+
"template < int g > struct d { enum Anonymous0 { e = c < g , int ( a :: * ) > :: f } ; } ;";
25362542
ASSERT_EQUALS(exp, tok(code, false));
25372543
}
25382544

test/testsimplifyusing.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ class TestSimplifyUsing : public TestFixture {
4242
settings0.addEnabled("style");
4343
settings2.addEnabled("style");
4444

45+
// If there are unused templates, keep those
46+
settings0.checkUnusedTemplates = true;
47+
settings1.checkUnusedTemplates = true;
48+
settings2.checkUnusedTemplates = true;
49+
4550
TEST_CASE(simplifyUsing1);
4651
TEST_CASE(simplifyUsing2);
4752
TEST_CASE(simplifyUsing3);
@@ -492,7 +497,8 @@ class TestSimplifyUsing : public TestFixture {
492497
"class c { "
493498
"int i ; i = 0 ; "
494499
"c ( ) { i -- ; } "
495-
"} ;";
500+
"} ; "
501+
"template < class T > class s { } ;";
496502

497503
ASSERT_EQUALS(exp, tok(code, true, Settings::Win64));
498504
}

0 commit comments

Comments
 (0)