Fix 11171: False positive: missing return statement when std::enable_if_t is used (#4504)
* Fix 11171: False positive: missing return statement when std::enable_if_t is used * Format * Return false * Workaround bug in cppcheck
This commit is contained in:
parent
657d9143f7
commit
fc39a4d1bb
|
@ -310,8 +310,6 @@ void CheckFunctions::checkMissingReturn()
|
||||||
continue;
|
continue;
|
||||||
if (Token::Match(function->retDef, "%name% (") && function->retDef->isUpperCaseName())
|
if (Token::Match(function->retDef, "%name% (") && function->retDef->isUpperCaseName())
|
||||||
continue;
|
continue;
|
||||||
if (mTokenizer->isCPP() && Token::Match(function->retDef, "std :: enable_if|enable_if_t"))
|
|
||||||
continue;
|
|
||||||
if (Function::returnsVoid(function, true))
|
if (Function::returnsVoid(function, true))
|
||||||
continue;
|
continue;
|
||||||
const Token *errorToken = checkMissingReturnScope(scope->bodyEnd, mSettings->library);
|
const Token *errorToken = checkMissingReturnScope(scope->bodyEnd, mSettings->library);
|
||||||
|
|
|
@ -2490,10 +2490,13 @@ const Token *Function::setFlags(const Token *tok1, const Scope *scope)
|
||||||
tok1 = tok1->link()->previous();
|
tok1 = tok1->link()->previous();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function template
|
else if (tok1->link() && tok1->str() == ">") {
|
||||||
else if (tok1->link() && tok1->str() == ">" && Token::simpleMatch(tok1->link()->previous(), "template <")) {
|
// Function template
|
||||||
templateDef = tok1->link()->previous();
|
if (Token::simpleMatch(tok1->link()->previous(), "template <")) {
|
||||||
break;
|
templateDef = tok1->link()->previous();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tok1 = tok1->link();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tok1;
|
return tok1;
|
||||||
|
@ -2853,60 +2856,88 @@ static bool isUnknownType(const Token* start, const Token* end)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Function::returnsConst(const Function* function, bool unknown)
|
static const Token* getEnableIfReturnType(const Token* start)
|
||||||
{
|
{
|
||||||
if (!function)
|
if (!start)
|
||||||
return false;
|
return nullptr;
|
||||||
if (function->type != Function::eFunction)
|
for (const Token* tok = start->next(); precedes(tok, start->link()); tok = tok->next()) {
|
||||||
return false;
|
if (tok->link() && Token::Match(tok, "(|[|{|<")) {
|
||||||
const Token* defEnd = function->returnDefEnd();
|
tok = tok->link();
|
||||||
if (Token::findsimplematch(function->retDef, "const", defEnd))
|
continue;
|
||||||
return true;
|
}
|
||||||
// Check for unknown types, which could be a const
|
if (Token::simpleMatch(tok, ","))
|
||||||
if (isUnknownType(function->retDef, defEnd))
|
return tok->next();
|
||||||
return unknown;
|
}
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Function::returnsReference(const Function* function, bool unknown)
|
template<class Predicate>
|
||||||
{
|
static bool checkReturns(const Function* function, bool unknown, bool emptyEnableIf, Predicate pred)
|
||||||
if (!function)
|
|
||||||
return false;
|
|
||||||
if (function->type != Function::eFunction)
|
|
||||||
return false;
|
|
||||||
const Token* defEnd = function->returnDefEnd();
|
|
||||||
if (defEnd->strAt(-1) == "&")
|
|
||||||
return true;
|
|
||||||
// Check for unknown types, which could be a reference
|
|
||||||
if (isUnknownType(function->retDef, defEnd))
|
|
||||||
return unknown;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Function::returnsVoid(const Function* function, bool unknown)
|
|
||||||
{
|
{
|
||||||
if (!function)
|
if (!function)
|
||||||
return false;
|
return false;
|
||||||
if (function->type != Function::eFunction && function->type != Function::eOperatorEqual)
|
if (function->type != Function::eFunction && function->type != Function::eOperatorEqual)
|
||||||
return false;
|
return false;
|
||||||
|
const Token* defStart = function->retDef;
|
||||||
const Token* defEnd = function->returnDefEnd();
|
const Token* defEnd = function->returnDefEnd();
|
||||||
if (defEnd->strAt(-1) == "void")
|
if (!defStart)
|
||||||
return true;
|
|
||||||
// Check for unknown types, which could be a void type
|
|
||||||
if (isUnknownType(function->retDef, defEnd))
|
|
||||||
return unknown;
|
return unknown;
|
||||||
if (unknown) {
|
if (!defEnd)
|
||||||
// void STDCALL foo()
|
return unknown;
|
||||||
const Token *def;
|
if (defEnd == defStart)
|
||||||
bool isVoid = false;
|
return unknown;
|
||||||
for (def = function->retDef; def && def->isName(); def = def->next())
|
if (pred(defStart, defEnd))
|
||||||
isVoid |= (def->str() == "void");
|
return true;
|
||||||
if (isVoid && def && !Token::Match(def, "*|&|&&"))
|
if (Token::Match(defEnd->tokAt(-1), "*|&|&&"))
|
||||||
return true;
|
return false;
|
||||||
|
// void STDCALL foo()
|
||||||
|
while (defEnd->previous() != defStart && Token::Match(defEnd->tokAt(-2), "%name%|> %name%") &&
|
||||||
|
!Token::Match(defEnd->tokAt(-2), "const|volatile"))
|
||||||
|
defEnd = defEnd->previous();
|
||||||
|
// enable_if
|
||||||
|
const Token* enableIfEnd = nullptr;
|
||||||
|
if (Token::simpleMatch(defEnd->previous(), ">"))
|
||||||
|
enableIfEnd = defEnd->previous();
|
||||||
|
else if (Token::simpleMatch(defEnd->tokAt(-3), "> :: type"))
|
||||||
|
enableIfEnd = defEnd->tokAt(-3);
|
||||||
|
if (enableIfEnd && enableIfEnd->link() &&
|
||||||
|
Token::Match(enableIfEnd->link()->previous(), "enable_if|enable_if_t|EnableIf")) {
|
||||||
|
if (const Token* start = getEnableIfReturnType(enableIfEnd->link())) {
|
||||||
|
defStart = start;
|
||||||
|
defEnd = enableIfEnd;
|
||||||
|
} else {
|
||||||
|
return emptyEnableIf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
assert(defEnd != defStart);
|
||||||
|
if (pred(defStart, defEnd))
|
||||||
|
return true;
|
||||||
|
if (isUnknownType(defStart, defEnd))
|
||||||
|
return unknown;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Function::returnsConst(const Function* function, bool unknown)
|
||||||
|
{
|
||||||
|
return checkReturns(function, unknown, false, [](const Token* defStart, const Token* defEnd) {
|
||||||
|
return Token::findsimplematch(defStart, "const", defEnd);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Function::returnsReference(const Function* function, bool unknown)
|
||||||
|
{
|
||||||
|
return checkReturns(function, unknown, false, [](UNUSED const Token* defStart, const Token* defEnd) {
|
||||||
|
return Token::simpleMatch(defEnd->previous(), "&");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Function::returnsVoid(const Function* function, bool unknown)
|
||||||
|
{
|
||||||
|
return checkReturns(function, unknown, true, [](UNUSED const Token* defStart, const Token* defEnd) {
|
||||||
|
return Token::simpleMatch(defEnd->previous(), "void");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<const Token*> Function::findReturns(const Function* f)
|
std::vector<const Token*> Function::findReturns(const Function* f)
|
||||||
{
|
{
|
||||||
std::vector<const Token*> result;
|
std::vector<const Token*> result;
|
||||||
|
|
|
@ -1726,7 +1726,37 @@ private:
|
||||||
"[test.cpp:10]: (error) Found a exit path from function with non-void return type that has missing return statement\n",
|
"[test.cpp:10]: (error) Found a exit path from function with non-void return type that has missing return statement\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
check("std::enable_if_t<sizeof(uint64_t) == 8> f() {}"); // #11171
|
// #11171
|
||||||
|
check("std::enable_if_t<sizeof(uint64_t) == 8> f() {}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("std::enable_if_t<sizeof(uint64_t) == 8, int> f() {}");
|
||||||
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:1]: (error) Found a exit path from function with non-void return type that has missing return statement\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
|
check("template<class T> std::enable_if_t<std::is_same<T, int>{}, int> f(T) {}");
|
||||||
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:1]: (error) Found a exit path from function with non-void return type that has missing return statement\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
|
check("template<class T> std::enable_if_t<std::is_same<T, int>{}> f(T) {}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("typename std::enable_if<sizeof(uint64_t) == 8>::type f() {}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("typename std::enable_if<sizeof(uint64_t) == 8, int>::type f() {}");
|
||||||
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:1]: (error) Found a exit path from function with non-void return type that has missing return statement\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
|
check("template<class T> typename std::enable_if<std::is_same<T, int>{}, int>::type f(T) {}");
|
||||||
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:1]: (error) Found a exit path from function with non-void return type that has missing return statement\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
|
check("template<class T> typename std::enable_if<std::is_same<T, int>{}>::type f(T) {}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue