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
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 <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() {
|
||||
GET_SYMBOL_DB("void f() { union {int x; char *p;} a={0}; }");
|
||||
ASSERT(db->scopeList.size() == 3);
|
||||
|
|
|
@ -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 <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() {
|
||||
ASSERT_EQUALS("* ( foo . bar [ 5 ] ) = x ;", tokenizeAndStringify("0[foo.bar[5]] = x;"));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue