Add LifetimeSymbolTable::LookupLifetimeAndMaybeDeclare().

PiperOrigin-RevId: 413108197
diff --git a/lifetime_annotations/lifetime_symbol_table.cc b/lifetime_annotations/lifetime_symbol_table.cc
index c5b6b31..040a0a7 100644
--- a/lifetime_annotations/lifetime_symbol_table.cc
+++ b/lifetime_annotations/lifetime_symbol_table.cc
@@ -5,6 +5,7 @@
 #include "lifetime_annotations/lifetime_symbol_table.h"
 
 #include <optional>
+#include <string>
 
 namespace devtools_rust {
 
@@ -50,4 +51,44 @@
   return iter->second;
 }
 
+static std::string NameFromIndex(int index) {
+  int num_chars = 1;
+  // Number of combinations that are possible with `num_chars` characters.
+  int num_combinations = 26;
+  while (index >= num_combinations) {
+    index -= num_combinations;
+    ++num_chars;
+    num_combinations *= 26;
+  }
+  std::string name;
+  name.reserve(num_chars);
+  for (int i = 0; i < num_chars; ++i) {
+    name.insert(0, 1, 'a' + index % 26);
+    index /= 26;
+  }
+  return name;
+}
+
+llvm::StringRef LifetimeSymbolTable::LookupLifetimeAndMaybeDeclare(
+    Lifetime lifetime) {
+  if (lifetime == Lifetime::Static()) {
+    return "static";
+  }
+
+  auto lifetime_to_name_iter = lifetime_to_name_.find(lifetime);
+  if (lifetime_to_name_iter != lifetime_to_name_.end()) {
+    return lifetime_to_name_iter->second;
+  }
+
+  while (true) {
+    std::string name = NameFromIndex(next_name_index_++);
+    auto [name_to_lifetime_iter, inserted] =
+        name_to_lifetime_.try_emplace(name, lifetime);
+    if (inserted) {
+      lifetime_to_name_[lifetime] = name;
+      return name_to_lifetime_iter->first();
+    }
+  }
+}
+
 }  // namespace devtools_rust
diff --git a/lifetime_annotations/lifetime_symbol_table.h b/lifetime_annotations/lifetime_symbol_table.h
index c9599f7..d53ca4d 100644
--- a/lifetime_annotations/lifetime_symbol_table.h
+++ b/lifetime_annotations/lifetime_symbol_table.h
@@ -35,9 +35,18 @@
   // If `lifetime` is `Lifetime::Static()`, returns "static".
   std::optional<llvm::StringRef> LookupLifetime(Lifetime lifetime) const;
 
+  // Looks up a lifetime in the symbol table and inserts a new autogenerated
+  // name for it if the lifetime was not found.
+  // If `lifetime` is `Lifetime::Static()`, returns "static".
+  // The autogenerated name will be the first name from the sequence
+  // "a", ..., "z", "aa", "ab", "ac", ... that is not yet contained in the
+  // symbol table.
+  llvm::StringRef LookupLifetimeAndMaybeDeclare(Lifetime lifetime);
+
  private:
   llvm::StringMap<Lifetime> name_to_lifetime_;
   llvm::DenseMap<Lifetime, std::string> lifetime_to_name_;
+  int next_name_index_ = 0;
 };
 
 }  // namespace devtools_rust
diff --git a/lifetime_annotations/lifetime_symbol_table_test.cc b/lifetime_annotations/lifetime_symbol_table_test.cc
index 9453778..9e2f6b9 100644
--- a/lifetime_annotations/lifetime_symbol_table_test.cc
+++ b/lifetime_annotations/lifetime_symbol_table_test.cc
@@ -40,5 +40,30 @@
   EXPECT_EQ(table.LookupLifetime(b), "b");
 }
 
+TEST(LifetimeSymbolTableTest, LookupLifetimeAndMaybeDeclare) {
+  {
+    LifetimeSymbolTable table;
+
+    table.LookupNameAndMaybeDeclare("a");
+    table.LookupNameAndMaybeDeclare("c");
+
+    EXPECT_EQ(table.LookupLifetimeAndMaybeDeclare(Lifetime::CreateVariable()),
+              "b");
+    EXPECT_EQ(table.LookupLifetimeAndMaybeDeclare(Lifetime::CreateVariable()),
+              "d");
+  }
+
+  {
+    LifetimeSymbolTable table;
+    for (int i = 0; i < 26; ++i) {
+      table.LookupLifetimeAndMaybeDeclare(Lifetime::CreateVariable());
+    }
+    EXPECT_EQ(table.LookupLifetimeAndMaybeDeclare(Lifetime::CreateVariable()),
+              "aa");
+    EXPECT_EQ(table.LookupLifetimeAndMaybeDeclare(Lifetime::CreateVariable()),
+              "ab");
+  }
+}
+
 }  // namespace
 }  // namespace devtools_rust