diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 9fcb0edf0..635e167e1 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2495,7 +2495,7 @@ void CheckClass::checkOverride() if (!classScope->definedType || classScope->definedType->derivedFrom.empty()) continue; for (const Function &func : classScope->functionList) { - if (func.hasOverrideKeyword()) + if (func.hasOverrideSpecifier() || func.hasFinalSpecifier()) continue; const Function *baseFunc = func.getOverridenFunction(); if (baseFunc) @@ -2516,7 +2516,7 @@ void CheckClass::overrideError(const Function *funcInBase, const Function *funcI reportError(errorPath, Severity::style, "missingOverride", "$symbol:" + functionName + "\n" - "Function '$symbol' overrides function in base class but does not have the 'override' keyword.", + "The function '$symbol' overrides a function in a base class but is not marked with a 'override' specifier.", CWE(0U) /* Unknown CWE! */, false); } diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index f258afa1b..5eb03eb27 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -1755,6 +1755,10 @@ Function::Function(const Tokenizer *_tokenizer, const Token *tok, const Scope *s hasLvalRefQualifier(true); else if (tok->str() == "&&") hasRvalRefQualifier(true); + else if (tok->str() == "override") + setFlag(fHasOverrideSpecifier, true); + else if (tok->str() == "final") + setFlag(fHasFinalSpecifier, true); else if (tok->str() == "noexcept") { isNoExcept(!Token::simpleMatch(tok->next(), "( false )")); tok = tok->next(); diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 5f529a6da..bf6164b68 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -653,25 +653,26 @@ private: class CPPCHECKLIB Function { /** @brief flags mask used to access specific bit. */ enum { - fHasBody = (1 << 0), ///< @brief has implementation - fIsInline = (1 << 1), ///< @brief implementation in class definition - fIsConst = (1 << 2), ///< @brief is const - fIsVirtual = (1 << 3), ///< @brief is virtual - fIsPure = (1 << 4), ///< @brief is pure virtual - fIsStatic = (1 << 5), ///< @brief is static - fIsStaticLocal = (1 << 6), ///< @brief is static local - fIsExtern = (1 << 7), ///< @brief is extern - fIsFriend = (1 << 8), ///< @brief is friend - fIsExplicit = (1 << 9), ///< @brief is explicit - fIsDefault = (1 << 10), ///< @brief is default - fIsDelete = (1 << 11), ///< @brief is delete - fHasOverrideKeyword = (1 << 12), ///< @brief does declaration contain 'override' keyword? - fIsNoExcept = (1 << 13), ///< @brief is noexcept - fIsThrow = (1 << 14), ///< @brief is throw - fIsOperator = (1 << 15), ///< @brief is operator - fHasLvalRefQual = (1 << 16), ///< @brief has & lvalue ref-qualifier - fHasRvalRefQual = (1 << 17), ///< @brief has && rvalue ref-qualifier - fIsVariadic = (1 << 18) ///< @brief is variadic + fHasBody = (1 << 0), ///< @brief has implementation + fIsInline = (1 << 1), ///< @brief implementation in class definition + fIsConst = (1 << 2), ///< @brief is const + fIsVirtual = (1 << 3), ///< @brief is virtual + fIsPure = (1 << 4), ///< @brief is pure virtual + fIsStatic = (1 << 5), ///< @brief is static + fIsStaticLocal = (1 << 6), ///< @brief is static local + fIsExtern = (1 << 7), ///< @brief is extern + fIsFriend = (1 << 8), ///< @brief is friend + fIsExplicit = (1 << 9), ///< @brief is explicit + fIsDefault = (1 << 10), ///< @brief is default + fIsDelete = (1 << 11), ///< @brief is delete + fHasOverrideSpecifier = (1 << 12), ///< @brief does declaration contain 'override' specifier? + fHasFinalSpecifier = (1 << 13), ///< @brief does declaration contain 'final' specifier? + fIsNoExcept = (1 << 14), ///< @brief is noexcept + fIsThrow = (1 << 15), ///< @brief is throw + fIsOperator = (1 << 16), ///< @brief is operator + fHasLvalRefQual = (1 << 17), ///< @brief has & lvalue ref-qualifier + fHasRvalRefQual = (1 << 18), ///< @brief has && rvalue ref-qualifier + fIsVariadic = (1 << 19) ///< @brief is variadic }; /** @@ -806,8 +807,11 @@ public: bool isThrow() const { return getFlag(fIsThrow); } - bool hasOverrideKeyword() const { - return getFlag(fHasOverrideKeyword); + bool hasOverrideSpecifier() const { + return getFlag(fHasOverrideSpecifier); + } + bool hasFinalSpecifier() const { + return getFlag(fHasFinalSpecifier); } bool isOperator() const { return getFlag(fIsOperator); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 055ab58c5..3bb6ad3ae 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -103,7 +103,7 @@ const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &end } if (cpp && tok->str() == ")") { tok = tok->next(); - while (Token::Match(tok, "const|noexcept|override|volatile|&|&& !!(") || + while (Token::Match(tok, "const|noexcept|override|final|volatile|&|&& !!(") || (Token::Match(tok, "%name% !!(") && tok->isUpperCaseName())) tok = tok->next(); if (tok && tok->str() == ")") @@ -9030,26 +9030,9 @@ void Tokenizer::simplifyKeyword() tok->deleteNext(); continue; } - // final: - // 2) void f() final; <- function is final - if (Token::Match(tok, ") const|override|final")) { - Token* specifier = tok->tokAt(2); - while (Token::Match(specifier, "const|override|final")) { - specifier=specifier->next(); - } - if (Token::Match(specifier, "[{;]")) { - specifier = tok->next(); - while (!Token::Match(specifier, "[{;]")) { - if (specifier->str()=="final") - specifier->deleteThis(); - else - specifier=specifier->next(); - } - } - } // noexcept -> noexcept(true) - // 3) void f() noexcept; -> void f() noexcept(true); + // 2) void f() noexcept; -> void f() noexcept(true); if (Token::Match(tok, ") noexcept :|{|;|const|override|final")) { // Insertion is done in inverse order // The brackets are linked together accordingly afterwards diff --git a/test/testclass.cpp b/test/testclass.cpp index 06a0f0dd9..518ae014c 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -6619,7 +6619,15 @@ private: void override1() { checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { virtual void f(); };"); - ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) Function 'f' overrides function in base class but does not have the 'override' keyword.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) The function 'f' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout.str()); + + checkOverride("class Base { virtual void f(); };\n" + "class Derived : Base { virtual void f() override; };"); + ASSERT_EQUALS("", errout.str()); + + checkOverride("class Base { virtual void f(); };\n" + "class Derived : Base { virtual void f() final; };"); + ASSERT_EQUALS("", errout.str()); } }; diff --git a/test/testsimplifytokens.cpp b/test/testsimplifytokens.cpp index 47672245d..d2d2c0f86 100644 --- a/test/testsimplifytokens.cpp +++ b/test/testsimplifytokens.cpp @@ -3268,7 +3268,6 @@ private: ASSERT_EQUALS("int foo ( ) { }", tok("__inline int foo ( ) { }", true)); ASSERT_EQUALS("int foo ( ) { }", tok("__forceinline int foo ( ) { }", true)); ASSERT_EQUALS("int foo ( ) { }", tok("constexpr int foo() { }", true)); - ASSERT_EQUALS("class C { int f ( ) ; } ;", tok("class C { int f() final ; };", true)); ASSERT_EQUALS("void f ( ) { int final [ 10 ] ; }", tok("void f() { int final[10]; }", true)); ASSERT_EQUALS("int * p ;", tok("int * __restrict p;", "test.c")); ASSERT_EQUALS("int * * p ;", tok("int * __restrict__ * p;", "test.c")); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index cb404b802..f37d6c906 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -4059,18 +4059,6 @@ private: "}\n" "} ;"; ASSERT_EQUALS(out1, tokenizeAndStringify(in1)); - const char in2[] = "class Derived{\n" - " virtual int test() final override;" - "};"; - const char out2[] = "class Derived {\n" - "virtual int test ( ) override ; } ;"; - ASSERT_EQUALS(out2, tokenizeAndStringify(in2)); - const char in3[] = "class Derived{\n" - " virtual int test() final override const;" - "};"; - const char out3[] = "class Derived {\n" - "virtual int test ( ) override const ; } ;"; - ASSERT_EQUALS(out3, tokenizeAndStringify(in3)); const char in4 [] = "struct B final : A { void foo(); };"; const char out4 [] = "struct B : A { void foo ( ) ; } ;";