Add std::*begin and std::*end cfg (#4796)

This commit is contained in:
Mateusz Michalak 2023-03-09 17:06:53 +01:00 committed by GitHub
parent 9351eddbca
commit 2d5cabed4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 163 additions and 15 deletions

View File

@ -8539,6 +8539,22 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
<valid>0:</valid>
</arg>
</function>
<function name="std::begin,std::cbegin,std::rbegin,std::crbegin">
<use-retval/>
<leak-ignore/>
<noreturn>false</noreturn>
<arg nr="1" direction="in"/>
<container yields="start-iterator"/>
<returnValue type="iterator" container="1"/>
</function>
<function name="std::end,std::cend,std::rend,std::crend">
<use-retval/>
<leak-ignore/>
<noreturn>false</noreturn>
<arg nr="1" direction="in"/>
<container yields="end-iterator"/>
<returnValue type="iterator" container="1"/>
</function>
<memory>
<alloc init="false" buffer-size="malloc">malloc</alloc>
<alloc init="true" buffer-size="calloc">calloc</alloc>

View File

@ -282,6 +282,7 @@ Library::Container::Action astContainerAction(const Token* tok, const Token** ft
return Library::Container::Action::NO_ACTION;
return tok->valueType()->container->getAction(ftok2->str());
}
Library::Container::Yield astContainerYield(const Token* tok, const Token** ftok)
{
const Token* ftok2 = getContainerFunction(tok);
@ -292,6 +293,20 @@ Library::Container::Yield astContainerYield(const Token* tok, const Token** ftok
return tok->valueType()->container->getYield(ftok2->str());
}
Library::Container::Yield astFunctionYield(const Token* tok, const Settings* settings, const Token** ftok)
{
if (!tok)
return Library::Container::Yield::NO_YIELD;
const auto* function = settings->library.getFunction(tok);
if (!function)
return Library::Container::Yield::NO_YIELD;
if (ftok)
*ftok = tok;
return function->containerYield;
}
bool astIsRangeBasedForDecl(const Token* tok)
{
return Token::simpleMatch(tok->astParent(), ":") && Token::simpleMatch(tok->astParent()->astParent(), "(");

View File

@ -153,6 +153,8 @@ bool astIsContainerOwned(const Token* tok);
Library::Container::Action astContainerAction(const Token* tok, const Token** ftok = nullptr);
Library::Container::Yield astContainerYield(const Token* tok, const Token** ftok = nullptr);
Library::Container::Yield astFunctionYield(const Token* tok, const Settings* settings, const Token** ftok = nullptr);
/** Is given token a range-declaration in a range-based for loop */
bool astIsRangeBasedForDecl(const Token* tok);

View File

@ -7029,6 +7029,7 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
}
}
//Is iterator fetching function invoked on container?
const bool isReturnIter = typestr == "iterator";
if (typestr.empty() || isReturnIter) {
if (Token::simpleMatch(tok->astOperand1(), ".") &&
@ -7037,7 +7038,7 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
tok->astOperand1()->astOperand1()->valueType() &&
tok->astOperand1()->astOperand1()->valueType()->container) {
const Library::Container *cont = tok->astOperand1()->astOperand1()->valueType()->container;
const std::map<std::string, Library::Container::Function>::const_iterator it = cont->functions.find(tok->astOperand1()->astOperand2()->str());
const auto it = cont->functions.find(tok->astOperand1()->astOperand2()->str());
if (it != cont->functions.end()) {
if (it->second.yield == Library::Container::Yield::START_ITERATOR ||
it->second.yield == Library::Container::Yield::END_ITERATOR ||
@ -7051,6 +7052,27 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
continue;
}
}
//Is iterator fetching function called?
} else if (Token::simpleMatch(tok->astOperand1(), "::") &&
tok->astOperand2() &&
tok->astOperand2()->isVariable()) {
const auto* const paramVariable = tok->astOperand2()->variable();
if (!paramVariable ||
!paramVariable->valueType() ||
!paramVariable->valueType()->container) {
continue;
}
const auto yield = astFunctionYield(tok->previous(), &mSettings);
if (yield == Library::Container::Yield::START_ITERATOR ||
yield == Library::Container::Yield::END_ITERATOR ||
yield == Library::Container::Yield::ITERATOR) {
ValueType vt;
vt.type = ValueType::Type::ITERATOR;
vt.container = paramVariable->valueType()->container;
vt.containerTypeToken = paramVariable->valueType()->containerTypeToken;
setValueType(tok, vt);
}
}
if (isReturnIter) {
const std::vector<const Token*> args = getArguments(tok);

View File

@ -8898,7 +8898,9 @@ static const std::set<std::string> stdFunctions = {
"set_symmetric_difference", "push_heap", "pop_heap", "make_heap", "sort_heap",
"min", "max", "min_element", "max_element", "lexicographical_compare", "next_permutation", "prev_permutation",
"advance", "back_inserter", "distance", "front_inserter", "inserter",
"make_pair", "make_shared", "make_tuple"
"make_pair", "make_shared", "make_tuple",
"begin", "cbegin", "rbegin", "crbegin",
"end", "cend", "rend", "crend"
};

View File

@ -663,7 +663,6 @@ static void setTokenValue(Token* tok,
}
}
}
else if (Token::Match(parent, ". %name% (") && parent->astParent() == parent->tokAt(2) &&
parent->astOperand1() && parent->astOperand1()->valueType()) {
const Library::Container* c = getLibraryContainer(parent->astOperand1());
@ -695,7 +694,6 @@ static void setTokenValue(Token* tok,
}
}
}
return;
}
@ -2203,7 +2201,7 @@ class SelectValueFromVarIdMapRange {
using pointer = value_type *;
using reference = value_type &;
explicit Iterator(const M::const_iterator &it)
explicit Iterator(const M::const_iterator & it)
: mIt(it) {}
reference operator*() const {
@ -4805,7 +4803,8 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase* /*db*/, Erro
// container lifetimes
else if (astIsContainer(tok)) {
Token * parent = astParentSkipParens(tok);
if (!Token::Match(parent, ". %name% ("))
if (!Token::Match(parent, ". %name% (") &&
!Token::simpleMatch(parent, "("))
continue;
ValueFlow::Value master;
@ -4815,8 +4814,12 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase* /*db*/, Erro
if (astIsIterator(parent->tokAt(2))) {
master.errorPath.emplace_back(parent->tokAt(2), "Iterator to container is created here.");
master.lifetimeKind = ValueFlow::Value::LifetimeKind::Iterator;
} else if ((astIsPointer(parent->tokAt(2)) && !isContainerOfPointers(tok->valueType()->containerTypeToken, settings)) ||
Token::Match(parent->next(), "data|c_str")) {
} else if (astIsIterator(parent)) {
master.errorPath.emplace_back(parent, "Iterator to container is created here.");
master.lifetimeKind = ValueFlow::Value::LifetimeKind::Iterator;
}
else if ((astIsPointer(parent->tokAt(2)) && !isContainerOfPointers(tok->valueType()->containerTypeToken, settings)) ||
Token::Match(parent->next(), "data|c_str")) {
master.errorPath.emplace_back(parent->tokAt(2), "Pointer to container is created here.");
master.lifetimeKind = ValueFlow::Value::LifetimeKind::Object;
} else {
@ -4853,7 +4856,10 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase* /*db*/, Erro
ValueFlow::Value value = master;
value.tokvalue = rt.token;
value.errorPath.insert(value.errorPath.begin(), rt.errors.cbegin(), rt.errors.cend());
setTokenValue(parent->tokAt(2), std::move(value), settings);
if (Token::simpleMatch(parent, "("))
setTokenValue(parent, value, settings);
else
setTokenValue(parent->tokAt(2), value, settings);
if (!rt.token->variable()) {
LifetimeStore ls = LifetimeStore{
@ -8100,6 +8106,19 @@ static void valueFlowSmartPointer(TokenList *tokenlist, ErrorLogger * errorLogge
}
}
static Library::Container::Yield findIteratorYield(Token* tok, const Token** ftok, const Settings *settings)
{
auto yield = astContainerYield(tok, ftok);
if (*ftok)
return yield;
if (!tok->astParent())
return yield;
//begin/end free functions
return astFunctionYield(tok->astParent()->previous(), settings, ftok);
}
static void valueFlowIterators(TokenList *tokenlist, const Settings *settings)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
@ -8110,7 +8129,7 @@ static void valueFlowIterators(TokenList *tokenlist, const Settings *settings)
if (!astIsContainer(tok))
continue;
const Token* ftok = nullptr;
const Library::Container::Yield yield = astContainerYield(tok, &ftok);
const Library::Container::Yield yield = findIteratorYield(tok, &ftok, settings);
if (ftok) {
ValueFlow::Value v(0);
v.setKnown();

View File

@ -4661,4 +4661,47 @@ void stdspan()
spn3.last<1>();
spn3.subspan<1, 1>();
#endif
}
void beginEnd()
{
std::vector<int> v;
//cppcheck-suppress ignoredReturnValue
std::begin(v);
//cppcheck-suppress ignoredReturnValue
std::rbegin(v);
//cppcheck-suppress ignoredReturnValue
std::cbegin(v);
//cppcheck-suppress ignoredReturnValue
std::crbegin(v);
//cppcheck-suppress ignoredReturnValue
std::end(v);
//cppcheck-suppress ignoredReturnValue
std::rend(v);
//cppcheck-suppress ignoredReturnValue
std::cend(v);
//cppcheck-suppress ignoredReturnValue
std::crend(v);
int arr[4];
//cppcheck-suppress ignoredReturnValue
std::begin(arr);
//cppcheck-suppress ignoredReturnValue
std::rbegin(arr);
//cppcheck-suppress ignoredReturnValue
std::cbegin(arr);
//cppcheck-suppress ignoredReturnValue
std::crbegin(arr);
//cppcheck-suppress ignoredReturnValue
std::end(arr);
//cppcheck-suppress ignoredReturnValue
std::rend(arr);
//cppcheck-suppress ignoredReturnValue
std::cend(arr);
//cppcheck-suppress ignoredReturnValue
std::crend(arr);
}

View File

@ -2281,6 +2281,13 @@ private:
"}");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout.str());
check("auto f() {\n"
" std::vector<int> x;\n"
" auto it = std::begin(x);\n"
" return it;\n"
"}");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout.str());
check("auto f() {\n"
" std::vector<int> x;\n"
" auto p = x.data();\n"

View File

@ -1885,7 +1885,6 @@ private:
ASSERT_EQUALS("", errout.str());
check("std::vector<int>& f();\n"
"std::vector<int>& g();\n"
"void foo() {\n"
" auto it = f().end() - 1;\n"
" f().begin() - it;\n"
@ -1907,18 +1906,28 @@ private:
" (void)std::find(begin(f()), end(f()) - 1, 0);\n"
" (void)std::find(begin(f()) + 1, end(f()) - 1, 0);\n"
"}");
ASSERT_EQUALS("[test.cpp:10]: (error) Dereference of an invalid iterator: f().end()+1\n", errout.str());
ASSERT_EQUALS("[test.cpp:9]: (error) Dereference of an invalid iterator: f().end()+1\n", errout.str());
check("std::vector<int>& f();\n"
"std::vector<int>& g();\n"
"void foo() {\n"
" if(f().begin() == f().end()) {}\n"
" if(f().begin() == f().end()+1) {}\n"
" if(f().begin()+1 == f().end()) {}\n"
" if(f().begin()+1 == f().end()+1) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Dereference of an invalid iterator: f().end()+1\n"
"[test.cpp:7]: (error) Dereference of an invalid iterator: f().end()+1\n",
ASSERT_EQUALS("[test.cpp:4]: (error) Dereference of an invalid iterator: f().end()+1\n"
"[test.cpp:6]: (error) Dereference of an invalid iterator: f().end()+1\n",
errout.str());
check("std::vector<int>& f();\n"
"void foo() {\n"
" if(std::begin(f()) == std::end(f())) {}\n"
" if(std::begin(f()) == std::end(f())+1) {}\n"
" if(std::begin(f())+1 == std::end(f())) {}\n"
" if(std::begin(f())+1 == std::end(f())+1) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Dereference of an invalid iterator: std::end(f())+1\n"
"[test.cpp:6]: (error) Dereference of an invalid iterator: std::end(f())+1\n",
errout.str());
check("template<int N>\n"
@ -4496,6 +4505,13 @@ private:
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (error) Dereference of an invalid iterator: i\n", errout.str());
check("void f() {\n"
" std::vector <int> v;\n"
" std::vector <int>::iterator i = std::end(v);\n"
" *i=0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (error) Dereference of an invalid iterator: i\n", errout.str());
check("void f(std::vector <int> v) {\n"
" std::vector <int>::iterator i = v.end();\n"
" *i=0;\n"
@ -4520,6 +4536,12 @@ private:
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (error) Dereference of an invalid iterator: i-1\n", errout.str());
check("void f(std::vector <int> v) {\n"
" std::vector <int>::iterator i = std::begin(v);\n"
" *(i-1)=0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (error) Dereference of an invalid iterator: i-1\n", errout.str());
check("void f(std::vector <int> v, bool b) {\n"
" std::vector <int>::iterator i = v.begin();\n"
" if (b)\n"