diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 3c404b5d2..942f6c020 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2205,8 +2205,13 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool& const Variable *var = lastVarTok->variable(); if (!var) return false; - if (var->isStlType() // assume all std::*::size() and std::*::empty() are const - && (Token::Match(end, "size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|get_allocator|c_str|str ( )") || Token::Match(end, "rfind|copy"))) + if ((var->isStlType() // assume all std::*::size() and std::*::empty() are const + && (Token::Match(end, "size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|get_allocator|c_str|str ( )") || Token::Match(end, "rfind|copy"))) || + + (lastVarTok->valueType() && lastVarTok->valueType()->container && + ((lastVarTok->valueType()->container->getYield(end->str()) == Library::Container::Yield::START_ITERATOR) || + (lastVarTok->valueType()->container->getYield(end->str()) == Library::Container::Yield::END_ITERATOR)) + && (tok1->previous()->isComparisonOp() || (tok1->previous()->isAssignmentOp() && Token::Match(tok1->tokAt(-2)->variable()->typeEndToken(), "const_iterator|const_reverse_iterator"))))) ; else if (!var->typeScope() || !isConstMemberFunc(var->typeScope(), end)) return false; diff --git a/test/cfg/std.cpp b/test/cfg/std.cpp index 3e06bd92a..fc15910ea 100644 --- a/test/cfg/std.cpp +++ b/test/cfg/std.cpp @@ -3636,3 +3636,46 @@ void stdbind() // cppcheck-suppress unreadVariable auto f2 = std::bind(stdbind_helper, 10); } + +class A +{ + std::vector m_str; + +public: + + A() {} + + // cppcheck-suppress functionConst + void begin_const_iterator(void) + { + for (std::vector::const_iterator it = m_str.begin(); it != m_str.end(); ++it) {;} + } + // cppcheck-suppress functionConst + void cbegin_const_iterator(void) + { + for (std::vector::const_iterator it = m_str.cbegin(); it != m_str.cend(); ++it) {;} + } + // cppcheck-suppress functionConst + void crbegin_const_iterator(void) + { + for (std::vector::const_reverse_iterator it = m_str.crbegin(); it != m_str.crend(); ++it) {;} + } + // cppcheck-suppress functionConst + void rbegin_const_iterator(void) + { + for (std::vector::const_reverse_iterator it = m_str.rbegin(); it != m_str.rend(); ++it) {;} + } + // cppcheck-suppress functionConst + void cbegin_auto(void) + { + for (auto it = m_str.cbegin(); it != m_str.cend(); ++it) {;} + } + void baz_begin_no_const_iterator(void) + { + for (std::vector::iterator it = m_str.begin(); it != m_str.end(); ++it) {;} + } + void rbegin_no_const_iterator(void) + { + for (std::vector::reverse_iterator it = m_str.rbegin(); it != m_str.rend(); ++it) {;} + } +}; diff --git a/test/testclass.cpp b/test/testclass.cpp index 66b0ed4b5..3f0aef3d0 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -54,6 +54,12 @@ private: " free\n" " \n" " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); @@ -184,6 +190,7 @@ private: TEST_CASE(const71); // ticket #10146 TEST_CASE(const72); // ticket #10520 TEST_CASE(const73); // ticket #10735 + TEST_CASE(const74); // ticket #10671 TEST_CASE(const_handleDefaultParameters); TEST_CASE(const_passThisToMemberOfOtherClass); TEST_CASE(assigningPointerToPointerIsNotAConstOperation); @@ -5919,6 +5926,18 @@ private: ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'S::f' can be const.\n", errout.str()); } + void const74() { // #10671 + checkConst("class A {\n" + " std::vector m_str;\n" + "public:\n" + " A() {}\n" + " void bar(void) {\n" + " for(std::vector::const_iterator it = m_str.begin(); it != m_str.end(); ++it) {;}\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'A::bar' can be const.\n", errout.str()); + } + void const_handleDefaultParameters() { checkConst("struct Foo {\n" " void foo1(int i, int j = 0) {\n"