Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
29 changes: 18 additions & 11 deletions lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8687,7 +8687,7 @@ namespace {
}

template<class F>
static Result accumulateStructMembers(const Scope* scope, F f, ValueType::Accuracy accuracy, const Settings& settings)
static Result accumulateStructMembers(const Scope* scope, F f, ValueType::Accuracy accuracy, const Settings& settings, bool isAnonUnion = false)
{
size_t total = 0;
std::set<const Scope*> anonScopes;
Expand All @@ -8701,21 +8701,27 @@ static Result accumulateStructMembers(const Scope* scope, F f, ValueType::Accura
const MathLib::bigint dim = std::accumulate(var.dimensions().cbegin(), var.dimensions().cend(), MathLib::bigint(1), [](MathLib::bigint i1, const Dimension& dim) {
return i1 * dim.num;
});
if (var.nameToken()->scope() != scope && var.nameToken()->scope()->definedType) { // anonymous union
const auto ret = anonScopes.insert(var.nameToken()->scope());
if (ret.second)
total = f(total, *vt, dim, bits);
if (var.nameToken()->scope()->type == ScopeType::eUnion && var.nameToken()->scope() != scope && var.nameToken()->scope()->definedType) { // anonymous union
anonScopes.insert(var.nameToken()->scope());
continue;
}
else if (vt->container && vt->container->startPattern == "std :: array <") {
if (vt->container && vt->container->startPattern == "std :: array <") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AI review

Removing the anonScopes handling means every member of an anonymous union is now passed to f. getAlignOf is fine with this because it takes the max. In getSizeOf, though, these members are now added together unless the new isUnion heuristic matches, and it doesn't match plain-typed members. That causes the regressions described on line 8837.

const ValueType vtElement = ValueType::parseDecl(vt->containerTypeToken, settings);
total = f(total, vtElement, dim, bits);
total = f(total, vtElement, dim, bits, /*isAnonUnion*/ false);
}
else
total = f(total, *vt, dim, bits);
total = f(total, *vt, dim, bits, isAnonUnion);
}
if (accuracy == ValueType::Accuracy::ExactOrZero && total == 0 && bits == -1)
return {0, false};
}
for (const Scope* aUnion : anonScopes) {
const Result res = accumulateStructMembers(aUnion, f, accuracy, settings, true);
if (res.success)
total += res.total;
else if (accuracy == ValueType::Accuracy::ExactOrZero)
return {0, false};
}
return {total, true};
}

Expand Down Expand Up @@ -8745,7 +8751,7 @@ static size_t getAlignOf(const ValueType& vt, const Settings& settings, ValueTyp
return align == 0 ? 0 : bitCeil(align);
}
if (vt.type == ValueType::Type::RECORD && vt.typeScope) {
auto accHelper = [&](size_t max, const ValueType& vt2, size_t /*dim*/, MathLib::bigint /*bits*/) {
auto accHelper = [&](size_t max, const ValueType& vt2, size_t /*dim*/, MathLib::bigint /*bits*/, bool /*isAnonUnion*/) {
size_t a = getAlignOf(vt2, settings, accuracy, ValueType::SizeOf::Pointer, ++maxRecursion);
return std::max(max, a);
};
Expand Down Expand Up @@ -8797,7 +8803,7 @@ size_t ValueType::getSizeOf( const Settings& settings, Accuracy accuracy, SizeOf
if (type == ValueType::Type::RECORD && typeScope) {
size_t currentBitCount = 0;
size_t currentBitfieldAlloc = 0;
auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, MathLib::bigint nBits) -> size_t {
auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, MathLib::bigint nBits, bool isAnonUnion) -> size_t {
const size_t charBit = settings.platform.char_bit;
size_t n = vt2.getSizeOf(settings, accuracy, SizeOf::Pointer, ++maxRecursion);
size_t a = getAlignOf(vt2, settings, accuracy, SizeOf::Pointer);
Expand Down Expand Up @@ -8840,7 +8846,8 @@ size_t ValueType::getSizeOf( const Settings& settings, Accuracy accuracy, SizeOf
currentBitfieldAlloc = 0;
currentBitCount = 0;
}
return typeScope->type == ScopeType::eUnion ? std::max(total, n) : total + padding + n;
const bool isUnion = isAnonUnion || typeScope->type == ScopeType::eUnion;
return isUnion ? std::max(total, n) : total + padding + n;
};
Result result = accumulateStructMembers(typeScope, accHelper, accuracy, settings);
size_t total = result.total;
Expand Down
11 changes: 11 additions & 0 deletions test/testother.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2787,6 +2787,17 @@ class TestOther : public TestFixture {
"}\n");
ASSERT_EQUALS("", errout_str());

check("struct S {\n" // #15130
" union {\n"
" int i;\n"
" struct {\n"
" char a[24];\n"
" } x;\n"
" };\n"
"};\n"
"int f(S s) { return s.i; }\n");
ASSERT_EQUALS("[test.cpp:9:9]: (performance) Function parameter 's' should be passed by const reference. [passedByValue]\n", errout_str());

/*const*/ Settings settingsWin64 = settingsBuilder().platform(Platform::Type::Win64).build();
check("using ui64 = unsigned __int64;\n"
"ui64 Test(ui64 one, ui64 two) { return one + two; }\n",
Expand Down
53 changes: 53 additions & 0 deletions test/testvalueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1862,6 +1862,59 @@ class TestValueFlow : public TestFixture {
ASSERT_EQUALS(1U, values.size());
ASSERT_EQUALS(settings.platform.sizeof_int, values.back().intvalue);
ASSERT_EQUALS_ENUM(ValueFlow::Value::ValueKind::Known, values.back().valueKind);

code = "struct S {\n"
" union {\n"
" int i;\n"
" struct {\n"
" char a[24];\n"
" } x;\n"
" };\n"
"};\n"
"int f() { return sizeof(S); }\n";
values = tokenValues(code, "( S");
ASSERT_EQUALS(1U, values.size());
ASSERT_EQUALS(24, values.back().intvalue);
ASSERT_EQUALS_ENUM(ValueFlow::Value::ValueKind::Known, values.back().valueKind);

code = "struct S {\n"
" union {\n"
" int i;\n"
" double d;\n"
" };\n"
"};\n"
"int f() { return sizeof(S); }\n";
values = tokenValues(code, "( S");
ASSERT_EQUALS(1U, values.size());
ASSERT_EQUALS(settings.platform.sizeof_double, values.back().intvalue);
ASSERT_EQUALS_ENUM(ValueFlow::Value::ValueKind::Known, values.back().valueKind);

code = "struct S {\n"
" struct {\n"
" char a[12];\n"
" };\n"
" struct {\n"
" char b[12];\n"
" };\n"
"};\n"
"int f() { return sizeof(S); }\n";
values = tokenValues(code, "( S");
ASSERT_EQUALS(1U, values.size());
ASSERT_EQUALS(24, values.back().intvalue);
ASSERT_EQUALS_ENUM(ValueFlow::Value::ValueKind::Known, values.back().valueKind);

code = "struct S {\n"
" int x, y, z;\n"
" union {\n"
" int p, q;\n"
" };\n"
" char a[8];\n"
"};\n"
"int f() { return sizeof(S); }\n";
values = tokenValues(code, "( S");
ASSERT_EQUALS(1U, values.size());
ASSERT_EQUALS(24, values.back().intvalue);
ASSERT_EQUALS_ENUM(ValueFlow::Value::ValueKind::Known, values.back().valueKind);
}

void valueFlowComma()
Expand Down