Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/checkstl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,11 @@ static ValueFlow::Value getLifetimeIteratorValue(const Token* tok, MathLib::bigi
}
if (values.size() == 1)
return values.front();
it = std::find_if(values.cbegin(), values.cend(), [](const ValueFlow::Value& v) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is problematic if there is divergent lifetimes, that is why we check the size is 1 or no duplicates for iterator kinds. Why is there multiple lifetimes? Do we have a lifetime for t.s.a and a both?

Copy link
Collaborator Author

@chrchr-github chrchr-github Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is problematic if there is divergent lifetimes, that is why we check the size is 1 or no duplicates for iterator kinds. Why is there multiple lifetimes? Do we have a lifetime for t.s.a and a both?

In the s.a example, the lifetime tokvalues are s and .. But I think it makes sense to choose the dot, unless there can be multiple dots.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its the . in s.a I assume? Ideally we should remove the lifetimes that refer to a sub expression of another lifetime from the vector. Something like this function could be called here(its not tested though):

std::vector<ValueFlow::Value> pruneLifetimes(std::vector<ValueFlow::Value> lifetimes)
{
    std::vector<ValueFlow::Value> result;
    auto start = lifetimes.begin();
    while(start != lifetimes.end())
    {
        const Token* tok1 = start->tokvalue;
        auto it = std::partition(start, lifetimes.end(), [&](const ValueFlow::Value& v) { 
            const Token* tok2 = v.tokvalue;
            return astHasToken(tok1, tok2) or astHasToken(tok2, tok1); 
        });
        auto root = std::min_element(start, it, [](const ValueFlow::Value& x, const ValueFlow::Value& y) {
            return astHasToken(x.tokvalue, y.tokvalue);
        });
        result.push_back(*root);
        start = it;
    }
    return result;
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not entirely sure what it does, but it seems to work 👍

Copy link
Contributor

@pfultz2 pfultz2 Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First it groups the lifetimes together using std::partition with the lifetimes that refer to the same token or token of a subexpression. Then it finds the lifetime in that group that refers to the "highest" parent using std::min_element and adds that to the vector. Hopefully that makes sense.

return v.tokvalue->str() == ".";
});
if (it != values.cend())
return *it;
return ValueFlow::Value{};
}

Expand Down
22 changes: 22 additions & 0 deletions test/teststl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class TestStl : public TestFixture {
TEST_CASE(iterator28); // #10450
TEST_CASE(iterator29);
TEST_CASE(iterator30);
TEST_CASE(iterator31);
TEST_CASE(iteratorExpression);
TEST_CASE(iteratorSameExpression);
TEST_CASE(mismatchingContainerIterator);
Expand Down Expand Up @@ -1900,6 +1901,27 @@ class TestStl : public TestFixture {
ASSERT_EQUALS("", errout_str());
}

void iterator31()
{
check("struct S {\n" // #13327
" std::string a;\n"
"};\n"
"struct T {\n"
" S s;\n"
"};\n"
"bool f(const S& s) {\n"
" std::string b;\n"
" return s.a.c_str() == b.c_str();\n"
"}\n"
"bool g(const T& t) {\n"
" std::string b;\n"
" return t.s.a.c_str() == b.c_str();\n"
"}\n");
ASSERT_EQUALS("[test.cpp:9]: (error) Iterators of different containers 's.a' and 'b' are used together.\n"
"[test.cpp:13]: (error) Iterators of different containers 't.s.a' and 'b' are used together.\n",
errout_str());
}

void iteratorExpression() {
check("std::vector<int>& f();\n"
"std::vector<int>& g();\n"
Expand Down
Loading