From 8e8a8f83eacee57c68dec063b848f1fbb4b38aaa Mon Sep 17 00:00:00 2001 From: Jan Schlosser Date: Mon, 24 Aug 2026 16:47:12 +0200 Subject: [PATCH] Fix RULE-5-10-1 false positives for locals/parameters in std specializations RULE-5-10-1 (poorly-formed identifier) flagged ordinary, well-formed, snake_case local variables and function parameters (e.g. `value`, `result`, `view`, `instance_identifier`) with "Identifier 'X' is defined in reserved namespace." whenever they were declared inside the body of an explicit template specialization that C++ explicitly permits users to add to namespace `std`, most commonly `std::hash::operator()`. On the eclipse-score/communication codebase this produced 66 findings, all inside `std::hash`- style specializations in headers such as handle_type.h, trace_point_key.h and service_identifier_type.h. Root cause: `VariableDeclarationEntryIdentifier::getNamespace()` used `Variable.getNamespace()` directly. For a `Parameter` or `LocalVariable`, the upstream CodeQL C++ library defines this as the namespace of the *enclosing function* (see `codeql/cpp-all/.../Declaration.qll`), which is a reasonable general notion of "namespace context" but is not what RULE-5-10-1 means by "is defined in reserved namespace". A block-scope local or parameter is scoped to the body of its enclosing function; it is not itself a member of that function's enclosing namespace and therefore cannot introduce a new name into (or "pollute") a reserved namespace such as `std`, regardless of which namespace the enclosing function happens to be declared in. This is exactly the situation for the body of a permitted `std::hash` specialization: the parameter and local names are ordinary user-chosen identifiers that merely happen to be lexically nested inside `namespace std { ... }` because the standard requires the specialization to be written there. Fix: in `VariableDeclarationEntryIdentifier`, only report a namespace for variables that are not `LocalScopeVariable` (i.e. not a `Parameter` or `LocalVariable`). This is a narrow, semantically-motivated scope check, not a name- or path-based exclusion: any genuinely new namespace-scope or class-scope member added directly to `std` (a new global, function, type, or the specialization itself) is still checked independently via `FunctionDeclarationEntryIdentifier` / `TypeDeclarationEntryIdentifier`, and remains flagged. A new NON_COMPLIANT test case (a function declared directly in `namespace std`) confirms this is unaffected. Added a regression test reproducing the `std::hash` specialization shape with a parameter and a local both named after the FP pattern (COMPLIANT), and confirmed with the "teeth" test that reverting only `Identifiers.qll` while keeping the new test makes it fail with exactly the two spurious findings. The existing RULE-5-10-1 test suite and the shared Identifiers library test continue to pass. Validated against the real eclipse-score/communication CodeQL database (`//score/message_passing //score/mw/com`): "is defined in reserved namespace" findings for this shape dropped from 66 to 0, while the one, unrelated "starts with underscore" finding in the same rule is unchanged. All 66 baseline findings were manually confirmed to be locals/parameters inside `std::hash`-style specializations, i.e. genuine false positives. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...-hash-specialization-reserved-namespace.md | 5 +++ .../src/codingstandards/cpp/Identifiers.qll | 15 ++++++- .../PoorlyFormedIdentifier.expected | 1 + cpp/misra/test/rules/RULE-5-10-1/test.cpp | 41 ++++++++++++++++++- 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 change_notes/2026-08-24-fix-fp-rule-5-10-1-hash-specialization-reserved-namespace.md diff --git a/change_notes/2026-08-24-fix-fp-rule-5-10-1-hash-specialization-reserved-namespace.md b/change_notes/2026-08-24-fix-fp-rule-5-10-1-hash-specialization-reserved-namespace.md new file mode 100644 index 000000000..d50c5ea90 --- /dev/null +++ b/change_notes/2026-08-24-fix-fp-rule-5-10-1-hash-specialization-reserved-namespace.md @@ -0,0 +1,5 @@ +- `RULE-5-10-1` - `PoorlyFormedIdentifier.ql`: + - Fixed false positives where a local variable or function parameter was reported as + "defined in reserved namespace" merely because its enclosing function is the body of + an explicit template specialization that C++ permits users to add to namespace `std` + (for example, `std::hash::operator()`'s parameter and local names). diff --git a/cpp/common/src/codingstandards/cpp/Identifiers.qll b/cpp/common/src/codingstandards/cpp/Identifiers.qll index 23751ac26..cbacb9b8e 100644 --- a/cpp/common/src/codingstandards/cpp/Identifiers.qll +++ b/cpp/common/src/codingstandards/cpp/Identifiers.qll @@ -361,7 +361,20 @@ private module IdentifierIntroductionImpl { override string getAnIdent() { result = this.getName() } - override Namespace getNamespace() { result = variable.getNamespace() } + override Namespace getNamespace() { + // A parameter or local variable is scoped to the body of its enclosing function, not to + // that function's enclosing namespace. It does not itself become a new member of the + // namespace, so it cannot "pollute" a reserved namespace such as `std`, regardless of which + // namespace its enclosing function happens to be declared in. This matters in particular + // for the bodies of explicit template specializations that C++ explicitly permits users to + // add to namespace `std` (such as `std::hash`), where parameter and local names + // are otherwise ordinary user-chosen identifiers that merely happen to be lexically nested + // inside `namespace std { ... }`. The function or class that actually introduces the + // specialization's name is still checked against `isReservedNamespace` independently, via + // `FunctionDeclarationEntryIdentifier` / `TypeDeclarationEntryIdentifier`. + not variable instanceof LocalScopeVariable and + result = variable.getNamespace() + } } /** diff --git a/cpp/misra/test/rules/RULE-5-10-1/PoorlyFormedIdentifier.expected b/cpp/misra/test/rules/RULE-5-10-1/PoorlyFormedIdentifier.expected index 1c5abc431..ad045a88a 100644 --- a/cpp/misra/test/rules/RULE-5-10-1/PoorlyFormedIdentifier.expected +++ b/cpp/misra/test/rules/RULE-5-10-1/PoorlyFormedIdentifier.expected @@ -51,3 +51,4 @@ | test.cpp:186:7:186:12 | wint_t | Identifier 'wint_t' is a reserved name. | | test.cpp:203:1:203:42 | __PRETTY_FUNCTION__ | Identifier '__PRETTY_FUNCTION__' contains double underscores. | | test.cpp:203:1:203:42 | __PRETTY_FUNCTION__ | Identifier '__PRETTY_FUNCTION__' starts with underscore. | +| test.cpp:263:6:263:21 | new_std_function | Identifier 'new_std_function' is defined in reserved namespace. | diff --git a/cpp/misra/test/rules/RULE-5-10-1/test.cpp b/cpp/misra/test/rules/RULE-5-10-1/test.cpp index b529a971c..3f74e104c 100644 --- a/cpp/misra/test/rules/RULE-5-10-1/test.cpp +++ b/cpp/misra/test/rules/RULE-5-10-1/test.cpp @@ -222,4 +222,43 @@ struct hash { // COMPLIANT - rule does not apply to template std::size_t operator()(const int &x) const { return static_cast(x); } -}; \ No newline at end of file +}; + +// Test case for RULE-5-10-1 false positive fix: a local variable or function +// parameter is scoped to the body of its enclosing function, not to the +// namespace that function happens to be declared in. This matters for the +// body of an explicit template specialization that C++ explicitly permits +// users to add to namespace `std` (such as `std::hash`): the +// parameter and local names below are ordinary user-chosen identifiers and +// do not themselves become new members of namespace `std`. +struct UserTypeForHash { + int payload; +}; + +namespace std { +template +struct hash; // forward declaration of the primary + // template that is specialized below + +template <> struct hash { + std::size_t + operator()(const UserTypeForHash &value) const noexcept { // COMPLIANT - + // 'value' is a + // parameter + // scoped to the + // function body, + // not a member + // of namespace + // std + std::size_t result = static_cast( + value.payload); // COMPLIANT - 'result' is a local variable scoped to + // the function body, not a member of namespace std + return result; + } +}; + +// A genuinely new function declared directly in namespace std remains a +// violation: this is not a permitted specialization, and it does introduce a +// new name into the reserved namespace. +void new_std_function() {} // NON_COMPLIANT - namespace std is reserved +} // namespace std \ No newline at end of file