Skip to content

Commit 2d5cabe

Browse files
authored
Add std::*begin and std::*end cfg (#4796)
1 parent 9351edd commit 2d5cabe

File tree

9 files changed

+163
-15
lines changed

9 files changed

+163
-15
lines changed

cfg/std.cfg

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8539,6 +8539,22 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
85398539
<valid>0:</valid>
85408540
</arg>
85418541
</function>
8542+
<function name="std::begin,std::cbegin,std::rbegin,std::crbegin">
8543+
<use-retval/>
8544+
<leak-ignore/>
8545+
<noreturn>false</noreturn>
8546+
<arg nr="1" direction="in"/>
8547+
<container yields="start-iterator"/>
8548+
<returnValue type="iterator" container="1"/>
8549+
</function>
8550+
<function name="std::end,std::cend,std::rend,std::crend">
8551+
<use-retval/>
8552+
<leak-ignore/>
8553+
<noreturn>false</noreturn>
8554+
<arg nr="1" direction="in"/>
8555+
<container yields="end-iterator"/>
8556+
<returnValue type="iterator" container="1"/>
8557+
</function>
85428558
<memory>
85438559
<alloc init="false" buffer-size="malloc">malloc</alloc>
85448560
<alloc init="true" buffer-size="calloc">calloc</alloc>

lib/astutils.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ Library::Container::Action astContainerAction(const Token* tok, const Token** ft
282282
return Library::Container::Action::NO_ACTION;
283283
return tok->valueType()->container->getAction(ftok2->str());
284284
}
285+
285286
Library::Container::Yield astContainerYield(const Token* tok, const Token** ftok)
286287
{
287288
const Token* ftok2 = getContainerFunction(tok);
@@ -292,6 +293,20 @@ Library::Container::Yield astContainerYield(const Token* tok, const Token** ftok
292293
return tok->valueType()->container->getYield(ftok2->str());
293294
}
294295

296+
Library::Container::Yield astFunctionYield(const Token* tok, const Settings* settings, const Token** ftok)
297+
{
298+
if (!tok)
299+
return Library::Container::Yield::NO_YIELD;
300+
301+
const auto* function = settings->library.getFunction(tok);
302+
if (!function)
303+
return Library::Container::Yield::NO_YIELD;
304+
305+
if (ftok)
306+
*ftok = tok;
307+
return function->containerYield;
308+
}
309+
295310
bool astIsRangeBasedForDecl(const Token* tok)
296311
{
297312
return Token::simpleMatch(tok->astParent(), ":") && Token::simpleMatch(tok->astParent()->astParent(), "(");

lib/astutils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ bool astIsContainerOwned(const Token* tok);
153153
Library::Container::Action astContainerAction(const Token* tok, const Token** ftok = nullptr);
154154
Library::Container::Yield astContainerYield(const Token* tok, const Token** ftok = nullptr);
155155

156+
Library::Container::Yield astFunctionYield(const Token* tok, const Settings* settings, const Token** ftok = nullptr);
157+
156158
/** Is given token a range-declaration in a range-based for loop */
157159
bool astIsRangeBasedForDecl(const Token* tok);
158160

lib/symboldatabase.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7029,6 +7029,7 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
70297029
}
70307030
}
70317031

7032+
//Is iterator fetching function invoked on container?
70327033
const bool isReturnIter = typestr == "iterator";
70337034
if (typestr.empty() || isReturnIter) {
70347035
if (Token::simpleMatch(tok->astOperand1(), ".") &&
@@ -7037,7 +7038,7 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
70377038
tok->astOperand1()->astOperand1()->valueType() &&
70387039
tok->astOperand1()->astOperand1()->valueType()->container) {
70397040
const Library::Container *cont = tok->astOperand1()->astOperand1()->valueType()->container;
7040-
const std::map<std::string, Library::Container::Function>::const_iterator it = cont->functions.find(tok->astOperand1()->astOperand2()->str());
7041+
const auto it = cont->functions.find(tok->astOperand1()->astOperand2()->str());
70417042
if (it != cont->functions.end()) {
70427043
if (it->second.yield == Library::Container::Yield::START_ITERATOR ||
70437044
it->second.yield == Library::Container::Yield::END_ITERATOR ||
@@ -7051,6 +7052,27 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
70517052
continue;
70527053
}
70537054
}
7055+
//Is iterator fetching function called?
7056+
} else if (Token::simpleMatch(tok->astOperand1(), "::") &&
7057+
tok->astOperand2() &&
7058+
tok->astOperand2()->isVariable()) {
7059+
const auto* const paramVariable = tok->astOperand2()->variable();
7060+
if (!paramVariable ||
7061+
!paramVariable->valueType() ||
7062+
!paramVariable->valueType()->container) {
7063+
continue;
7064+
}
7065+
7066+
const auto yield = astFunctionYield(tok->previous(), &mSettings);
7067+
if (yield == Library::Container::Yield::START_ITERATOR ||
7068+
yield == Library::Container::Yield::END_ITERATOR ||
7069+
yield == Library::Container::Yield::ITERATOR) {
7070+
ValueType vt;
7071+
vt.type = ValueType::Type::ITERATOR;
7072+
vt.container = paramVariable->valueType()->container;
7073+
vt.containerTypeToken = paramVariable->valueType()->containerTypeToken;
7074+
setValueType(tok, vt);
7075+
}
70547076
}
70557077
if (isReturnIter) {
70567078
const std::vector<const Token*> args = getArguments(tok);

lib/tokenize.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8898,7 +8898,9 @@ static const std::set<std::string> stdFunctions = {
88988898
"set_symmetric_difference", "push_heap", "pop_heap", "make_heap", "sort_heap",
88998899
"min", "max", "min_element", "max_element", "lexicographical_compare", "next_permutation", "prev_permutation",
89008900
"advance", "back_inserter", "distance", "front_inserter", "inserter",
8901-
"make_pair", "make_shared", "make_tuple"
8901+
"make_pair", "make_shared", "make_tuple",
8902+
"begin", "cbegin", "rbegin", "crbegin",
8903+
"end", "cend", "rend", "crend"
89028904
};
89038905

89048906

lib/valueflow.cpp

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,6 @@ static void setTokenValue(Token* tok,
663663
}
664664
}
665665
}
666-
667666
else if (Token::Match(parent, ". %name% (") && parent->astParent() == parent->tokAt(2) &&
668667
parent->astOperand1() && parent->astOperand1()->valueType()) {
669668
const Library::Container* c = getLibraryContainer(parent->astOperand1());
@@ -695,7 +694,6 @@ static void setTokenValue(Token* tok,
695694
}
696695
}
697696
}
698-
699697
return;
700698
}
701699

@@ -2203,7 +2201,7 @@ class SelectValueFromVarIdMapRange {
22032201
using pointer = value_type *;
22042202
using reference = value_type &;
22052203

2206-
explicit Iterator(const M::const_iterator &it)
2204+
explicit Iterator(const M::const_iterator & it)
22072205
: mIt(it) {}
22082206

22092207
reference operator*() const {
@@ -4805,7 +4803,8 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase* /*db*/, Erro
48054803
// container lifetimes
48064804
else if (astIsContainer(tok)) {
48074805
Token * parent = astParentSkipParens(tok);
4808-
if (!Token::Match(parent, ". %name% ("))
4806+
if (!Token::Match(parent, ". %name% (") &&
4807+
!Token::simpleMatch(parent, "("))
48094808
continue;
48104809

48114810
ValueFlow::Value master;
@@ -4815,8 +4814,12 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase* /*db*/, Erro
48154814
if (astIsIterator(parent->tokAt(2))) {
48164815
master.errorPath.emplace_back(parent->tokAt(2), "Iterator to container is created here.");
48174816
master.lifetimeKind = ValueFlow::Value::LifetimeKind::Iterator;
4818-
} else if ((astIsPointer(parent->tokAt(2)) && !isContainerOfPointers(tok->valueType()->containerTypeToken, settings)) ||
4819-
Token::Match(parent->next(), "data|c_str")) {
4817+
} else if (astIsIterator(parent)) {
4818+
master.errorPath.emplace_back(parent, "Iterator to container is created here.");
4819+
master.lifetimeKind = ValueFlow::Value::LifetimeKind::Iterator;
4820+
}
4821+
else if ((astIsPointer(parent->tokAt(2)) && !isContainerOfPointers(tok->valueType()->containerTypeToken, settings)) ||
4822+
Token::Match(parent->next(), "data|c_str")) {
48204823
master.errorPath.emplace_back(parent->tokAt(2), "Pointer to container is created here.");
48214824
master.lifetimeKind = ValueFlow::Value::LifetimeKind::Object;
48224825
} else {
@@ -4853,7 +4856,10 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase* /*db*/, Erro
48534856
ValueFlow::Value value = master;
48544857
value.tokvalue = rt.token;
48554858
value.errorPath.insert(value.errorPath.begin(), rt.errors.cbegin(), rt.errors.cend());
4856-
setTokenValue(parent->tokAt(2), std::move(value), settings);
4859+
if (Token::simpleMatch(parent, "("))
4860+
setTokenValue(parent, value, settings);
4861+
else
4862+
setTokenValue(parent->tokAt(2), value, settings);
48574863

48584864
if (!rt.token->variable()) {
48594865
LifetimeStore ls = LifetimeStore{
@@ -8100,6 +8106,19 @@ static void valueFlowSmartPointer(TokenList *tokenlist, ErrorLogger * errorLogge
81008106
}
81018107
}
81028108

8109+
static Library::Container::Yield findIteratorYield(Token* tok, const Token** ftok, const Settings *settings)
8110+
{
8111+
auto yield = astContainerYield(tok, ftok);
8112+
if (*ftok)
8113+
return yield;
8114+
8115+
if (!tok->astParent())
8116+
return yield;
8117+
8118+
//begin/end free functions
8119+
return astFunctionYield(tok->astParent()->previous(), settings, ftok);
8120+
}
8121+
81038122
static void valueFlowIterators(TokenList *tokenlist, const Settings *settings)
81048123
{
81058124
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
@@ -8110,7 +8129,7 @@ static void valueFlowIterators(TokenList *tokenlist, const Settings *settings)
81108129
if (!astIsContainer(tok))
81118130
continue;
81128131
const Token* ftok = nullptr;
8113-
const Library::Container::Yield yield = astContainerYield(tok, &ftok);
8132+
const Library::Container::Yield yield = findIteratorYield(tok, &ftok, settings);
81148133
if (ftok) {
81158134
ValueFlow::Value v(0);
81168135
v.setKnown();

test/cfg/std.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4661,4 +4661,47 @@ void stdspan()
46614661
spn3.last<1>();
46624662
spn3.subspan<1, 1>();
46634663
#endif
4664+
}
4665+
4666+
void beginEnd()
4667+
{
4668+
std::vector<int> v;
4669+
4670+
//cppcheck-suppress ignoredReturnValue
4671+
std::begin(v);
4672+
//cppcheck-suppress ignoredReturnValue
4673+
std::rbegin(v);
4674+
//cppcheck-suppress ignoredReturnValue
4675+
std::cbegin(v);
4676+
//cppcheck-suppress ignoredReturnValue
4677+
std::crbegin(v);
4678+
4679+
//cppcheck-suppress ignoredReturnValue
4680+
std::end(v);
4681+
//cppcheck-suppress ignoredReturnValue
4682+
std::rend(v);
4683+
//cppcheck-suppress ignoredReturnValue
4684+
std::cend(v);
4685+
//cppcheck-suppress ignoredReturnValue
4686+
std::crend(v);
4687+
4688+
int arr[4];
4689+
4690+
//cppcheck-suppress ignoredReturnValue
4691+
std::begin(arr);
4692+
//cppcheck-suppress ignoredReturnValue
4693+
std::rbegin(arr);
4694+
//cppcheck-suppress ignoredReturnValue
4695+
std::cbegin(arr);
4696+
//cppcheck-suppress ignoredReturnValue
4697+
std::crbegin(arr);
4698+
4699+
//cppcheck-suppress ignoredReturnValue
4700+
std::end(arr);
4701+
//cppcheck-suppress ignoredReturnValue
4702+
std::rend(arr);
4703+
//cppcheck-suppress ignoredReturnValue
4704+
std::cend(arr);
4705+
//cppcheck-suppress ignoredReturnValue
4706+
std::crend(arr);
46644707
}

test/testautovariables.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2281,6 +2281,13 @@ class TestAutoVariables : public TestFixture {
22812281
"}");
22822282
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());
22832283

2284+
check("auto f() {\n"
2285+
" std::vector<int> x;\n"
2286+
" auto it = std::begin(x);\n"
2287+
" return it;\n"
2288+
"}");
2289+
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());
2290+
22842291
check("auto f() {\n"
22852292
" std::vector<int> x;\n"
22862293
" auto p = x.data();\n"

test/teststl.cpp

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1885,7 +1885,6 @@ class TestStl : public TestFixture {
18851885
ASSERT_EQUALS("", errout.str());
18861886

18871887
check("std::vector<int>& f();\n"
1888-
"std::vector<int>& g();\n"
18891888
"void foo() {\n"
18901889
" auto it = f().end() - 1;\n"
18911890
" f().begin() - it;\n"
@@ -1907,18 +1906,28 @@ class TestStl : public TestFixture {
19071906
" (void)std::find(begin(f()), end(f()) - 1, 0);\n"
19081907
" (void)std::find(begin(f()) + 1, end(f()) - 1, 0);\n"
19091908
"}");
1910-
ASSERT_EQUALS("[test.cpp:10]: (error) Dereference of an invalid iterator: f().end()+1\n", errout.str());
1909+
ASSERT_EQUALS("[test.cpp:9]: (error) Dereference of an invalid iterator: f().end()+1\n", errout.str());
19111910

19121911
check("std::vector<int>& f();\n"
1913-
"std::vector<int>& g();\n"
19141912
"void foo() {\n"
19151913
" if(f().begin() == f().end()) {}\n"
19161914
" if(f().begin() == f().end()+1) {}\n"
19171915
" if(f().begin()+1 == f().end()) {}\n"
19181916
" if(f().begin()+1 == f().end()+1) {}\n"
19191917
"}");
1920-
ASSERT_EQUALS("[test.cpp:5]: (error) Dereference of an invalid iterator: f().end()+1\n"
1921-
"[test.cpp:7]: (error) Dereference of an invalid iterator: f().end()+1\n",
1918+
ASSERT_EQUALS("[test.cpp:4]: (error) Dereference of an invalid iterator: f().end()+1\n"
1919+
"[test.cpp:6]: (error) Dereference of an invalid iterator: f().end()+1\n",
1920+
errout.str());
1921+
1922+
check("std::vector<int>& f();\n"
1923+
"void foo() {\n"
1924+
" if(std::begin(f()) == std::end(f())) {}\n"
1925+
" if(std::begin(f()) == std::end(f())+1) {}\n"
1926+
" if(std::begin(f())+1 == std::end(f())) {}\n"
1927+
" if(std::begin(f())+1 == std::end(f())+1) {}\n"
1928+
"}");
1929+
ASSERT_EQUALS("[test.cpp:4]: (error) Dereference of an invalid iterator: std::end(f())+1\n"
1930+
"[test.cpp:6]: (error) Dereference of an invalid iterator: std::end(f())+1\n",
19221931
errout.str());
19231932

19241933
check("template<int N>\n"
@@ -4496,6 +4505,13 @@ class TestStl : public TestFixture {
44964505
"}\n");
44974506
ASSERT_EQUALS("[test.cpp:4]: (error) Dereference of an invalid iterator: i\n", errout.str());
44984507

4508+
check("void f() {\n"
4509+
" std::vector <int> v;\n"
4510+
" std::vector <int>::iterator i = std::end(v);\n"
4511+
" *i=0;\n"
4512+
"}\n");
4513+
ASSERT_EQUALS("[test.cpp:4]: (error) Dereference of an invalid iterator: i\n", errout.str());
4514+
44994515
check("void f(std::vector <int> v) {\n"
45004516
" std::vector <int>::iterator i = v.end();\n"
45014517
" *i=0;\n"
@@ -4520,6 +4536,12 @@ class TestStl : public TestFixture {
45204536
"}\n");
45214537
ASSERT_EQUALS("[test.cpp:3]: (error) Dereference of an invalid iterator: i-1\n", errout.str());
45224538

4539+
check("void f(std::vector <int> v) {\n"
4540+
" std::vector <int>::iterator i = std::begin(v);\n"
4541+
" *(i-1)=0;\n"
4542+
"}\n");
4543+
ASSERT_EQUALS("[test.cpp:3]: (error) Dereference of an invalid iterator: i-1\n", errout.str());
4544+
45234545
check("void f(std::vector <int> v, bool b) {\n"
45244546
" std::vector <int>::iterator i = v.begin();\n"
45254547
" if (b)\n"

0 commit comments

Comments
 (0)