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
This commit is contained in:
parent
1b4141cbe5
commit
80a486dda0
|
@ -2588,11 +2588,13 @@ void CheckClass::checkVirtualFunctionCallInConstructor()
|
||||||
getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, callToken, callstack);
|
getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, callToken, callstack);
|
||||||
if (callstack.empty())
|
if (callstack.empty())
|
||||||
continue;
|
continue;
|
||||||
if (!(callstack.back()->function()->hasVirtualSpecifier() || callstack.back()->function()->hasOverrideSpecifier()))
|
const Function* const func = callstack.back()->function();
|
||||||
|
if (!(func->hasVirtualSpecifier() || func->hasOverrideSpecifier()))
|
||||||
continue;
|
continue;
|
||||||
if (callstack.back()->function()->isPure())
|
if (func->isPure())
|
||||||
pureVirtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str());
|
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());
|
virtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -680,6 +680,13 @@ public:
|
||||||
setFlag(fIsSimplifedScope, b);
|
setFlag(fIsSimplifedScope, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isFinalType() const {
|
||||||
|
return getFlag(fIsFinalType);
|
||||||
|
}
|
||||||
|
void isFinalType(bool b) {
|
||||||
|
setFlag(fIsFinalType, b);
|
||||||
|
}
|
||||||
|
|
||||||
bool isBitfield() const {
|
bool isBitfield() const {
|
||||||
return mImpl->mBits > 0;
|
return mImpl->mBits > 0;
|
||||||
}
|
}
|
||||||
|
@ -1287,6 +1294,7 @@ private:
|
||||||
fIsIncompleteConstant = (1ULL << 36),
|
fIsIncompleteConstant = (1ULL << 36),
|
||||||
fIsRestrict = (1ULL << 37), // Is this a restrict pointer type
|
fIsRestrict = (1ULL << 37), // Is this a restrict pointer type
|
||||||
fIsSimplifiedTypedef = (1ULL << 38),
|
fIsSimplifiedTypedef = (1ULL << 38),
|
||||||
|
fIsFinalType = (1ULL << 39), // Is this a type with final specifier
|
||||||
};
|
};
|
||||||
|
|
||||||
Token::Type mTokType;
|
Token::Type mTokType;
|
||||||
|
|
|
@ -8427,8 +8427,17 @@ void Tokenizer::simplifyKeyword()
|
||||||
|
|
||||||
// final:
|
// final:
|
||||||
// 1) struct name final { }; <- struct is final
|
// 1) struct name final { }; <- struct is final
|
||||||
if (Token::Match(tok->previous(), "struct|class|union %type% final [:{]")) {
|
if (Token::Match(tok->previous(), "struct|class|union %type%")) {
|
||||||
tok->deleteNext();
|
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)
|
// noexcept -> noexcept(true)
|
||||||
|
|
|
@ -7439,6 +7439,14 @@ private:
|
||||||
"}\n"
|
"}\n"
|
||||||
"S::~S() = default;\n");
|
"S::~S() = default;\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
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() {
|
void pureVirtualFunctionCall() {
|
||||||
|
|
|
@ -5531,7 +5531,7 @@ private:
|
||||||
"private:\n"
|
"private:\n"
|
||||||
" std::basic_ostream<unsigned char> &outputStream_;\n"
|
" std::basic_ostream<unsigned char> &outputStream_;\n"
|
||||||
"};";
|
"};";
|
||||||
const char expected[] = "struct OutputU16<unsignedchar> final { "
|
const char expected[] = "struct OutputU16<unsignedchar> { "
|
||||||
"explicit OutputU16<unsignedchar> ( std :: basic_ostream < unsigned char > & t ) : outputStream_ ( t ) { } "
|
"explicit OutputU16<unsignedchar> ( std :: basic_ostream < unsigned char > & t ) : outputStream_ ( t ) { } "
|
||||||
"void operator() ( unsigned short ) const ; "
|
"void operator() ( unsigned short ) const ; "
|
||||||
"private: "
|
"private: "
|
||||||
|
|
|
@ -156,6 +156,7 @@ private:
|
||||||
TEST_CASE(varid_templateParameter); // #7046 set varid for "X": std::array<int,X> Y;
|
TEST_CASE(varid_templateParameter); // #7046 set varid for "X": std::array<int,X> Y;
|
||||||
TEST_CASE(varid_templateParameterFunctionPointer); // #11050
|
TEST_CASE(varid_templateParameterFunctionPointer); // #11050
|
||||||
TEST_CASE(varid_templateUsing); // #5781 #7273
|
TEST_CASE(varid_templateUsing); // #5781 #7273
|
||||||
|
TEST_CASE(varid_templateSpecializationFinal);
|
||||||
TEST_CASE(varid_not_template_in_condition); // #7988
|
TEST_CASE(varid_not_template_in_condition); // #7988
|
||||||
TEST_CASE(varid_cppcast); // #6190
|
TEST_CASE(varid_cppcast); // #6190
|
||||||
TEST_CASE(varid_variadicFunc);
|
TEST_CASE(varid_variadicFunc);
|
||||||
|
@ -2379,6 +2380,19 @@ private:
|
||||||
tokenize(code));
|
tokenize(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void varid_templateSpecializationFinal() {
|
||||||
|
const char code[] = "template <typename T>\n"
|
||||||
|
"struct S;\n"
|
||||||
|
"template <>\n"
|
||||||
|
"struct S<void> final {};\n";
|
||||||
|
ASSERT_EQUALS("4: struct S<void> ;\n"
|
||||||
|
"1: template < typename T >\n"
|
||||||
|
"2: struct S ;\n"
|
||||||
|
"3:\n"
|
||||||
|
"4: struct S<void> { } ;\n",
|
||||||
|
tokenize(code));
|
||||||
|
}
|
||||||
|
|
||||||
void varid_not_template_in_condition() {
|
void varid_not_template_in_condition() {
|
||||||
const char code1[] = "void f() { if (x<a||x>b); }";
|
const char code1[] = "void f() { if (x<a||x>b); }";
|
||||||
ASSERT_EQUALS("1: void f ( ) { if ( x < a || x > b ) { ; } }\n", tokenize(code1));
|
ASSERT_EQUALS("1: void f ( ) { if ( x < a || x > b ) { ; } }\n", tokenize(code1));
|
||||||
|
|
Loading…
Reference in New Issue