Skip to content

Commit 0f83aff

Browse files
IOBYTEdanmar
authored andcommitted
Improve trailing return type support. (cppcheck-opensource#1520)
* Improve trailing return type support. * Partial fix for #8889 (varid on function when using trailing return type) * Handle operators in templates.
1 parent e0b64ec commit 0f83aff

6 files changed

Lines changed: 132 additions & 33 deletions

File tree

lib/symboldatabase.cpp

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,15 +1420,16 @@ bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const
14201420
}
14211421

14221422
// skip over const, noexcept, throw, override, final and volatile specifiers
1423-
while (Token::Match(tok2, "const|noexcept|throw|override|final|volatile")) {
1423+
while (Token::Match(tok2, "const|noexcept|throw|override|final|volatile|&|&&")) {
14241424
tok2 = tok2->next();
14251425
if (tok2 && tok2->str() == "(")
14261426
tok2 = tok2->link()->next();
14271427
}
14281428

1429+
// skip over trailing return type
14291430
if (tok2 && tok2->str() == ".") {
14301431
for (tok2 = tok2->next(); tok2; tok2 = tok2->next()) {
1431-
if (Token::Match(tok2, ";|{|="))
1432+
if (Token::Match(tok2, ";|{|=|override|final"))
14321433
break;
14331434
if (tok2->link() && Token::Match(tok2, "<|[|("))
14341435
tok2 = tok2->link();
@@ -1794,9 +1795,17 @@ Function::Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *s
17941795

17951796
// find the return type
17961797
if (!isConstructor() && !isDestructor()) {
1797-
if (argDef->link()->strAt(1) == ".") // Trailing return type
1798-
retDef = argDef->link()->tokAt(2);
1799-
else {
1798+
// @todo auto type deduction should be checked
1799+
// @todo attributes and exception specification can also precede trailing return type
1800+
if (Token::Match(argDef->link()->next(), "const|volatile| &|&&| .")) { // Trailing return type
1801+
hasTrailingReturnType(true);
1802+
if (argDef->link()->strAt(1) == ".")
1803+
retDef = argDef->link()->tokAt(2);
1804+
else if (argDef->link()->strAt(2) == ".")
1805+
retDef = argDef->link()->tokAt(3);
1806+
else if (argDef->link()->strAt(3) == ".")
1807+
retDef = argDef->link()->tokAt(4);
1808+
} else {
18001809
if (tok1->str() == ">")
18011810
tok1 = tok1->next();
18021811
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
18361845
isPure(modifier == "0");
18371846
isDefault(modifier == "default");
18381847
isDelete(modifier == "delete");
1848+
} else if (tok->str() == ".") { // trailing return type
1849+
// skip over return type
1850+
while (tok && !Token::Match(tok->next(), ";|{|override|final"))
1851+
tok = tok->next();
18391852
} else
18401853
break;
1841-
tok = tok->next();
1854+
if (tok)
1855+
tok = tok->next();
18421856
}
18431857

18441858
if (mTokenizer->isFunctionHead(end, ":{")) {
@@ -2696,13 +2710,16 @@ void SymbolDatabase::printOut(const char *title) const
26962710
std::cout << " isExplicit: " << func->isExplicit() << std::endl;
26972711
std::cout << " isDefault: " << func->isDefault() << std::endl;
26982712
std::cout << " isDelete: " << func->isDelete() << std::endl;
2713+
std::cout << " hasOverrideSpecifier: " << func->hasOverrideSpecifier() << std::endl;
2714+
std::cout << " hasFinalSpecifier: " << func->hasFinalSpecifier() << std::endl;
26992715
std::cout << " isNoExcept: " << func->isNoExcept() << std::endl;
27002716
std::cout << " isThrow: " << func->isThrow() << std::endl;
27012717
std::cout << " isOperator: " << func->isOperator() << std::endl;
27022718
std::cout << " hasLvalRefQual: " << func->hasLvalRefQualifier() << std::endl;
27032719
std::cout << " hasRvalRefQual: " << func->hasRvalRefQualifier() << std::endl;
27042720
std::cout << " isVariadic: " << func->isVariadic() << std::endl;
27052721
std::cout << " isVolatile: " << func->isVolatile() << std::endl;
2722+
std::cout << " hasTrailingReturnType: " << func->hasTrailingReturnType() << std::endl;
27062723
std::cout << " attributes:";
27072724
if (func->isAttributeConst())
27082725
std::cout << " const ";
@@ -2727,7 +2744,7 @@ void SymbolDatabase::printOut(const char *title) const
27272744
std::cout << " retDef: " << tokenToString(func->retDef, mTokenizer) << std::endl;
27282745
if (func->retDef) {
27292746
std::cout << " ";
2730-
for (const Token * tok = func->retDef; tok && tok != func->tokenDef && !Token::Match(tok, "{|;"); tok = tok->next())
2747+
for (const Token * tok = func->retDef; tok && tok != func->tokenDef && !Token::Match(tok, "{|;|override|final"); tok = tok->next())
27312748
std::cout << " " << tokenType(tok);
27322749
std::cout << std::endl;
27332750
}

lib/symboldatabase.h

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -657,27 +657,28 @@ class CPPCHECKLIB Variable {
657657
class CPPCHECKLIB Function {
658658
/** @brief flags mask used to access specific bit. */
659659
enum {
660-
fHasBody = (1 << 0), ///< @brief has implementation
661-
fIsInline = (1 << 1), ///< @brief implementation in class definition
662-
fIsConst = (1 << 2), ///< @brief is const
663-
fIsVirtual = (1 << 3), ///< @brief is virtual
664-
fIsPure = (1 << 4), ///< @brief is pure virtual
665-
fIsStatic = (1 << 5), ///< @brief is static
666-
fIsStaticLocal = (1 << 6), ///< @brief is static local
667-
fIsExtern = (1 << 7), ///< @brief is extern
668-
fIsFriend = (1 << 8), ///< @brief is friend
669-
fIsExplicit = (1 << 9), ///< @brief is explicit
670-
fIsDefault = (1 << 10), ///< @brief is default
671-
fIsDelete = (1 << 11), ///< @brief is delete
672-
fHasOverrideSpecifier = (1 << 12), ///< @brief does declaration contain 'override' specifier?
673-
fHasFinalSpecifier = (1 << 13), ///< @brief does declaration contain 'final' specifier?
674-
fIsNoExcept = (1 << 14), ///< @brief is noexcept
675-
fIsThrow = (1 << 15), ///< @brief is throw
676-
fIsOperator = (1 << 16), ///< @brief is operator
677-
fHasLvalRefQual = (1 << 17), ///< @brief has & lvalue ref-qualifier
678-
fHasRvalRefQual = (1 << 18), ///< @brief has && rvalue ref-qualifier
679-
fIsVariadic = (1 << 19), ///< @brief is variadic
680-
fIsVolatile = (1 << 20) ///< @brief is volatile
660+
fHasBody = (1 << 0), ///< @brief has implementation
661+
fIsInline = (1 << 1), ///< @brief implementation in class definition
662+
fIsConst = (1 << 2), ///< @brief is const
663+
fIsVirtual = (1 << 3), ///< @brief is virtual
664+
fIsPure = (1 << 4), ///< @brief is pure virtual
665+
fIsStatic = (1 << 5), ///< @brief is static
666+
fIsStaticLocal = (1 << 6), ///< @brief is static local
667+
fIsExtern = (1 << 7), ///< @brief is extern
668+
fIsFriend = (1 << 8), ///< @brief is friend
669+
fIsExplicit = (1 << 9), ///< @brief is explicit
670+
fIsDefault = (1 << 10), ///< @brief is default
671+
fIsDelete = (1 << 11), ///< @brief is delete
672+
fHasOverrideSpecifier = (1 << 12), ///< @brief does declaration contain 'override' specifier?
673+
fHasFinalSpecifier = (1 << 13), ///< @brief does declaration contain 'final' specifier?
674+
fIsNoExcept = (1 << 14), ///< @brief is noexcept
675+
fIsThrow = (1 << 15), ///< @brief is throw
676+
fIsOperator = (1 << 16), ///< @brief is operator
677+
fHasLvalRefQual = (1 << 17), ///< @brief has & lvalue ref-qualifier
678+
fHasRvalRefQual = (1 << 18), ///< @brief has && rvalue ref-qualifier
679+
fIsVariadic = (1 << 19), ///< @brief is variadic
680+
fIsVolatile = (1 << 20), ///< @brief is volatile
681+
fHasTrailingReturnType = (1 << 21), ///< @brief has trailing return type
681682
};
682683

683684
/**
@@ -819,6 +820,9 @@ class CPPCHECKLIB Function {
819820
bool isVolatile() const {
820821
return getFlag(fIsVolatile);
821822
}
823+
bool hasTrailingReturnType() const {
824+
return getFlag(fHasTrailingReturnType);
825+
}
822826

823827
void hasBody(bool state) {
824828
setFlag(fHasBody, state);
@@ -907,6 +911,9 @@ class CPPCHECKLIB Function {
907911
void isVolatile(bool state) {
908912
setFlag(fIsVolatile, state);
909913
}
914+
void hasTrailingReturnType(bool state) {
915+
return setFlag(fHasTrailingReturnType, state);
916+
}
910917
};
911918

912919
class CPPCHECKLIB Scope {

lib/tokenize.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,16 @@ const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &end
112112
if (Token::Match(tok, "%name% (") && tok->isUpperCaseName())
113113
tok = tok->linkAt(1)->next();
114114
if (tok && tok->str() == ".") { // trailing return type
115-
for (tok = tok->next(); tok && !Token::Match(tok, "[;{]"); tok = tok->next())
115+
for (tok = tok->next(); tok && !Token::Match(tok, ";|{|override|final"); tok = tok->next())
116116
if (tok->link() && Token::Match(tok, "<|[|("))
117117
tok = tok->link();
118118
}
119+
while (Token::Match(tok, "override|final !!(") ||
120+
(Token::Match(tok, "%name% !!(") && tok->isUpperCaseName()))
121+
tok = tok->next();
119122
if (Token::Match(tok, "= 0|default|delete ;"))
120123
tok = tok->tokAt(2);
124+
121125
return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr;
122126
}
123127
return nullptr;
@@ -9906,9 +9910,12 @@ void Tokenizer::simplifyOperatorName()
99069910
}
99079911
done = false;
99089912
} else if (Token::Match(par, ".|%op%|,")) {
9909-
op += par->str();
9910-
par = par->next();
9911-
done = false;
9913+
// check for operator in template
9914+
if (!(Token::Match(par, "<|>") && !op.empty())) {
9915+
op += par->str();
9916+
par = par->next();
9917+
done = false;
9918+
}
99129919
} else if (Token::simpleMatch(par, "[ ]")) {
99139920
op += "[]";
99149921
par = par->tokAt(2);
@@ -9928,7 +9935,7 @@ void Tokenizer::simplifyOperatorName()
99289935
}
99299936
}
99309937

9931-
if (par && operatorEnd(par->link())) {
9938+
if (par && (Token::Match(par, "<|>") || isFunctionHead(par, "{|;"))) {
99329939
tok->str("operator" + op);
99339940
Token::eraseTokens(tok, par);
99349941
}

test/testclass.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7034,6 +7034,18 @@ class TestClass : public TestFixture {
70347034
checkOverride("class Base { virtual void f(); };\n"
70357035
"class Derived : Base { virtual void f() final; };");
70367036
ASSERT_EQUALS("", errout.str());
7037+
7038+
checkOverride("class Base {\n"
7039+
"public:\n"
7040+
" virtual auto foo( ) const -> size_t { return 1; }\n"
7041+
" virtual auto bar( ) const -> size_t { return 1; }\n"
7042+
"};\n"
7043+
"class Derived : public Base {\n"
7044+
"public :\n"
7045+
" auto foo( ) const -> size_t { return 0; }\n"
7046+
" auto bar( ) const -> size_t override { return 0; }\n"
7047+
"};");
7048+
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());
70377049
}
70387050

70397051
void overrideCVRefQualifiers() {

test/testsymboldatabase.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ class TestSymbolDatabase: public TestFixture {
295295
TEST_CASE(symboldatabase72); // #8600
296296
TEST_CASE(symboldatabase73); // #8603
297297
TEST_CASE(symboldatabase74); // #8838 - final
298+
TEST_CASE(symboldatabase75);
298299

299300
TEST_CASE(createSymbolDatabaseFindAllScopes1);
300301

@@ -4147,6 +4148,43 @@ class TestSymbolDatabase: public TestFixture {
41474148
ASSERT(f1->function->hasFinalSpecifier());
41484149
}
41494150

4151+
void symboldatabase75() {
4152+
GET_SYMBOL_DB("template <typename T>\n"
4153+
"class optional {\n"
4154+
" auto value() & -> T &;\n"
4155+
" auto value() && -> T &&;\n"
4156+
" auto value() const& -> T const &;\n"
4157+
"};\n"
4158+
"template <typename T>\n"
4159+
"auto optional<T>::value() & -> T & {}\n"
4160+
"template <typename T>\n"
4161+
"auto optional<T>::value() && -> T && {}\n"
4162+
"template <typename T>\n"
4163+
"auto optional<T>::value() const & -> T const & {}\n"
4164+
"optional<int> i;");
4165+
4166+
ASSERT_EQUALS(5, db->scopeList.size());
4167+
ASSERT_EQUALS(3, db->functionScopes.size());
4168+
4169+
const Scope *f = db->functionScopes[0];
4170+
ASSERT(f->function->hasBody());
4171+
ASSERT(!f->function->isConst());
4172+
ASSERT(f->function->hasTrailingReturnType());
4173+
ASSERT(f->function->hasLvalRefQualifier());
4174+
4175+
f = db->functionScopes[1];
4176+
ASSERT(f->function->hasBody());
4177+
ASSERT(!f->function->isConst());
4178+
ASSERT(f->function->hasTrailingReturnType());
4179+
ASSERT(f->function->hasRvalRefQualifier());
4180+
4181+
f = db->functionScopes[2];
4182+
ASSERT(f->function->hasBody());
4183+
ASSERT(f->function->isConst());
4184+
ASSERT(f->function->hasTrailingReturnType());
4185+
ASSERT(f->function->hasLvalRefQualifier());
4186+
}
4187+
41504188
void createSymbolDatabaseFindAllScopes1() {
41514189
GET_SYMBOL_DB("void f() { union {int x; char *p;} a={0}; }");
41524190
ASSERT(db->scopeList.size() == 3);

test/testtokenize.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ class TestTokenizer : public TestFixture {
394394
TEST_CASE(simplifyOperatorName8); // ticket #5706
395395
TEST_CASE(simplifyOperatorName9); // ticket #5709 - comma operator not properly tokenized
396396
TEST_CASE(simplifyOperatorName10); // #8746 - using a::operator=
397+
TEST_CASE(simplifyOperatorName11); // #8889
397398

398399
TEST_CASE(simplifyNullArray);
399400

@@ -6168,6 +6169,23 @@ class TestTokenizer : public TestFixture {
61686169
ASSERT_EQUALS("using a :: operator= ;", tokenizeAndStringify(code));
61696170
}
61706171

6172+
void simplifyOperatorName11() { // #8889
6173+
const char code[] = "auto operator = (const Fred & other) -> Fred & ;";
6174+
ASSERT_EQUALS("auto operator= ( const Fred & other ) . Fred & ;", tokenizeAndStringify(code));
6175+
6176+
const char code1[] = "auto operator = (const Fred & other) -> Fred & { }";
6177+
ASSERT_EQUALS("auto operator= ( const Fred & other ) . Fred & { }", tokenizeAndStringify(code1));
6178+
6179+
const char code2[] = "template <typename T> void g(S<&T::operator+ >) {}";
6180+
ASSERT_EQUALS("template < typename T > void g ( S < & T :: operator+ > ) { }", tokenizeAndStringify(code2));
6181+
6182+
const char code3[] = "template <typename T> void g(S<&T::operator int>) {}";
6183+
ASSERT_EQUALS("template < typename T > void g ( S < & T :: operatorint > ) { }", tokenizeAndStringify(code3));
6184+
6185+
const char code4[] = "template <typename T> void g(S<&T::template operator- <double> >) {}";
6186+
ASSERT_EQUALS("template < typename T > void g ( S < & T :: template operator- < double > > ) { }", tokenizeAndStringify(code4));
6187+
}
6188+
61716189
void simplifyNullArray() {
61726190
ASSERT_EQUALS("* ( foo . bar [ 5 ] ) = x ;", tokenizeAndStringify("0[foo.bar[5]] = x;"));
61736191
}

0 commit comments

Comments
 (0)