[nullability] Smart pointers: Support `absl_nullability_compatible` tag.

This allows tagging user-defined smart pointer types as supporting nullability
annotations.

PiperOrigin-RevId: 601341438
Change-Id: I48ab21128dc6b90f080bb8ae7697e0773e647fdd
diff --git a/bazel/llvm.bzl b/bazel/llvm.bzl
index 0064a25..fcd7f41 100644
--- a/bazel/llvm.bzl
+++ b/bazel/llvm.bzl
@@ -53,7 +53,7 @@
             executable = False,
         )
 
-LLVM_COMMIT_SHA = "c416b2efe89c11db593fe8041c366e0cb63d4eeb"
+LLVM_COMMIT_SHA = "98509c7f9792c79b05a41b95c24607f6dd489c5a"
 
 def llvm_loader_repository_dependencies():
     # This *declares* the dependency, but it won't actually be *downloaded* unless it's used.
diff --git a/nullability/test/smart_pointers.cc b/nullability/test/smart_pointers.cc
index 67f083e..387446a 100644
--- a/nullability/test/smart_pointers.cc
+++ b/nullability/test/smart_pointers.cc
@@ -536,3 +536,32 @@
   std::weak_ptr<int> Weak(Shared);
   nullable(Weak.lock());
 }
+
+namespace user_defined_smart_pointers {
+
+template <typename T>
+struct UserDefinedSmartPointer {
+  using absl_nullability_compatible = void;
+  using pointer = T *;
+
+  pointer get() const;
+};
+
+TEST void userDefinedSmartPointers(
+    Nonnull<UserDefinedSmartPointer<int>> NonnullParam,
+    Nullable<UserDefinedSmartPointer<int>> NullableParam,
+    UserDefinedSmartPointer<int> UnknownParam) {
+  // Just spot-check some basic behaviors, as the implementation treats
+  // user-defined smart pointers like standard smart pointers, so the tests for
+  // standard smart pointers provide sufficient coverage.
+
+  nonnull(NonnullParam);
+  nullable(NullableParam);
+  unknown(UnknownParam);
+
+  nonnull(NonnullParam.get());
+  nullable(NullableParam.get());
+  unknown(UnknownParam.get());
+}
+
+}  // namespace user_defined_smart_pointers
diff --git a/nullability/type_nullability.cc b/nullability/type_nullability.cc
index 2d1aebf..18b2534 100644
--- a/nullability/type_nullability.cc
+++ b/nullability/type_nullability.cc
@@ -52,20 +52,37 @@
   return !underlyingRawPointerType(T).isNull();
 }
 
+static bool isStandardSmartPointerDecl(const CXXRecordDecl *RD) {
+  if (!RD->getDeclContext()->isStdNamespace()) return false;
+
+  const IdentifierInfo *ID = RD->getIdentifier();
+  if (ID == nullptr) return false;
+
+  StringRef Name = ID->getName();
+  return Name == "unique_ptr" || Name == "shared_ptr";
+}
+
+static bool hasAbslNullabilityCompatibleTag(const CXXRecordDecl *RD) {
+  // If the specialization hasn't been instantiated -- for example because it
+  // is only used as a function parameter type -- then the specialization won't
+  // contain the `absl_nullability_compatible` tag. Therefore, we look at the
+  // template rather than the specialization.
+  if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+    RD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
+  const auto &Idents = RD->getASTContext().Idents;
+  auto It = Idents.find("absl_nullability_compatible");
+  if (It == Idents.end()) return false;
+  return RD->lookup(It->getValue()).find_first<TypedefNameDecl>() != nullptr;
+}
+
 QualType underlyingRawPointerType(QualType T) {
   if (!SmartPointersEnabled) return QualType();
 
-  // TODO(b/304963199): Add support for the `absl_nullability_compatible` tag.
   const CXXRecordDecl *RD = T.getCanonicalType()->getAsCXXRecordDecl();
   if (RD == nullptr) return QualType();
 
-  if (!RD->getDeclContext()->isStdNamespace()) return QualType();
-
-  const IdentifierInfo *ID = RD->getIdentifier();
-  if (ID == nullptr) return QualType();
-
-  StringRef Name = ID->getName();
-  if (Name != "unique_ptr" && Name != "shared_ptr") return QualType();
+  if (!isStandardSmartPointerDecl(RD) && !hasAbslNullabilityCompatibleTag(RD))
+    return QualType();
 
   const ASTContext &ASTCtx = RD->getASTContext();
   const auto &Idents = ASTCtx.Idents;
diff --git a/nullability/type_nullability_test.cc b/nullability/type_nullability_test.cc
index b530708..20bdda3 100644
--- a/nullability/type_nullability_test.cc
+++ b/nullability/type_nullability_test.cc
@@ -95,6 +95,12 @@
 
     using SugaredPointer = UniquePointer;
 
+    template <typename T>
+    struct UserDefinedSmartPointer {
+      using absl_nullability_compatible = void;
+    };
+    using UserDefined = UserDefinedSmartPointer<NotPointer>;
+
     template <class>
     struct Container;
     using ContainsPointers = Container<std::unique_ptr<int>>;
@@ -107,6 +113,7 @@
   EXPECT_FALSE(isSupportedSmartPointerType(
       underlying("UniquePointerWrongNamespace", AST)));
   EXPECT_TRUE(isSupportedSmartPointerType(underlying("SugaredPointer", AST)));
+  EXPECT_TRUE(isSupportedSmartPointerType(underlying("UserDefined", AST)));
   EXPECT_FALSE(
       isSupportedSmartPointerType(underlying("ContainsPointers", AST)));
 }
@@ -131,14 +138,22 @@
     };
     }  // namespace std
 
+    template <typename T>
+    struct UserDefinedSmartPointer {
+      using absl_nullability_compatible = void;
+      using pointer = char *;
+    };
+
     using UniquePointer = std::unique_ptr<int>;
     using SharedPointer = std::shared_ptr<int>;
+    using UserDefined = UserDefinedSmartPointer<int>;
     // Force the compiler to instantiate the templates. Otherwise, the
     // `ClassTemplateSpecializationDecl` won't contain a `TypedefNameDecl` for
     // `pointer` or `element_type`, and `underlyingRawPointerType()` will
     // use the fallback behavior of looking at the template argument.
     template class std::unique_ptr<int>;
     template class std::shared_ptr<int>;
+    template class UserDefinedSmartPointer<int>;
   )cpp");
 
   QualType PointerToCharTy = AST.context().getPointerType(AST.context().CharTy);
@@ -146,6 +161,8 @@
             PointerToCharTy);
   EXPECT_EQ(underlyingRawPointerType(underlying("SharedPointer", AST)),
             PointerToCharTy);
+  EXPECT_EQ(underlyingRawPointerType(underlying("UserDefined", AST)),
+            PointerToCharTy);
 }
 
 TEST_F(UnderlyingRawPointerTest, NotInstantiated) {