diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index b20fd14a4..a62cd183a 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2588,11 +2588,13 @@ void CheckClass::checkVirtualFunctionCallInConstructor() getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, callToken, callstack); if (callstack.empty()) continue; - if (!(callstack.back()->function()->hasVirtualSpecifier() || callstack.back()->function()->hasOverrideSpecifier())) + const Function* const func = callstack.back()->function(); + if (!(func->hasVirtualSpecifier() || func->hasOverrideSpecifier())) continue; - if (callstack.back()->function()->isPure()) + if (func->isPure()) pureVirtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str()); - else if (!callstack.back()->function()->hasFinalSpecifier()) + else if (!func->hasFinalSpecifier() && + !(func->nestedIn && func->nestedIn->classDef && func->nestedIn->classDef->isFinalType())) virtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str()); } } diff --git a/lib/token.h b/lib/token.h index f44857137..ef2b4b2b1 100644 --- a/lib/token.h +++ b/lib/token.h @@ -680,6 +680,13 @@ public: setFlag(fIsSimplifedScope, b); } + bool isFinalType() const { + return getFlag(fIsFinalType); + } + void isFinalType(bool b) { + setFlag(fIsFinalType, b); + } + bool isBitfield() const { return mImpl->mBits > 0; } @@ -1287,6 +1294,7 @@ private: fIsIncompleteConstant = (1ULL << 36), fIsRestrict = (1ULL << 37), // Is this a restrict pointer type fIsSimplifiedTypedef = (1ULL << 38), + fIsFinalType = (1ULL << 39), // Is this a type with final specifier }; Token::Type mTokType; diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 634a7c597..28f0517e3 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -8427,8 +8427,17 @@ void Tokenizer::simplifyKeyword() // final: // 1) struct name final { }; <- struct is final - if (Token::Match(tok->previous(), "struct|class|union %type% final [:{]")) { - tok->deleteNext(); + if (Token::Match(tok->previous(), "struct|class|union %type%")) { + Token* finalTok = tok->next(); + if (Token::simpleMatch(finalTok, "<")) { // specialization + finalTok = finalTok->findClosingBracket(); + if (finalTok) + finalTok = finalTok->next(); + } + if (Token::Match(finalTok, "final [:{]")) { + finalTok->deleteThis(); + tok->previous()->isFinalType(true); + } } // noexcept -> noexcept(true) diff --git a/test/testclass.cpp b/test/testclass.cpp index ce7bf9aee..b5474611c 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -7439,6 +7439,14 @@ private: "}\n" "S::~S() = default;\n"); ASSERT_EQUALS("", errout.str()); + + checkVirtualFunctionCall("struct Base: { virtual void wibble() = 0; virtual ~Base() {} };\n" // #11167 + "struct D final : public Base {\n" + " void wibble() override;\n" + " D() {}\n" + " virtual ~D() { wibble(); }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); } void pureVirtualFunctionCall() { diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 87fa8938c..53a366d1a 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -5531,7 +5531,7 @@ private: "private:\n" " std::basic_ostream &outputStream_;\n" "};"; - const char expected[] = "struct OutputU16 final { " + const char expected[] = "struct OutputU16 { " "explicit OutputU16 ( std :: basic_ostream < unsigned char > & t ) : outputStream_ ( t ) { } " "void operator() ( unsigned short ) const ; " "private: " diff --git a/test/testvarid.cpp b/test/testvarid.cpp index fbda0ef32..992d8ec3e 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -156,6 +156,7 @@ private: TEST_CASE(varid_templateParameter); // #7046 set varid for "X": std::array Y; TEST_CASE(varid_templateParameterFunctionPointer); // #11050 TEST_CASE(varid_templateUsing); // #5781 #7273 + TEST_CASE(varid_templateSpecializationFinal); TEST_CASE(varid_not_template_in_condition); // #7988 TEST_CASE(varid_cppcast); // #6190 TEST_CASE(varid_variadicFunc); @@ -2379,6 +2380,19 @@ private: tokenize(code)); } + void varid_templateSpecializationFinal() { + const char code[] = "template \n" + "struct S;\n" + "template <>\n" + "struct S final {};\n"; + ASSERT_EQUALS("4: struct S ;\n" + "1: template < typename T >\n" + "2: struct S ;\n" + "3:\n" + "4: struct S { } ;\n", + tokenize(code)); + } + void varid_not_template_in_condition() { const char code1[] = "void f() { if (xb); }"; ASSERT_EQUALS("1: void f ( ) { if ( x < a || x > b ) { ; } }\n", tokenize(code1));