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