Repository: llvm/llvm-project
File: clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp (C++)

Code before:
      // Assignment
      "swap"};

  // 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
                                         "swap", "replace"};

  return llvm::StringSwitch<const llvm::StringSet<> *>(ContainerName)
      .Case("vector", &Vector)
      .Case("basic_string", &String)
      .Case("deque", &Deque)
      .Case("priority_queue", &PriorityQueue)
      .Cases({"set", "multiset", "map", "multimap"}, &Associative)
      .Cases({"unordered_set", "unordered_multiset", "unordered_map",
              "unordered_multimap"},
             &UnorderedAssociative)
      .Cases({"flat_map", "flat_set", "flat_multimap", "flat_multiset"}, &Flat)
      .Default(nullptr);
}

bool isContainerInvalidationMethod(const CXXMethodDecl &MD) {
  const CXXRecordDecl *RD = MD.getParent();
  if (!isInStlNamespace(RD))
    return false;

  StringRef ContainerName = getName(*RD);
  const llvm::StringSet<> *InvalidatingMethods =
      getInvalidatingMethods(ContainerName);
  if (!InvalidatingMethods)
    return false;

  // Handle Operators via OverloadedOperatorKind
  OverloadedOperatorKind OO = MD.getOverloadedOperator();
  if (OO != OO_None) {
    switch (OO) {
    case OO_Equal:     // operator= : Always invalidates (Assignment)
    case OO_PlusEqual: // operator+= : Append (String/Vector)
      return true;
    case OO_Subscript: // operator[] : Invalidation only for Maps
                       // (Insert-or-access)
    {
      static const llvm::StringSet<> MapContainers = {"map", "unordered_map",
                                                      "flat_map"};

Review comment (bug):
I wonder if the calling code becomes simpler if we return an empty set instead of a null pointer. 

Code after:
  // TODO: Consider caching this lookup by CXXMethodDecl pointer if this
  // StringSwitch becomes a performance bottleneck.
  const llvm::StringSet<> *InvalidatingMethods =
      llvm::StringSwitch<const llvm::StringSet<> *>(ContainerName)
          .Case("vector", &Vector)
          .Case("basic_string", &String)
          .Case("deque", &Deque)
          .Case("priority_queue", &PriorityQueue)
          .Cases({"set", "multiset", "map", "multimap", "unordered_set",
                  "unordered_multiset", "unordered_map", "unordered_multimap"},
                 &NodeBased)
          .Cases({"flat_map", "flat_set", "flat_multimap", "flat_multiset"},
                 &Flat)
          .Default(nullptr);

  if (!InvalidatingMethods)
    return false;

  // Handle Operators via OverloadedOperatorKind
  OverloadedOperatorKind OO = MD.getOverloadedOperator();
  if (OO != OO_None) {
    switch (OO) {
    case OO_Equal:     // operator= : Always invalidates (Assignment)
    case OO_PlusEqual: // operator+= : Append (String/Vector)
      return true;
    case OO_Subscript: // operator[] : Invalidation only for
                       // `flat_map` (Insert-or-access).
                       // `map` and `unordered_map` are excluded.
      return ContainerName == "flat_map";
    default:
      return false;
    }
  }

  if (!MD.getIdentifier())
    return false;

  return InvalidatingMethods->contains(MD.getName());
}
} // namespace clang::lifetimes