diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql index 4ba1ad93f565..5239fe5d26ad 100644 --- a/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql @@ -57,5 +57,6 @@ where not declarationHasSideEffects(v) and not exists(AsmStmt s | f = s.getEnclosingFunction()) and not v.getAnAttribute().getName() = "unused" and + not v.getAnAttribute().getName() = "maybe_unused" and not f.hasErrors() // Unextracted expressions may use `v` select v, "Variable " + v.getName() + " is not used." diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql index 03176b8a7791..dc60d9af92b1 100644 --- a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql @@ -26,5 +26,6 @@ where not v instanceof MemberVariable and not declarationHasSideEffects(v) and not v.getAnAttribute().hasName("used") and - not v.getAnAttribute().hasName("unused") + not v.getAnAttribute().hasName("unused") and + not v.getAnAttribute().hasName("maybe_unused") select v, "Static variable " + v.getName() + " is never read." diff --git a/cpp/ql/src/change-notes/2026-10-29-unused-maybe-unused.md b/cpp/ql/src/change-notes/2026-10-29-unused-maybe-unused.md new file mode 100644 index 000000000000..a898159583fb --- /dev/null +++ b/cpp/ql/src/change-notes/2026-10-29-unused-maybe-unused.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +The `cpp/unused-local-variable` and `cpp/unused-static-variable` queries no longer report variables marked with the C++17 `[[maybe_unused]]` attribute. 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 b0e00dc8fbe1..9e92ec37b4cf 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 @@ -176,3 +176,8 @@ void test_switch_initializer() // ... } } + +void test_maybe_unused() +{ + [[maybe_unused]] int unused; // GOOD: explicitly marked as maybe_unused +} 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 3b60a4722d4d..0483595ba385 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 @@ -1,4 +1,4 @@ - +// semmle-extractor-options: -std=c++17 int globalVar; // GOOD (not static) static int staticVar1; // GOOD (used) static int staticVar2; // GOOD (used) @@ -10,6 +10,7 @@ static __attribute__((__unused__)) int staticVar7; // GOOD (unused but this is e const int constVar8 = 8; // $ Alert // BAD (const defaults to static) extern const int constVar9 = 9; // GOOD static int staticVar10 = 10; // $ SPURIOUS: Alert // GOOD [FALSE POSITIVE] (referenced in a never instantiated template) +[[maybe_unused]] static int staticVar11; // GOOD (explicitly marked as maybe_unused) void f() {