From f985aee6430ad1fcfd1dc84000bae5015420e754 Mon Sep 17 00:00:00 2001 From: jketema <93738568+jketema@users.noreply.github.com> Date: Thu, 29 Oct 2026 15:13:32 +0000 Subject: [PATCH 1/5] Test side effects in unused-variable queries Cover side-effecting initializers for local and static variables, recording the current alerts as spurious. Also cover constructor and destructor side effects for static global objects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../UnusedLocals/UnusedLocals.expected | 1 + .../Unused Entities/UnusedLocals/code2.cpp | 12 +++++++++++ .../UnusedStaticVariables.expected | 1 + .../UnusedStaticVariables/test.cpp | 20 +++++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/UnusedLocals.expected b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/UnusedLocals.expected index 8db9fdb3ec64..1db7537577ae 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/UnusedLocals.expected +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/UnusedLocals.expected @@ -6,6 +6,7 @@ | code2.cpp:64:7:64:8 | v3 | Variable v3 is not used. | | code2.cpp:108:11:108:12 | v2 | Variable v2 is not used. | | code2.cpp:128:9:128:9 | b | Variable b is not used. | +| code2.cpp:194:6:194:16 | initialized | Variable initialized is not used. | | code.c:10:18:10:18 | y | Variable y is not used. | | code.c:11:18:11:18 | z | Variable z is not used. | | code.c:18:7:18:7 | x | Variable x is not used. | diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp index 9e92ec37b4cf..216f55232478 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp @@ -181,3 +181,15 @@ void test_maybe_unused() { [[maybe_unused]] int unused; // GOOD: explicitly marked as maybe_unused } + +int globalVar; + +int initialize() +{ + return ++globalVar; +} + +void test_initializer_side_effect() +{ + int initialized = initialize(); // $ SPURIOUS: Alert // GOOD (initializer has a side effect) +} diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/UnusedStaticVariables.expected b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/UnusedStaticVariables.expected index 45367233424e..38baa4ea4d8d 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/UnusedStaticVariables.expected +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/UnusedStaticVariables.expected @@ -2,3 +2,4 @@ | test.cpp:8:12:8:21 | staticVar6 | Static variable staticVar6 is never read. | | test.cpp:10:11:10:19 | constVar8 | Static variable constVar8 is never read. | | test.cpp:12:12:12:22 | staticVar10 | Static variable staticVar10 is never read. | +| test.cpp:38:12:38:22 | initialized | Static variable initialized is never read. | diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/test.cpp b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/test.cpp index 0483595ba385..be51744a3fb4 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/test.cpp +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/test.cpp @@ -29,3 +29,23 @@ class MyTemplateClass // never instantiated // ... } }; + +int initialize() +{ + return ++globalVar; +} + +static int initialized = initialize(); // $ SPURIOUS: Alert // GOOD (initializer has a side effect) + +struct WithConstructor +{ + WithConstructor() { ++globalVar; } +}; + +struct WithDestructor +{ + ~WithDestructor() { ++globalVar; } +}; + +static WithConstructor constructed; // GOOD (constructor has a side effect) +static WithDestructor destructed; // GOOD (destructor has a side effect) From 8a8c4f395f70e3630316880508711465439237a4 Mon Sep 17 00:00:00 2001 From: jketema <93738568+jketema@users.noreply.github.com> Date: Thu, 29 Oct 2026 15:16:35 +0000 Subject: [PATCH 2/5] Respect initializer side effects in unused-variable queries Exclude local and static variables whose initializer may be impure, preserving existing constructor and destructor exemptions. Regenerate expected results and mark the range-for case as missing because its undefined iterator dereference may have side effects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql | 2 ++ .../Best Practices/Unused Entities/UnusedStaticVariables.ql | 2 ++ .../Unused Entities/UnusedLocals/UnusedLocals.expected | 2 -- .../Best Practices/Unused Entities/UnusedLocals/code2.cpp | 4 ++-- .../UnusedStaticVariables/UnusedStaticVariables.expected | 1 - .../Unused Entities/UnusedStaticVariables/test.cpp | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql index 5239fe5d26ad..6d5948300db1 100644 --- a/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql @@ -46,6 +46,8 @@ predicate declarationHasSideEffects(Variable v) { ) or v.getType() instanceof TemplateDependentType // may have a constructor/destructor + or + v.getInitializer().getExpr().mayBeImpure() } from LocalVariable v, Function f diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql index dc60d9af92b1..c67bac65bdd3 100644 --- a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql @@ -15,6 +15,8 @@ import cpp predicate declarationHasSideEffects(Variable v) { exists(Class c | c = v.getUnspecifiedType() | c.hasConstructor() or c.hasDestructor()) + or + v.getInitializer().getExpr().mayBeImpure() } from Variable v diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/UnusedLocals.expected b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/UnusedLocals.expected index 1db7537577ae..cf5058b3cfa8 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/UnusedLocals.expected +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/UnusedLocals.expected @@ -4,9 +4,7 @@ | code2.cpp:26:16:26:17 | v1 | Variable v1 is not used. | | code2.cpp:42:11:42:16 | myVar1 | Variable myVar1 is not used. | | code2.cpp:64:7:64:8 | v3 | Variable v3 is not used. | -| code2.cpp:108:11:108:12 | v2 | Variable v2 is not used. | | code2.cpp:128:9:128:9 | b | Variable b is not used. | -| code2.cpp:194:6:194:16 | initialized | Variable initialized is not used. | | code.c:10:18:10:18 | y | Variable y is not used. | | code.c:11:18:11:18 | z | Variable z is not used. | | code.c:18:7:18:7 | x | Variable x is not used. | diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp index 216f55232478..cdab9af74da8 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp @@ -105,7 +105,7 @@ void test_range_based_for() output(v1); } - for (int v2 : myContainer) // $ Alert // BAD: v2 is not used + for (int v2 : myContainer) // $ MISSING: Alert // BAD: v2 is not used (iterator dereference may have side effects) { } } @@ -191,5 +191,5 @@ int initialize() void test_initializer_side_effect() { - int initialized = initialize(); // $ SPURIOUS: Alert // GOOD (initializer has a side effect) + int initialized = initialize(); // GOOD (initializer has a side effect) } diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/UnusedStaticVariables.expected b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/UnusedStaticVariables.expected index 38baa4ea4d8d..45367233424e 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/UnusedStaticVariables.expected +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/UnusedStaticVariables.expected @@ -2,4 +2,3 @@ | test.cpp:8:12:8:21 | staticVar6 | Static variable staticVar6 is never read. | | test.cpp:10:11:10:19 | constVar8 | Static variable constVar8 is never read. | | test.cpp:12:12:12:22 | staticVar10 | Static variable staticVar10 is never read. | -| test.cpp:38:12:38:22 | initialized | Static variable initialized is never read. | diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/test.cpp b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/test.cpp index be51744a3fb4..7a18491408c6 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/test.cpp +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedStaticVariables/test.cpp @@ -35,7 +35,7 @@ int initialize() return ++globalVar; } -static int initialized = initialize(); // $ SPURIOUS: Alert // GOOD (initializer has a side effect) +static int initialized = initialize(); // GOOD (initializer has a side effect) struct WithConstructor { From 24f492b6a89abcdeeff187b073ca0c17b2790cd0 Mon Sep 17 00:00:00 2001 From: jketema <93738568+jketema@users.noreply.github.com> Date: Thu, 29 Oct 2026 15:21:15 +0000 Subject: [PATCH 3/5] Document unused-variable initializer side-effect fix Add a minor analysis change note for suppressing alerts on variables whose initializers may have side effects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../change-notes/2026-10-29-unused-variable-initializers.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/src/change-notes/2026-10-29-unused-variable-initializers.md diff --git a/cpp/ql/src/change-notes/2026-10-29-unused-variable-initializers.md b/cpp/ql/src/change-notes/2026-10-29-unused-variable-initializers.md new file mode 100644 index 000000000000..fa9ad266924c --- /dev/null +++ b/cpp/ql/src/change-notes/2026-10-29-unused-variable-initializers.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +The `cpp/unused-local-variable` and `cpp/unused-static-variable` queries no longer report variables whose initializers may have side effects. From 0ecfb5e6bf93ffb1928a05eccc0837d811e39376 Mon Sep 17 00:00:00 2001 From: jketema <93738568+jketema@users.noreply.github.com> Date: Thu, 29 Oct 2026 15:22:14 +0000 Subject: [PATCH 4/5] Mark side-effecting iterator dereference as a good case Treat the unused range-for variable as intentionally unreported because its initializer may have side effects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Best Practices/Unused Entities/UnusedLocals/code2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp index cdab9af74da8..e033a69f9610 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedLocals/code2.cpp @@ -105,7 +105,7 @@ void test_range_based_for() output(v1); } - for (int v2 : myContainer) // $ MISSING: Alert // BAD: v2 is not used (iterator dereference may have side effects) + for (int v2 : myContainer) // GOOD: iterator dereference may have side effects { } } From b9d9c24c5c2ceb82ea70ad63f61680579824d9a5 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema <93738568+jketema@users.noreply.github.com> Date: Thu, 29 Oct 2026 18:18:54 +0100 Subject: [PATCH 5/5] Update cpp/ql/src/change-notes/2026-10-29-unused-variable-initializers.md --- .../src/change-notes/2026-10-29-unused-variable-initializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/change-notes/2026-10-29-unused-variable-initializers.md b/cpp/ql/src/change-notes/2026-10-29-unused-variable-initializers.md index fa9ad266924c..1cc9f421d377 100644 --- a/cpp/ql/src/change-notes/2026-10-29-unused-variable-initializers.md +++ b/cpp/ql/src/change-notes/2026-10-29-unused-variable-initializers.md @@ -1,4 +1,4 @@ --- category: minorAnalysis --- -The `cpp/unused-local-variable` and `cpp/unused-static-variable` queries no longer report variables whose initializers may have side effects. +* The `cpp/unused-local-variable` and `cpp/unused-static-variable` queries no longer report variables whose initializers may have side effects.