diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 26801640e99..af860d0a1b2 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -8687,7 +8687,7 @@ namespace { } template -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 anonScopes; @@ -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 <") { 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}; } @@ -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); }; @@ -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); @@ -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; diff --git a/test/testother.cpp b/test/testother.cpp index 61ff7b2dae5..730bdb8015f 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -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", diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index b8496c863d5..3b6921e0df8 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -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()