Skip to content

Commit 2cacb13

Browse files
Fix #10671: functionConst FN with begin/end and const_iterator (danmar#3749)
Check if the iterator is assigned to a const_iterator or const_revese_iterator, in which case it is possible the function can be const. Unfortunately, it is not possible to remove the hard coding of cbegin, cend, crbegin and crend due to the need to handle auto, as in the following code snippet: void cbegin_auto(void) { for (auto it = m_str.cbegin(); it != m_str.cend(); ++it) {;} }
1 parent dad64bf commit 2cacb13

File tree

3 files changed

+69
-2
lines changed

3 files changed

+69
-2
lines changed

lib/checkclass.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2205,8 +2205,13 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
22052205
const Variable *var = lastVarTok->variable();
22062206
if (!var)
22072207
return false;
2208-
if (var->isStlType() // assume all std::*::size() and std::*::empty() are const
2209-
&& (Token::Match(end, "size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|get_allocator|c_str|str ( )") || Token::Match(end, "rfind|copy")))
2208+
if ((var->isStlType() // assume all std::*::size() and std::*::empty() are const
2209+
&& (Token::Match(end, "size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|get_allocator|c_str|str ( )") || Token::Match(end, "rfind|copy"))) ||
2210+
2211+
(lastVarTok->valueType() && lastVarTok->valueType()->container &&
2212+
((lastVarTok->valueType()->container->getYield(end->str()) == Library::Container::Yield::START_ITERATOR) ||
2213+
(lastVarTok->valueType()->container->getYield(end->str()) == Library::Container::Yield::END_ITERATOR))
2214+
&& (tok1->previous()->isComparisonOp() || (tok1->previous()->isAssignmentOp() && Token::Match(tok1->tokAt(-2)->variable()->typeEndToken(), "const_iterator|const_reverse_iterator")))))
22102215
;
22112216
else if (!var->typeScope() || !isConstMemberFunc(var->typeScope(), end))
22122217
return false;

test/cfg/std.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3636,3 +3636,46 @@ void stdbind()
36363636
// cppcheck-suppress unreadVariable
36373637
auto f2 = std::bind(stdbind_helper, 10);
36383638
}
3639+
3640+
class A
3641+
{
3642+
std::vector<std::string> m_str;
3643+
3644+
public:
3645+
3646+
A() {}
3647+
3648+
// cppcheck-suppress functionConst
3649+
void begin_const_iterator(void)
3650+
{
3651+
for (std::vector<std::string>::const_iterator it = m_str.begin(); it != m_str.end(); ++it) {;}
3652+
}
3653+
// cppcheck-suppress functionConst
3654+
void cbegin_const_iterator(void)
3655+
{
3656+
for (std::vector<std::string>::const_iterator it = m_str.cbegin(); it != m_str.cend(); ++it) {;}
3657+
}
3658+
// cppcheck-suppress functionConst
3659+
void crbegin_const_iterator(void)
3660+
{
3661+
for (std::vector<std::string>::const_reverse_iterator it = m_str.crbegin(); it != m_str.crend(); ++it) {;}
3662+
}
3663+
// cppcheck-suppress functionConst
3664+
void rbegin_const_iterator(void)
3665+
{
3666+
for (std::vector<std::string>::const_reverse_iterator it = m_str.rbegin(); it != m_str.rend(); ++it) {;}
3667+
}
3668+
// cppcheck-suppress functionConst
3669+
void cbegin_auto(void)
3670+
{
3671+
for (auto it = m_str.cbegin(); it != m_str.cend(); ++it) {;}
3672+
}
3673+
void baz_begin_no_const_iterator(void)
3674+
{
3675+
for (std::vector<std::string>::iterator it = m_str.begin(); it != m_str.end(); ++it) {;}
3676+
}
3677+
void rbegin_no_const_iterator(void)
3678+
{
3679+
for (std::vector<std::string>::reverse_iterator it = m_str.rbegin(); it != m_str.rend(); ++it) {;}
3680+
}
3681+
};

test/testclass.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ class TestClass : public TestFixture {
5454
" <dealloc>free</dealloc>\n"
5555
" </memory>\n"
5656
" <smart-pointer class-name=\"std::shared_ptr\"/>\n"
57+
" <container id=\"stdVector\" startPattern=\"std :: vector &lt;\" itEndPattern=\"&gt; :: const_iterator\">\n"
58+
" <access>\n"
59+
" <function name=\"begin\" yields=\"start-iterator\"/>\n"
60+
" <function name=\"end\" yields=\"end-iterator\"/>\n"
61+
" </access>\n"
62+
" </container>\n"
5763
"</def>";
5864
tinyxml2::XMLDocument doc;
5965
doc.Parse(xmldata, sizeof(xmldata));
@@ -184,6 +190,7 @@ class TestClass : public TestFixture {
184190
TEST_CASE(const71); // ticket #10146
185191
TEST_CASE(const72); // ticket #10520
186192
TEST_CASE(const73); // ticket #10735
193+
TEST_CASE(const74); // ticket #10671
187194
TEST_CASE(const_handleDefaultParameters);
188195
TEST_CASE(const_passThisToMemberOfOtherClass);
189196
TEST_CASE(assigningPointerToPointerIsNotAConstOperation);
@@ -5919,6 +5926,18 @@ class TestClass : public TestFixture {
59195926
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'S::f' can be const.\n", errout.str());
59205927
}
59215928

5929+
void const74() { // #10671
5930+
checkConst("class A {\n"
5931+
" std::vector<std::string> m_str;\n"
5932+
"public:\n"
5933+
" A() {}\n"
5934+
" void bar(void) {\n"
5935+
" for(std::vector<std::string>::const_iterator it = m_str.begin(); it != m_str.end(); ++it) {;}\n"
5936+
" }\n"
5937+
"};");
5938+
ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'A::bar' can be const.\n", errout.str());
5939+
}
5940+
59225941
void const_handleDefaultParameters() {
59235942
checkConst("struct Foo {\n"
59245943
" void foo1(int i, int j = 0) {\n"

0 commit comments

Comments
 (0)