From dad6835dc22db7b4435f644dcae9055ccf2cdf66 Mon Sep 17 00:00:00 2001 From: chrchr Date: Fri, 19 Aug 2022 12:29:50 +0200 Subject: [PATCH 1/4] Add 'final' keyword --- lib/tokenlist.cpp | 1 + test/testvarid.cpp | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index c258cbec2d5..c94e0d9dd53 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -138,6 +138,7 @@ void TokenList::determineCppC() mKeywords.insert("explicit"); mKeywords.insert("export"); //mKeywords.insert("false"); // literal + mKeywords.insert("final"); mKeywords.insert("friend"); mKeywords.insert("mutable"); mKeywords.insert("namespace"); diff --git a/test/testvarid.cpp b/test/testvarid.cpp index 9bc608b2ba8..d84888466cf 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -155,6 +155,7 @@ class TestVarID : public TestFixture { 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); @@ -2378,6 +2379,19 @@ class TestVarID : public TestFixture { tokenize(code)); } + void varid_templateSpecializationFinal() { + const char code[] = "template \n" + "struct S;\n" + "template <>\n" + "struct S final {};\n"; + ASSERT_EQUALS("4: struct S final ;\n" + "1: template < typename T >\n" + "2: struct S ;\n" + "3:\n" + "4: struct S final { } ;\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)); From 176bafc03410f2089bb33506d84aa1bbad31c234 Mon Sep 17 00:00:00 2001 From: chrchr Date: Fri, 19 Aug 2022 14:35:18 +0200 Subject: [PATCH 2/4] Delete 'final' from specializations --- lib/tokenize.cpp | 12 ++++++++++-- lib/tokenlist.cpp | 1 - test/testvarid.cpp | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 65ad8148138..db20f8a3ee9 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -8426,8 +8426,16 @@ 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(); + // TODO: preserve final type info } // noexcept -> noexcept(true) diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index c94e0d9dd53..c258cbec2d5 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -138,7 +138,6 @@ void TokenList::determineCppC() mKeywords.insert("explicit"); mKeywords.insert("export"); //mKeywords.insert("false"); // literal - mKeywords.insert("final"); mKeywords.insert("friend"); mKeywords.insert("mutable"); mKeywords.insert("namespace"); diff --git a/test/testvarid.cpp b/test/testvarid.cpp index d84888466cf..36e6e436b73 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -2384,11 +2384,11 @@ class TestVarID : public TestFixture { "struct S;\n" "template <>\n" "struct S final {};\n"; - ASSERT_EQUALS("4: struct S final ;\n" + ASSERT_EQUALS("4: struct S ;\n" "1: template < typename T >\n" "2: struct S ;\n" "3:\n" - "4: struct S final { } ;\n", + "4: struct S { } ;\n", tokenize(code)); } From ceb7b21e0a6e05ef567be22240bee7692d901daf Mon Sep 17 00:00:00 2001 From: chrchr Date: Fri, 19 Aug 2022 15:00:21 +0200 Subject: [PATCH 3/4] Fix #11167 FP virtual call in destructor even though class is final --- lib/checkclass.cpp | 8 +++++--- lib/token.h | 8 ++++++++ lib/tokenize.cpp | 5 +++-- test/testclass.cpp | 8 ++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index b20fd14a446..a62cd183a51 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 4498fd1e020..2ddaa215507 100644 --- a/lib/token.h +++ b/lib/token.h @@ -680,6 +680,13 @@ class CPPCHECKLIB Token { 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 @@ class CPPCHECKLIB Token { 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 db20f8a3ee9..9f35c38adba 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -8433,9 +8433,10 @@ void Tokenizer::simplifyKeyword() if (finalTok) finalTok = finalTok->next(); } - if (Token::Match(finalTok, "final [:{]")) + if (Token::Match(finalTok, "final [:{]")) { finalTok->deleteThis(); - // TODO: preserve final type info + tok->previous()->isFinalType(true); + } } // noexcept -> noexcept(true) diff --git a/test/testclass.cpp b/test/testclass.cpp index 6891f1b2162..474a37ffbaf 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -7438,6 +7438,14 @@ class TestClass : public TestFixture { "}\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() { From 530234da7c8d1b48811ef7ac39bfe9a5f33edaef Mon Sep 17 00:00:00 2001 From: chrchr Date: Fri, 19 Aug 2022 15:04:29 +0200 Subject: [PATCH 4/4] Fix test --- test/testsimplifytemplate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index be25ac05367..844d58e26ab 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -5530,7 +5530,7 @@ class TestSimplifyTemplate : public TestFixture { "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: "