Improve trailing return type support. (#1520)
* Improve trailing return type support. * Partial fix for #8889 (varid on function when using trailing return type) * Handle operators in templates.
This commit is contained in:
parent
e0b64ec7a9
commit
0f83aff3b8
|
@ -1420,15 +1420,16 @@ bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip over const, noexcept, throw, override, final and volatile specifiers
|
// 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();
|
tok2 = tok2->next();
|
||||||
if (tok2 && tok2->str() == "(")
|
if (tok2 && tok2->str() == "(")
|
||||||
tok2 = tok2->link()->next();
|
tok2 = tok2->link()->next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip over trailing return type
|
||||||
if (tok2 && tok2->str() == ".") {
|
if (tok2 && tok2->str() == ".") {
|
||||||
for (tok2 = tok2->next(); tok2; tok2 = tok2->next()) {
|
for (tok2 = tok2->next(); tok2; tok2 = tok2->next()) {
|
||||||
if (Token::Match(tok2, ";|{|="))
|
if (Token::Match(tok2, ";|{|=|override|final"))
|
||||||
break;
|
break;
|
||||||
if (tok2->link() && Token::Match(tok2, "<|[|("))
|
if (tok2->link() && Token::Match(tok2, "<|[|("))
|
||||||
tok2 = tok2->link();
|
tok2 = tok2->link();
|
||||||
|
@ -1794,9 +1795,17 @@ Function::Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *s
|
||||||
|
|
||||||
// find the return type
|
// find the return type
|
||||||
if (!isConstructor() && !isDestructor()) {
|
if (!isConstructor() && !isDestructor()) {
|
||||||
if (argDef->link()->strAt(1) == ".") // Trailing return type
|
// @todo auto type deduction should be checked
|
||||||
retDef = argDef->link()->tokAt(2);
|
// @todo attributes and exception specification can also precede trailing return type
|
||||||
else {
|
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() == ">")
|
if (tok1->str() == ">")
|
||||||
tok1 = tok1->next();
|
tok1 = tok1->next();
|
||||||
while (Token::Match(tok1, "extern|virtual|static|friend|struct|union|enum"))
|
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");
|
isPure(modifier == "0");
|
||||||
isDefault(modifier == "default");
|
isDefault(modifier == "default");
|
||||||
isDelete(modifier == "delete");
|
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
|
} else
|
||||||
break;
|
break;
|
||||||
tok = tok->next();
|
if (tok)
|
||||||
|
tok = tok->next();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mTokenizer->isFunctionHead(end, ":{")) {
|
if (mTokenizer->isFunctionHead(end, ":{")) {
|
||||||
|
@ -2696,6 +2710,8 @@ void SymbolDatabase::printOut(const char *title) const
|
||||||
std::cout << " isExplicit: " << func->isExplicit() << std::endl;
|
std::cout << " isExplicit: " << func->isExplicit() << std::endl;
|
||||||
std::cout << " isDefault: " << func->isDefault() << std::endl;
|
std::cout << " isDefault: " << func->isDefault() << std::endl;
|
||||||
std::cout << " isDelete: " << func->isDelete() << 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 << " isNoExcept: " << func->isNoExcept() << std::endl;
|
||||||
std::cout << " isThrow: " << func->isThrow() << std::endl;
|
std::cout << " isThrow: " << func->isThrow() << std::endl;
|
||||||
std::cout << " isOperator: " << func->isOperator() << 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 << " hasRvalRefQual: " << func->hasRvalRefQualifier() << std::endl;
|
||||||
std::cout << " isVariadic: " << func->isVariadic() << std::endl;
|
std::cout << " isVariadic: " << func->isVariadic() << std::endl;
|
||||||
std::cout << " isVolatile: " << func->isVolatile() << std::endl;
|
std::cout << " isVolatile: " << func->isVolatile() << std::endl;
|
||||||
|
std::cout << " hasTrailingReturnType: " << func->hasTrailingReturnType() << std::endl;
|
||||||
std::cout << " attributes:";
|
std::cout << " attributes:";
|
||||||
if (func->isAttributeConst())
|
if (func->isAttributeConst())
|
||||||
std::cout << " const ";
|
std::cout << " const ";
|
||||||
|
@ -2727,7 +2744,7 @@ void SymbolDatabase::printOut(const char *title) const
|
||||||
std::cout << " retDef: " << tokenToString(func->retDef, mTokenizer) << std::endl;
|
std::cout << " retDef: " << tokenToString(func->retDef, mTokenizer) << std::endl;
|
||||||
if (func->retDef) {
|
if (func->retDef) {
|
||||||
std::cout << " ";
|
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 << " " << tokenType(tok);
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -657,27 +657,28 @@ private:
|
||||||
class CPPCHECKLIB Function {
|
class CPPCHECKLIB Function {
|
||||||
/** @brief flags mask used to access specific bit. */
|
/** @brief flags mask used to access specific bit. */
|
||||||
enum {
|
enum {
|
||||||
fHasBody = (1 << 0), ///< @brief has implementation
|
fHasBody = (1 << 0), ///< @brief has implementation
|
||||||
fIsInline = (1 << 1), ///< @brief implementation in class definition
|
fIsInline = (1 << 1), ///< @brief implementation in class definition
|
||||||
fIsConst = (1 << 2), ///< @brief is const
|
fIsConst = (1 << 2), ///< @brief is const
|
||||||
fIsVirtual = (1 << 3), ///< @brief is virtual
|
fIsVirtual = (1 << 3), ///< @brief is virtual
|
||||||
fIsPure = (1 << 4), ///< @brief is pure virtual
|
fIsPure = (1 << 4), ///< @brief is pure virtual
|
||||||
fIsStatic = (1 << 5), ///< @brief is static
|
fIsStatic = (1 << 5), ///< @brief is static
|
||||||
fIsStaticLocal = (1 << 6), ///< @brief is static local
|
fIsStaticLocal = (1 << 6), ///< @brief is static local
|
||||||
fIsExtern = (1 << 7), ///< @brief is extern
|
fIsExtern = (1 << 7), ///< @brief is extern
|
||||||
fIsFriend = (1 << 8), ///< @brief is friend
|
fIsFriend = (1 << 8), ///< @brief is friend
|
||||||
fIsExplicit = (1 << 9), ///< @brief is explicit
|
fIsExplicit = (1 << 9), ///< @brief is explicit
|
||||||
fIsDefault = (1 << 10), ///< @brief is default
|
fIsDefault = (1 << 10), ///< @brief is default
|
||||||
fIsDelete = (1 << 11), ///< @brief is delete
|
fIsDelete = (1 << 11), ///< @brief is delete
|
||||||
fHasOverrideSpecifier = (1 << 12), ///< @brief does declaration contain 'override' specifier?
|
fHasOverrideSpecifier = (1 << 12), ///< @brief does declaration contain 'override' specifier?
|
||||||
fHasFinalSpecifier = (1 << 13), ///< @brief does declaration contain 'final' specifier?
|
fHasFinalSpecifier = (1 << 13), ///< @brief does declaration contain 'final' specifier?
|
||||||
fIsNoExcept = (1 << 14), ///< @brief is noexcept
|
fIsNoExcept = (1 << 14), ///< @brief is noexcept
|
||||||
fIsThrow = (1 << 15), ///< @brief is throw
|
fIsThrow = (1 << 15), ///< @brief is throw
|
||||||
fIsOperator = (1 << 16), ///< @brief is operator
|
fIsOperator = (1 << 16), ///< @brief is operator
|
||||||
fHasLvalRefQual = (1 << 17), ///< @brief has & lvalue ref-qualifier
|
fHasLvalRefQual = (1 << 17), ///< @brief has & lvalue ref-qualifier
|
||||||
fHasRvalRefQual = (1 << 18), ///< @brief has && rvalue ref-qualifier
|
fHasRvalRefQual = (1 << 18), ///< @brief has && rvalue ref-qualifier
|
||||||
fIsVariadic = (1 << 19), ///< @brief is variadic
|
fIsVariadic = (1 << 19), ///< @brief is variadic
|
||||||
fIsVolatile = (1 << 20) ///< @brief is volatile
|
fIsVolatile = (1 << 20), ///< @brief is volatile
|
||||||
|
fHasTrailingReturnType = (1 << 21), ///< @brief has trailing return type
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -819,6 +820,9 @@ public:
|
||||||
bool isVolatile() const {
|
bool isVolatile() const {
|
||||||
return getFlag(fIsVolatile);
|
return getFlag(fIsVolatile);
|
||||||
}
|
}
|
||||||
|
bool hasTrailingReturnType() const {
|
||||||
|
return getFlag(fHasTrailingReturnType);
|
||||||
|
}
|
||||||
|
|
||||||
void hasBody(bool state) {
|
void hasBody(bool state) {
|
||||||
setFlag(fHasBody, state);
|
setFlag(fHasBody, state);
|
||||||
|
@ -907,6 +911,9 @@ private:
|
||||||
void isVolatile(bool state) {
|
void isVolatile(bool state) {
|
||||||
setFlag(fIsVolatile, state);
|
setFlag(fIsVolatile, state);
|
||||||
}
|
}
|
||||||
|
void hasTrailingReturnType(bool state) {
|
||||||
|
return setFlag(fHasTrailingReturnType, state);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CPPCHECKLIB Scope {
|
class CPPCHECKLIB Scope {
|
||||||
|
|
|
@ -112,12 +112,16 @@ const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &end
|
||||||
if (Token::Match(tok, "%name% (") && tok->isUpperCaseName())
|
if (Token::Match(tok, "%name% (") && tok->isUpperCaseName())
|
||||||
tok = tok->linkAt(1)->next();
|
tok = tok->linkAt(1)->next();
|
||||||
if (tok && tok->str() == ".") { // trailing return type
|
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, "<|[|("))
|
if (tok->link() && Token::Match(tok, "<|[|("))
|
||||||
tok = tok->link();
|
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 ;"))
|
if (Token::Match(tok, "= 0|default|delete ;"))
|
||||||
tok = tok->tokAt(2);
|
tok = tok->tokAt(2);
|
||||||
|
|
||||||
return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr;
|
return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -9906,9 +9910,12 @@ void Tokenizer::simplifyOperatorName()
|
||||||
}
|
}
|
||||||
done = false;
|
done = false;
|
||||||
} else if (Token::Match(par, ".|%op%|,")) {
|
} else if (Token::Match(par, ".|%op%|,")) {
|
||||||
op += par->str();
|
// check for operator in template
|
||||||
par = par->next();
|
if (!(Token::Match(par, "<|>") && !op.empty())) {
|
||||||
done = false;
|
op += par->str();
|
||||||
|
par = par->next();
|
||||||
|
done = false;
|
||||||
|
}
|
||||||
} else if (Token::simpleMatch(par, "[ ]")) {
|
} else if (Token::simpleMatch(par, "[ ]")) {
|
||||||
op += "[]";
|
op += "[]";
|
||||||
par = par->tokAt(2);
|
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);
|
tok->str("operator" + op);
|
||||||
Token::eraseTokens(tok, par);
|
Token::eraseTokens(tok, par);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7034,6 +7034,18 @@ private:
|
||||||
checkOverride("class Base { virtual void f(); };\n"
|
checkOverride("class Base { virtual void f(); };\n"
|
||||||
"class Derived : Base { virtual void f() final; };");
|
"class Derived : Base { virtual void f() final; };");
|
||||||
ASSERT_EQUALS("", errout.str());
|
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() {
|
void overrideCVRefQualifiers() {
|
||||||
|
|
|
@ -295,6 +295,7 @@ private:
|
||||||
TEST_CASE(symboldatabase72); // #8600
|
TEST_CASE(symboldatabase72); // #8600
|
||||||
TEST_CASE(symboldatabase73); // #8603
|
TEST_CASE(symboldatabase73); // #8603
|
||||||
TEST_CASE(symboldatabase74); // #8838 - final
|
TEST_CASE(symboldatabase74); // #8838 - final
|
||||||
|
TEST_CASE(symboldatabase75);
|
||||||
|
|
||||||
TEST_CASE(createSymbolDatabaseFindAllScopes1);
|
TEST_CASE(createSymbolDatabaseFindAllScopes1);
|
||||||
|
|
||||||
|
@ -4147,6 +4148,43 @@ private:
|
||||||
ASSERT(f1->function->hasFinalSpecifier());
|
ASSERT(f1->function->hasFinalSpecifier());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void symboldatabase75() {
|
||||||
|
GET_SYMBOL_DB("template <typename T>\n"
|
||||||
|
"class optional {\n"
|
||||||
|
" auto value() & -> T &;\n"
|
||||||
|
" auto value() && -> T &&;\n"
|
||||||
|
" auto value() const& -> T const &;\n"
|
||||||
|
"};\n"
|
||||||
|
"template <typename T>\n"
|
||||||
|
"auto optional<T>::value() & -> T & {}\n"
|
||||||
|
"template <typename T>\n"
|
||||||
|
"auto optional<T>::value() && -> T && {}\n"
|
||||||
|
"template <typename T>\n"
|
||||||
|
"auto optional<T>::value() const & -> T const & {}\n"
|
||||||
|
"optional<int> 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() {
|
void createSymbolDatabaseFindAllScopes1() {
|
||||||
GET_SYMBOL_DB("void f() { union {int x; char *p;} a={0}; }");
|
GET_SYMBOL_DB("void f() { union {int x; char *p;} a={0}; }");
|
||||||
ASSERT(db->scopeList.size() == 3);
|
ASSERT(db->scopeList.size() == 3);
|
||||||
|
|
|
@ -394,6 +394,7 @@ private:
|
||||||
TEST_CASE(simplifyOperatorName8); // ticket #5706
|
TEST_CASE(simplifyOperatorName8); // ticket #5706
|
||||||
TEST_CASE(simplifyOperatorName9); // ticket #5709 - comma operator not properly tokenized
|
TEST_CASE(simplifyOperatorName9); // ticket #5709 - comma operator not properly tokenized
|
||||||
TEST_CASE(simplifyOperatorName10); // #8746 - using a::operator=
|
TEST_CASE(simplifyOperatorName10); // #8746 - using a::operator=
|
||||||
|
TEST_CASE(simplifyOperatorName11); // #8889
|
||||||
|
|
||||||
TEST_CASE(simplifyNullArray);
|
TEST_CASE(simplifyNullArray);
|
||||||
|
|
||||||
|
@ -6168,6 +6169,23 @@ private:
|
||||||
ASSERT_EQUALS("using a :: operator= ;", tokenizeAndStringify(code));
|
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 <typename T> void g(S<&T::operator+ >) {}";
|
||||||
|
ASSERT_EQUALS("template < typename T > void g ( S < & T :: operator+ > ) { }", tokenizeAndStringify(code2));
|
||||||
|
|
||||||
|
const char code3[] = "template <typename T> void g(S<&T::operator int>) {}";
|
||||||
|
ASSERT_EQUALS("template < typename T > void g ( S < & T :: operatorint > ) { }", tokenizeAndStringify(code3));
|
||||||
|
|
||||||
|
const char code4[] = "template <typename T> void g(S<&T::template operator- <double> >) {}";
|
||||||
|
ASSERT_EQUALS("template < typename T > void g ( S < & T :: template operator- < double > > ) { }", tokenizeAndStringify(code4));
|
||||||
|
}
|
||||||
|
|
||||||
void simplifyNullArray() {
|
void simplifyNullArray() {
|
||||||
ASSERT_EQUALS("* ( foo . bar [ 5 ] ) = x ;", tokenizeAndStringify("0[foo.bar[5]] = x;"));
|
ASSERT_EQUALS("* ( foo . bar [ 5 ] ) = x ;", tokenizeAndStringify("0[foo.bar[5]] = x;"));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue