Skip to content

Commit 3baed90

Browse files
Merge pull request #1181 from castler/fix-rule-5-10-1-hash-specialization-reserved-namespace
RULE-5-10-1: fix false positives for locals/parameters in permitted std specializations (e.g. std::hash)
2 parents 672571f + 8e8a8f8 commit 3baed90

4 files changed

Lines changed: 60 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- `RULE-5-10-1` - `PoorlyFormedIdentifier.ql`:
2+
- Fixed false positives where a local variable or function parameter was reported as
3+
"defined in reserved namespace" merely because its enclosing function is the body of
4+
an explicit template specialization that C++ permits users to add to namespace `std`
5+
(for example, `std::hash<UserType>::operator()`'s parameter and local names).

cpp/common/src/codingstandards/cpp/Identifiers.qll

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,20 @@ private module IdentifierIntroductionImpl {
361361

362362
override string getAnIdent() { result = this.getName() }
363363

364-
override Namespace getNamespace() { result = variable.getNamespace() }
364+
override Namespace getNamespace() {
365+
// A parameter or local variable is scoped to the body of its enclosing function, not to
366+
// that function's enclosing namespace. It does not itself become a new member of the
367+
// namespace, so it cannot "pollute" a reserved namespace such as `std`, regardless of which
368+
// namespace its enclosing function happens to be declared in. This matters in particular
369+
// for the bodies of explicit template specializations that C++ explicitly permits users to
370+
// add to namespace `std` (such as `std::hash<UserType>`), where parameter and local names
371+
// are otherwise ordinary user-chosen identifiers that merely happen to be lexically nested
372+
// inside `namespace std { ... }`. The function or class that actually introduces the
373+
// specialization's name is still checked against `isReservedNamespace` independently, via
374+
// `FunctionDeclarationEntryIdentifier` / `TypeDeclarationEntryIdentifier`.
375+
not variable instanceof LocalScopeVariable and
376+
result = variable.getNamespace()
377+
}
365378
}
366379

367380
/**

cpp/misra/test/rules/RULE-5-10-1/PoorlyFormedIdentifier.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@
5151
| test.cpp:186:7:186:12 | wint_t | Identifier 'wint_t' is a reserved name. |
5252
| test.cpp:203:1:203:42 | __PRETTY_FUNCTION__ | Identifier '__PRETTY_FUNCTION__' contains double underscores. |
5353
| test.cpp:203:1:203:42 | __PRETTY_FUNCTION__ | Identifier '__PRETTY_FUNCTION__' starts with underscore. |
54+
| test.cpp:263:6:263:21 | new_std_function | Identifier 'new_std_function' is defined in reserved namespace. |

cpp/misra/test/rules/RULE-5-10-1/test.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,43 @@ struct hash<int> { // COMPLIANT - rule does not apply to template
222222
std::size_t operator()(const int &x) const {
223223
return static_cast<std::size_t>(x);
224224
}
225-
};
225+
};
226+
227+
// Test case for RULE-5-10-1 false positive fix: a local variable or function
228+
// parameter is scoped to the body of its enclosing function, not to the
229+
// namespace that function happens to be declared in. This matters for the
230+
// body of an explicit template specialization that C++ explicitly permits
231+
// users to add to namespace `std` (such as `std::hash<UserType>`): the
232+
// parameter and local names below are ordinary user-chosen identifiers and
233+
// do not themselves become new members of namespace `std`.
234+
struct UserTypeForHash {
235+
int payload;
236+
};
237+
238+
namespace std {
239+
template <typename T>
240+
struct hash; // forward declaration of the primary
241+
// template that is specialized below
242+
243+
template <> struct hash<UserTypeForHash> {
244+
std::size_t
245+
operator()(const UserTypeForHash &value) const noexcept { // COMPLIANT -
246+
// 'value' is a
247+
// parameter
248+
// scoped to the
249+
// function body,
250+
// not a member
251+
// of namespace
252+
// std
253+
std::size_t result = static_cast<std::size_t>(
254+
value.payload); // COMPLIANT - 'result' is a local variable scoped to
255+
// the function body, not a member of namespace std
256+
return result;
257+
}
258+
};
259+
260+
// A genuinely new function declared directly in namespace std remains a
261+
// violation: this is not a permitted specialization, and it does introduce a
262+
// new name into the reserved namespace.
263+
void new_std_function() {} // NON_COMPLIANT - namespace std is reserved
264+
} // namespace std

0 commit comments

Comments
 (0)