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:
IOBYTE 2018-12-13 00:34:10 -05:00 committed by Daniel Marjamäki
parent e0b64ec7a9
commit 0f83aff3b8
6 changed files with 132 additions and 33 deletions

View File

@ -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;
}

View File

@ -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 {

View File

@ -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);
}

View File

@ -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() {

View File

@ -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);

View File

@ -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;"));
}