diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 115e13ad2..acafaa98d 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -1420,15 +1420,16 @@ bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const } // skip over const, noexcept, throw, override, final and volatile specifiers - while (Token::Match(tok2, "const|noexcept|throw|override|final|volatile")) { + while (Token::Match(tok2, "const|noexcept|throw|override|final|volatile|&|&&")) { tok2 = tok2->next(); if (tok2 && tok2->str() == "(") tok2 = tok2->link()->next(); } + // skip over trailing return type if (tok2 && tok2->str() == ".") { for (tok2 = tok2->next(); tok2; tok2 = tok2->next()) { - if (Token::Match(tok2, ";|{|=")) + if (Token::Match(tok2, ";|{|=|override|final")) break; if (tok2->link() && Token::Match(tok2, "<|[|(")) tok2 = tok2->link(); @@ -1794,9 +1795,17 @@ Function::Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *s // find the return type if (!isConstructor() && !isDestructor()) { - if (argDef->link()->strAt(1) == ".") // Trailing return type - retDef = argDef->link()->tokAt(2); - else { + // @todo auto type deduction should be checked + // @todo attributes and exception specification can also precede trailing return type + if (Token::Match(argDef->link()->next(), "const|volatile| &|&&| .")) { // Trailing return type + hasTrailingReturnType(true); + if (argDef->link()->strAt(1) == ".") + retDef = argDef->link()->tokAt(2); + else if (argDef->link()->strAt(2) == ".") + retDef = argDef->link()->tokAt(3); + else if (argDef->link()->strAt(3) == ".") + retDef = argDef->link()->tokAt(4); + } else { if (tok1->str() == ">") tok1 = tok1->next(); while (Token::Match(tok1, "extern|virtual|static|friend|struct|union|enum")) @@ -1836,9 +1845,14 @@ Function::Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *s isPure(modifier == "0"); isDefault(modifier == "default"); isDelete(modifier == "delete"); + } else if (tok->str() == ".") { // trailing return type + // skip over return type + while (tok && !Token::Match(tok->next(), ";|{|override|final")) + tok = tok->next(); } else break; - tok = tok->next(); + if (tok) + tok = tok->next(); } if (mTokenizer->isFunctionHead(end, ":{")) { @@ -2696,6 +2710,8 @@ void SymbolDatabase::printOut(const char *title) const std::cout << " isExplicit: " << func->isExplicit() << std::endl; std::cout << " isDefault: " << func->isDefault() << std::endl; std::cout << " isDelete: " << func->isDelete() << std::endl; + std::cout << " hasOverrideSpecifier: " << func->hasOverrideSpecifier() << std::endl; + std::cout << " hasFinalSpecifier: " << func->hasFinalSpecifier() << std::endl; std::cout << " isNoExcept: " << func->isNoExcept() << std::endl; std::cout << " isThrow: " << func->isThrow() << std::endl; std::cout << " isOperator: " << func->isOperator() << std::endl; @@ -2703,6 +2719,7 @@ void SymbolDatabase::printOut(const char *title) const std::cout << " hasRvalRefQual: " << func->hasRvalRefQualifier() << std::endl; std::cout << " isVariadic: " << func->isVariadic() << std::endl; std::cout << " isVolatile: " << func->isVolatile() << std::endl; + std::cout << " hasTrailingReturnType: " << func->hasTrailingReturnType() << std::endl; std::cout << " attributes:"; if (func->isAttributeConst()) std::cout << " const "; @@ -2727,7 +2744,7 @@ void SymbolDatabase::printOut(const char *title) const std::cout << " retDef: " << tokenToString(func->retDef, mTokenizer) << std::endl; if (func->retDef) { std::cout << " "; - for (const Token * tok = func->retDef; tok && tok != func->tokenDef && !Token::Match(tok, "{|;"); tok = tok->next()) + for (const Token * tok = func->retDef; tok && tok != func->tokenDef && !Token::Match(tok, "{|;|override|final"); tok = tok->next()) std::cout << " " << tokenType(tok); std::cout << std::endl; } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index fbb129a5b..69ed960f8 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -657,27 +657,28 @@ 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 - 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 - fIsVolatile = (1 << 20) ///< @brief is volatile + 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 + fIsVolatile = (1 << 20), ///< @brief is volatile + fHasTrailingReturnType = (1 << 21), ///< @brief has trailing return type }; /** @@ -819,6 +820,9 @@ public: bool isVolatile() const { return getFlag(fIsVolatile); } + bool hasTrailingReturnType() const { + return getFlag(fHasTrailingReturnType); + } void hasBody(bool state) { setFlag(fHasBody, state); @@ -907,6 +911,9 @@ private: void isVolatile(bool state) { setFlag(fIsVolatile, state); } + void hasTrailingReturnType(bool state) { + return setFlag(fHasTrailingReturnType, state); + } }; class CPPCHECKLIB Scope { diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 5959fb39d..cfca4c12d 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -112,12 +112,16 @@ const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &end if (Token::Match(tok, "%name% (") && tok->isUpperCaseName()) tok = tok->linkAt(1)->next(); if (tok && tok->str() == ".") { // trailing return type - for (tok = tok->next(); tok && !Token::Match(tok, "[;{]"); tok = tok->next()) + for (tok = tok->next(); tok && !Token::Match(tok, ";|{|override|final"); tok = tok->next()) if (tok->link() && Token::Match(tok, "<|[|(")) tok = tok->link(); } + while (Token::Match(tok, "override|final !!(") || + (Token::Match(tok, "%name% !!(") && tok->isUpperCaseName())) + tok = tok->next(); if (Token::Match(tok, "= 0|default|delete ;")) tok = tok->tokAt(2); + return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr; } return nullptr; @@ -9906,9 +9910,12 @@ void Tokenizer::simplifyOperatorName() } done = false; } else if (Token::Match(par, ".|%op%|,")) { - op += par->str(); - par = par->next(); - done = false; + // check for operator in template + if (!(Token::Match(par, "<|>") && !op.empty())) { + op += par->str(); + par = par->next(); + done = false; + } } else if (Token::simpleMatch(par, "[ ]")) { op += "[]"; par = par->tokAt(2); @@ -9928,7 +9935,7 @@ void Tokenizer::simplifyOperatorName() } } - if (par && operatorEnd(par->link())) { + if (par && (Token::Match(par, "<|>") || isFunctionHead(par, "{|;"))) { tok->str("operator" + op); Token::eraseTokens(tok, par); } diff --git a/test/testclass.cpp b/test/testclass.cpp index 47a1843c7..5c97ebe07 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -7034,6 +7034,18 @@ private: checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { virtual void f() final; };"); ASSERT_EQUALS("", errout.str()); + + checkOverride("class Base {\n" + "public:\n" + " virtual auto foo( ) const -> size_t { return 1; }\n" + " virtual auto bar( ) const -> size_t { return 1; }\n" + "};\n" + "class Derived : public Base {\n" + "public :\n" + " auto foo( ) const -> size_t { return 0; }\n" + " auto bar( ) const -> size_t override { return 0; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:8]: (style) The function 'foo' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout.str()); } void overrideCVRefQualifiers() { diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index aba087797..e3fb1f421 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -295,6 +295,7 @@ private: TEST_CASE(symboldatabase72); // #8600 TEST_CASE(symboldatabase73); // #8603 TEST_CASE(symboldatabase74); // #8838 - final + TEST_CASE(symboldatabase75); TEST_CASE(createSymbolDatabaseFindAllScopes1); @@ -4147,6 +4148,43 @@ private: ASSERT(f1->function->hasFinalSpecifier()); } + void symboldatabase75() { + GET_SYMBOL_DB("template \n" + "class optional {\n" + " auto value() & -> T &;\n" + " auto value() && -> T &&;\n" + " auto value() const& -> T const &;\n" + "};\n" + "template \n" + "auto optional::value() & -> T & {}\n" + "template \n" + "auto optional::value() && -> T && {}\n" + "template \n" + "auto optional::value() const & -> T const & {}\n" + "optional i;"); + + ASSERT_EQUALS(5, db->scopeList.size()); + ASSERT_EQUALS(3, db->functionScopes.size()); + + const Scope *f = db->functionScopes[0]; + ASSERT(f->function->hasBody()); + ASSERT(!f->function->isConst()); + ASSERT(f->function->hasTrailingReturnType()); + ASSERT(f->function->hasLvalRefQualifier()); + + f = db->functionScopes[1]; + ASSERT(f->function->hasBody()); + ASSERT(!f->function->isConst()); + ASSERT(f->function->hasTrailingReturnType()); + ASSERT(f->function->hasRvalRefQualifier()); + + f = db->functionScopes[2]; + ASSERT(f->function->hasBody()); + ASSERT(f->function->isConst()); + ASSERT(f->function->hasTrailingReturnType()); + ASSERT(f->function->hasLvalRefQualifier()); + } + void createSymbolDatabaseFindAllScopes1() { GET_SYMBOL_DB("void f() { union {int x; char *p;} a={0}; }"); ASSERT(db->scopeList.size() == 3); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 36d9dc421..862e69e78 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -394,6 +394,7 @@ private: TEST_CASE(simplifyOperatorName8); // ticket #5706 TEST_CASE(simplifyOperatorName9); // ticket #5709 - comma operator not properly tokenized TEST_CASE(simplifyOperatorName10); // #8746 - using a::operator= + TEST_CASE(simplifyOperatorName11); // #8889 TEST_CASE(simplifyNullArray); @@ -6168,6 +6169,23 @@ private: ASSERT_EQUALS("using a :: operator= ;", tokenizeAndStringify(code)); } + void simplifyOperatorName11() { // #8889 + const char code[] = "auto operator = (const Fred & other) -> Fred & ;"; + ASSERT_EQUALS("auto operator= ( const Fred & other ) . Fred & ;", tokenizeAndStringify(code)); + + const char code1[] = "auto operator = (const Fred & other) -> Fred & { }"; + ASSERT_EQUALS("auto operator= ( const Fred & other ) . Fred & { }", tokenizeAndStringify(code1)); + + const char code2[] = "template void g(S<&T::operator+ >) {}"; + ASSERT_EQUALS("template < typename T > void g ( S < & T :: operator+ > ) { }", tokenizeAndStringify(code2)); + + const char code3[] = "template void g(S<&T::operator int>) {}"; + ASSERT_EQUALS("template < typename T > void g ( S < & T :: operatorint > ) { }", tokenizeAndStringify(code3)); + + const char code4[] = "template void g(S<&T::template operator- >) {}"; + ASSERT_EQUALS("template < typename T > void g ( S < & T :: template operator- < double > > ) { }", tokenizeAndStringify(code4)); + } + void simplifyNullArray() { ASSERT_EQUALS("* ( foo . bar [ 5 ] ) = x ;", tokenizeAndStringify("0[foo.bar[5]] = x;")); }