Skip to content

Commit 80a486d

Browse files
Fix #11167 FP virtual call in destructor even though class is final / Delete 'final' from specializations (#4383)
* Add 'final' keyword * Delete 'final' from specializations * Fix #11167 FP virtual call in destructor even though class is final * Fix test
1 parent 1b4141c commit 80a486d

6 files changed

Lines changed: 47 additions & 6 deletions

File tree

lib/checkclass.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2588,11 +2588,13 @@ void CheckClass::checkVirtualFunctionCallInConstructor()
25882588
getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, callToken, callstack);
25892589
if (callstack.empty())
25902590
continue;
2591-
if (!(callstack.back()->function()->hasVirtualSpecifier() || callstack.back()->function()->hasOverrideSpecifier()))
2591+
const Function* const func = callstack.back()->function();
2592+
if (!(func->hasVirtualSpecifier() || func->hasOverrideSpecifier()))
25922593
continue;
2593-
if (callstack.back()->function()->isPure())
2594+
if (func->isPure())
25942595
pureVirtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str());
2595-
else if (!callstack.back()->function()->hasFinalSpecifier())
2596+
else if (!func->hasFinalSpecifier() &&
2597+
!(func->nestedIn && func->nestedIn->classDef && func->nestedIn->classDef->isFinalType()))
25962598
virtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str());
25972599
}
25982600
}

lib/token.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,13 @@ class CPPCHECKLIB Token {
680680
setFlag(fIsSimplifedScope, b);
681681
}
682682

683+
bool isFinalType() const {
684+
return getFlag(fIsFinalType);
685+
}
686+
void isFinalType(bool b) {
687+
setFlag(fIsFinalType, b);
688+
}
689+
683690
bool isBitfield() const {
684691
return mImpl->mBits > 0;
685692
}
@@ -1287,6 +1294,7 @@ class CPPCHECKLIB Token {
12871294
fIsIncompleteConstant = (1ULL << 36),
12881295
fIsRestrict = (1ULL << 37), // Is this a restrict pointer type
12891296
fIsSimplifiedTypedef = (1ULL << 38),
1297+
fIsFinalType = (1ULL << 39), // Is this a type with final specifier
12901298
};
12911299

12921300
Token::Type mTokType;

lib/tokenize.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8427,8 +8427,17 @@ void Tokenizer::simplifyKeyword()
84278427

84288428
// final:
84298429
// 1) struct name final { }; <- struct is final
8430-
if (Token::Match(tok->previous(), "struct|class|union %type% final [:{]")) {
8431-
tok->deleteNext();
8430+
if (Token::Match(tok->previous(), "struct|class|union %type%")) {
8431+
Token* finalTok = tok->next();
8432+
if (Token::simpleMatch(finalTok, "<")) { // specialization
8433+
finalTok = finalTok->findClosingBracket();
8434+
if (finalTok)
8435+
finalTok = finalTok->next();
8436+
}
8437+
if (Token::Match(finalTok, "final [:{]")) {
8438+
finalTok->deleteThis();
8439+
tok->previous()->isFinalType(true);
8440+
}
84328441
}
84338442

84348443
// noexcept -> noexcept(true)

test/testclass.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7439,6 +7439,14 @@ class TestClass : public TestFixture {
74397439
"}\n"
74407440
"S::~S() = default;\n");
74417441
ASSERT_EQUALS("", errout.str());
7442+
7443+
checkVirtualFunctionCall("struct Base: { virtual void wibble() = 0; virtual ~Base() {} };\n" // #11167
7444+
"struct D final : public Base {\n"
7445+
" void wibble() override;\n"
7446+
" D() {}\n"
7447+
" virtual ~D() { wibble(); }\n"
7448+
"};\n");
7449+
ASSERT_EQUALS("", errout.str());
74427450
}
74437451

74447452
void pureVirtualFunctionCall() {

test/testsimplifytemplate.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5531,7 +5531,7 @@ class TestSimplifyTemplate : public TestFixture {
55315531
"private:\n"
55325532
" std::basic_ostream<unsigned char> &outputStream_;\n"
55335533
"};";
5534-
const char expected[] = "struct OutputU16<unsignedchar> final { "
5534+
const char expected[] = "struct OutputU16<unsignedchar> { "
55355535
"explicit OutputU16<unsignedchar> ( std :: basic_ostream < unsigned char > & t ) : outputStream_ ( t ) { } "
55365536
"void operator() ( unsigned short ) const ; "
55375537
"private: "

test/testvarid.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ class TestVarID : public TestFixture {
156156
TEST_CASE(varid_templateParameter); // #7046 set varid for "X": std::array<int,X> Y;
157157
TEST_CASE(varid_templateParameterFunctionPointer); // #11050
158158
TEST_CASE(varid_templateUsing); // #5781 #7273
159+
TEST_CASE(varid_templateSpecializationFinal);
159160
TEST_CASE(varid_not_template_in_condition); // #7988
160161
TEST_CASE(varid_cppcast); // #6190
161162
TEST_CASE(varid_variadicFunc);
@@ -2379,6 +2380,19 @@ class TestVarID : public TestFixture {
23792380
tokenize(code));
23802381
}
23812382

2383+
void varid_templateSpecializationFinal() {
2384+
const char code[] = "template <typename T>\n"
2385+
"struct S;\n"
2386+
"template <>\n"
2387+
"struct S<void> final {};\n";
2388+
ASSERT_EQUALS("4: struct S<void> ;\n"
2389+
"1: template < typename T >\n"
2390+
"2: struct S ;\n"
2391+
"3:\n"
2392+
"4: struct S<void> { } ;\n",
2393+
tokenize(code));
2394+
}
2395+
23822396
void varid_not_template_in_condition() {
23832397
const char code1[] = "void f() { if (x<a||x>b); }";
23842398
ASSERT_EQUALS("1: void f ( ) { if ( x < a || x > b ) { ; } }\n", tokenize(code1));

0 commit comments

Comments
 (0)