From d5f2fb4854fba3cb492a78164af47f10e8749557 Mon Sep 17 00:00:00 2001 From: Daniel <52839433+DanTheMan2000@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:02:02 -0500 Subject: [PATCH 1/2] Fix #14371: FP redundantAssignment for nested union members --- lib/checkother.cpp | 6 +++- test/testother.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index d8f2f6ff6f4..2e6d5eafa4e 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -700,8 +700,12 @@ void CheckOtherImpl::checkRedundantAssignment() // Get next assignment.. const Token *nextAssign = fwdAnalysis.reassign(tokenToCheck, start, scope->bodyEnd); // extra check for union - if (nextAssign && tokenToCheck != tok->astOperand1()) + if (nextAssign && tokenToCheck != tok->astOperand1()) { nextAssign = fwdAnalysis.reassign(tok->astOperand1(), start, scope->bodyEnd); + // reading another member of the same union in the rhs is a use through aliasing + if (nextAssign && fwdAnalysis.hasOperand(nextAssign->astOperand2(), tokenToCheck)) + nextAssign = nullptr; + } if (!nextAssign) continue; diff --git a/test/testother.cpp b/test/testother.cpp index 92fefb72fd3..eed8d1c4124 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -11063,6 +11063,95 @@ class TestOther : public TestFixture { " Dst.s->y = Src.s->y;\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + // Ticket #14371 "redundantAssignment when using a union" + check("union U {\n" + " struct {\n" + " unsigned int abcd;\n" + " } u32;\n" + " struct {\n" + " unsigned short ab;\n" + " unsigned short cd;\n" + " } u16;\n" + "};\n" + "void f() {\n" + " U m;\n" + " m.u32.abcd = 1234;\n" + " m.u32.abcd = 5 * m.u16.ab;\n" + "}\n", dinit(CheckOptions, $.inconclusive = false)); + ASSERT_EQUALS("", errout_str()); + + // Ticket #14371 "redundantAssignment when using a union" + check("union U {\n" + " struct {\n" + " unsigned int abcd;\n" + " } u32;\n" + " struct {\n" + " unsigned short ab;\n" + " unsigned short cd;\n" + " } u16;\n" + "};\n" + "void f(unsigned int a, unsigned short b) {\n" + " U m;\n" + " m.u32.abcd = a;\n" + " m.u32.abcd += 0x8000;\n" + " m.u32.abcd = m.u16.ab * b;\n" + "}\n", dinit(CheckOptions, $.inconclusive = false)); + ASSERT_EQUALS("", errout_str()); + + // Related to #14371 - reading a different union variable must not suppress the warning + check("union U {\n" + " struct {\n" + " unsigned int abcd;\n" + " } u32;\n" + " struct {\n" + " unsigned short ab;\n" + " unsigned short cd;\n" + " } u16;\n" + "};\n" + "void f(unsigned int seed) {\n" + " U m, other;\n" + " other.u32.abcd = seed;\n" + " m.u32.abcd = 1234;\n" + " m.u32.abcd = other.u16.ab * 2;\n" + "}\n", dinit(CheckOptions, $.inconclusive = false)); + ASSERT_EQUALS("[test.cpp:13:16] -> [test.cpp:14:16]: (style) Variable 'm.u32.abcd' is reassigned a value before the old one has been used. [redundantAssignment]\n", errout_str()); + + // Ticket #14371 "redundantAssignment when using a union" + check("union U {\n" + " struct {\n" + " unsigned int abcd;\n" + " } u32;\n" + " struct {\n" + " unsigned short ab;\n" + " unsigned short cd;\n" + " } u16;\n" + "};\n" + "void f(unsigned short x) {\n" + " U m;\n" + " m.u16.ab = x;\n" + " m.u16.cd = 0;\n" + " m.u16.ab = m.u32.abcd / 53;\n" + "}\n", dinit(CheckOptions, $.inconclusive = false)); + ASSERT_EQUALS("", errout_str()); + + // Related to #14371 - an intervening write to a sibling member must not by itself suppress the warning + check("union U {\n" + " struct {\n" + " unsigned int abcd;\n" + " } u32;\n" + " struct {\n" + " unsigned short ab;\n" + " unsigned short cd;\n" + " } u16;\n" + "};\n" + "void f(unsigned short x, unsigned int y) {\n" + " U m;\n" + " m.u16.ab = x;\n" + " m.u16.cd = 0;\n" + " m.u16.ab = y;\n" + "}\n", dinit(CheckOptions, $.inconclusive = false)); + ASSERT_EQUALS("[test.cpp:12:14] -> [test.cpp:14:14]: (style) Variable 'm.u16.ab' is reassigned a value before the old one has been used. [redundantAssignment]\n", errout_str()); } void redundantVarAssignment_7133() { From 4304c1aebc4e01b15d456a77b1cc2d0db7b18c71 Mon Sep 17 00:00:00 2001 From: Daniel <52839433+DanTheMan2000@users.noreply.github.com> Date: Thu, 23 Jul 2026 07:08:56 -0500 Subject: [PATCH 2/2] Consolidate union tests into a single test case The separate #14371 tests all used the same union definition, so combine them into one test case containing multiple functions as requested in review. --- test/testother.cpp | 69 ++++++++-------------------------------------- 1 file changed, 11 insertions(+), 58 deletions(-) diff --git a/test/testother.cpp b/test/testother.cpp index eed8d1c4124..1fddea4b536 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -11074,84 +11074,37 @@ class TestOther : public TestFixture { " unsigned short cd;\n" " } u16;\n" "};\n" - "void f() {\n" + "void f1() {\n" " U m;\n" " m.u32.abcd = 1234;\n" " m.u32.abcd = 5 * m.u16.ab;\n" - "}\n", dinit(CheckOptions, $.inconclusive = false)); - ASSERT_EQUALS("", errout_str()); - - // Ticket #14371 "redundantAssignment when using a union" - check("union U {\n" - " struct {\n" - " unsigned int abcd;\n" - " } u32;\n" - " struct {\n" - " unsigned short ab;\n" - " unsigned short cd;\n" - " } u16;\n" - "};\n" - "void f(unsigned int a, unsigned short b) {\n" + "}\n" + "void f2(unsigned int a, unsigned short b) {\n" " U m;\n" " m.u32.abcd = a;\n" " m.u32.abcd += 0x8000;\n" " m.u32.abcd = m.u16.ab * b;\n" - "}\n", dinit(CheckOptions, $.inconclusive = false)); - ASSERT_EQUALS("", errout_str()); - - // Related to #14371 - reading a different union variable must not suppress the warning - check("union U {\n" - " struct {\n" - " unsigned int abcd;\n" - " } u32;\n" - " struct {\n" - " unsigned short ab;\n" - " unsigned short cd;\n" - " } u16;\n" - "};\n" - "void f(unsigned int seed) {\n" + "}\n" + "void f3(unsigned int seed) {\n" " U m, other;\n" " other.u32.abcd = seed;\n" " m.u32.abcd = 1234;\n" " m.u32.abcd = other.u16.ab * 2;\n" - "}\n", dinit(CheckOptions, $.inconclusive = false)); - ASSERT_EQUALS("[test.cpp:13:16] -> [test.cpp:14:16]: (style) Variable 'm.u32.abcd' is reassigned a value before the old one has been used. [redundantAssignment]\n", errout_str()); - - // Ticket #14371 "redundantAssignment when using a union" - check("union U {\n" - " struct {\n" - " unsigned int abcd;\n" - " } u32;\n" - " struct {\n" - " unsigned short ab;\n" - " unsigned short cd;\n" - " } u16;\n" - "};\n" - "void f(unsigned short x) {\n" + "}\n" + "void f4(unsigned short x) {\n" " U m;\n" " m.u16.ab = x;\n" " m.u16.cd = 0;\n" " m.u16.ab = m.u32.abcd / 53;\n" - "}\n", dinit(CheckOptions, $.inconclusive = false)); - ASSERT_EQUALS("", errout_str()); - - // Related to #14371 - an intervening write to a sibling member must not by itself suppress the warning - check("union U {\n" - " struct {\n" - " unsigned int abcd;\n" - " } u32;\n" - " struct {\n" - " unsigned short ab;\n" - " unsigned short cd;\n" - " } u16;\n" - "};\n" - "void f(unsigned short x, unsigned int y) {\n" + "}\n" + "void f5(unsigned short x, unsigned int y) {\n" " U m;\n" " m.u16.ab = x;\n" " m.u16.cd = 0;\n" " m.u16.ab = y;\n" "}\n", dinit(CheckOptions, $.inconclusive = false)); - ASSERT_EQUALS("[test.cpp:12:14] -> [test.cpp:14:14]: (style) Variable 'm.u16.ab' is reassigned a value before the old one has been used. [redundantAssignment]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:24:16] -> [test.cpp:25:16]: (style) Variable 'm.u32.abcd' is reassigned a value before the old one has been used. [redundantAssignment]\n" + "[test.cpp:35:14] -> [test.cpp:37:14]: (style) Variable 'm.u16.ab' is reassigned a value before the old one has been used. [redundantAssignment]\n", errout_str()); } void redundantVarAssignment_7133() {