[clang] [LifetimeSafety] Use per-container invalidation rules to fix false positives (PR #183000)

Utkarsh Saxena via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 3 02:56:44 PST 2026


================
@@ -277,23 +277,85 @@ bool isContainerInvalidationMethod(const CXXMethodDecl &MD) {
   if (!isInStlNamespace(RD))
     return false;
 
-  StringRef ContainerName = getName(*RD);
-  static const llvm::StringSet<> Containers = {
-      // Sequence
-      "vector", "basic_string", "deque",
-      // Adaptors
-      // FIXME: Add queue and stack and check for underlying container (e.g. no
-      // invalidation for std::list).
-      "priority_queue",
-      // Associative
-      "set", "multiset", "map", "multimap",
-      // Unordered Associative
-      "unordered_set", "unordered_multiset", "unordered_map",
-      "unordered_multimap",
-      // C++23 Flat
-      "flat_map", "flat_set", "flat_multimap", "flat_multiset"};
-
-  if (!Containers.contains(ContainerName))
+  // `pop_back` is excluded: it only invalidates references to the removed
+  // element, not to other elements.
+  // https://en.cppreference.com/w/cpp/container/vector/pop_back.html
+  static const llvm::StringSet<> Vector = {// Insertion
+                                           "insert", "emplace", "emplace_back",
+                                           "push_back", "insert_range",
+                                           "append_range",
+                                           // Removal
+                                           "erase", "clear",
+                                           // Memory management
+                                           "reserve", "resize", "shrink_to_fit",
+                                           // Assignment
+                                           "assign", "assign_range"};
+
+  // `pop_*` methods are excluded: they only invalidate references to the
+  // removed element, not to other elements.
+  // https://en.cppreference.com/w/cpp/container/deque.html#Invalidation_notes
+  static const llvm::StringSet<> Deque = {// Insertion
+                                          "insert", "emplace", "insert_range",
+                                          // Removal
+                                          "erase", "clear",
+                                          // Memory management
+                                          "resize", "shrink_to_fit",
+                                          // Assignment
+                                          "assign", "assign_range"};
+
+  static const llvm::StringSet<> String = {
+      // Insertion
+      "insert", "push_back", "append", "replace", "replace_with_range",
+      "insert_range", "append_range",
+      // Removal
+      "pop_back", "erase", "clear",
+      // Memory management
+      "reserve", "resize", "resize_and_overwrite", "shrink_to_fit",
+      // Assignment
+      "swap", "assign", "assign_range"};
+
+  // FIXME: Add queue and stack and check for underlying container
+  // (e.g. no invalidation for std::list).
+  static const llvm::StringSet<> PriorityQueue = {// Insertion
+                                                  "push", "emplace",
+                                                  "push_range",
+                                                  // Removal
+                                                  "pop"};
+
+  // `erase` and `extract` are excluded: they only affect the removed element,
+  // not to other elements.
+  // https://en.cppreference.com/w/cpp/container/map/erase.html
+  // https://en.cppreference.com/w/cpp/container/map/extract.html
+  static const llvm::StringSet<> NodeBased = {// Removal
+                                              "clear"};
+
+  // For `flat_*` container adaptors, `try_emplace` and `insert_or_assign`
+  // only exist on `flat_map`. Listing them here is harmless since the methods
+  // won't be found on other types.
+  static const llvm::StringSet<> Flat = {// Insertion
+                                         "insert", "emplace", "emplace_hint",
+                                         "try_emplace", "insert_or_assign",
+                                         "insert_range", "merge",
+                                         // Removal
+                                         "extract", "erase", "clear",
+                                         // Assignment
+                                         "replace"};
+
+  const StringRef ContainerName = getName(*RD);
+  const llvm::StringSet<> *InvalidatingMethods =
----------------
usx95 wrote:

Not particular to this PR but I expect this function `isContainerInvalidationMethod`and specially this `StringSwitch` to be very hot. Consider adding a TODO to cache this by `CXXMethodDecl` pointer in case this becomes a bottleneck.

https://github.com/llvm/llvm-project/pull/183000


More information about the cfe-commits mailing list