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/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..1cc9f421d377 --- /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. 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..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,7 +4,6 @@ | 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. | | code.c:10:18:10:18 | y | Variable y is not used. | | code.c:11:18:11:18 | z | Variable z 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..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) // $ Alert // BAD: v2 is not used + for (int v2 : myContainer) // GOOD: iterator dereference may have side effects { } } @@ -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(); // GOOD (initializer has a side effect) +} 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..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 @@ -29,3 +29,23 @@ class MyTemplateClass // never instantiated // ... } }; + +int initialize() +{ + return ++globalVar; +} + +static int initialized = initialize(); // 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)