Thread `TypeLoc` through `FunctionLifetimes` and `ValueLifetimes`.

A followup CL will extract `annotate_type` attributes from the `TypeLoc`.

Note: One might ask why all this mechanism is necessary. Couldn't the attribute
arguments be placed on the `AttributedType`, so that we could extract them
without having to access the `TypeLoc`?

However, putting the attribute arguments on the `AttributedType` would violate
the conceptual distinction between `Type` and `TypeLoc`. The attribute arguments
are `Expr`s, which are associated with a specific location in the source code.
In contrast, a `Type` is intended to be independent of source location.

PiperOrigin-RevId: 456703847
diff --git a/lifetime_analysis/builtin_lifetimes.cc b/lifetime_analysis/builtin_lifetimes.cc
index e20577e..ee43929 100644
--- a/lifetime_analysis/builtin_lifetimes.cc
+++ b/lifetime_analysis/builtin_lifetimes.cc
@@ -26,13 +26,13 @@
 
 class ForwardAndMoveFactory : public FunctionLifetimeFactory {
   llvm::Expected<ValueLifetimes> CreateParamLifetimes(
-      clang::QualType type) const override {
+      clang::QualType type, clang::TypeLoc) const override {
     return ValueLifetimes::Create(
         type, [](const clang::Expr*) { return Lifetime::CreateVariable(); });
   }
 
   llvm::Expected<ValueLifetimes> CreateReturnLifetimes(
-      clang::QualType type,
+      clang::QualType type, clang::TypeLoc,
       const llvm::SmallVector<ValueLifetimes>& param_lifetimes,
       const std::optional<ValueLifetimes>& /*this_lifetimes*/) const override {
     assert(param_lifetimes.size() == 1);
diff --git a/lifetime_annotations/function_lifetimes.cc b/lifetime_annotations/function_lifetimes.cc
index 1ae177b..a1dbad7 100644
--- a/lifetime_annotations/function_lifetimes.cc
+++ b/lifetime_annotations/function_lifetimes.cc
@@ -28,14 +28,24 @@
       method && !method->isStatic()) {
     this_type = method->getThisType();
   }
-  return Create(func->getType()->getAs<clang::FunctionProtoType>(), this_type,
-                lifetime_factory);
+  clang::TypeLoc type_loc;
+  if (func->getTypeSourceInfo()) {
+    type_loc = func->getTypeSourceInfo()->getTypeLoc();
+  }
+  return Create(func->getType()->getAs<clang::FunctionProtoType>(), type_loc,
+                this_type, lifetime_factory);
+}
+
+llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateForFunctionType(
+    const clang::FunctionProtoType* func, clang::TypeLoc func_type_loc,
+    const FunctionLifetimeFactory& lifetime_factory) {
+  return Create(func, func_type_loc, clang::QualType(), lifetime_factory);
 }
 
 llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateForFunctionType(
     const clang::FunctionProtoType* func,
     const FunctionLifetimeFactory& lifetime_factory) {
-  return Create(func, clang::QualType(), lifetime_factory);
+  return CreateForFunctionType(func, clang::TypeLoc(), lifetime_factory);
 }
 
 llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateCopy(
@@ -95,33 +105,53 @@
 }
 
 llvm::Expected<FunctionLifetimes> FunctionLifetimes::Create(
-    const clang::FunctionProtoType* type, const clang::QualType this_type,
+    const clang::FunctionProtoType* type, clang::TypeLoc type_loc,
+    const clang::QualType this_type,
     const FunctionLifetimeFactory& lifetime_factory) {
   FunctionLifetimes ret;
 
   if (!this_type.isNull()) {
     ValueLifetimes tmp;
+    // TODO(mboehme): We don't have a `TypeLoc` for this because the `this` is
+    // never spelled out.
     if (llvm::Error err =
-            lifetime_factory.CreateParamLifetimes(this_type).moveInto(tmp)) {
+            lifetime_factory.CreateParamLifetimes(this_type, clang::TypeLoc())
+                .moveInto(tmp)) {
       return std::move(err);
     }
     ret.this_lifetimes_ = std::move(tmp);
   }
 
+  clang::FunctionTypeLoc func_type_loc;
+  if (type_loc) {
+    func_type_loc = type_loc.getAsAdjusted<clang::FunctionTypeLoc>();
+  }
   ret.param_lifetimes_.reserve(type->getNumParams());
   for (size_t i = 0; i < type->getNumParams(); i++) {
+    clang::TypeLoc param_type_loc;
+    if (type_loc) {
+      const clang::ParmVarDecl* param = func_type_loc.getParam(i);
+      if (param && param->getTypeSourceInfo()) {
+        param_type_loc = param->getTypeSourceInfo()->getTypeLoc();
+      }
+    }
     ValueLifetimes tmp;
     if (llvm::Error err =
-            lifetime_factory.CreateParamLifetimes(type->getParamType(i))
+            lifetime_factory
+                .CreateParamLifetimes(type->getParamType(i), param_type_loc)
                 .moveInto(tmp)) {
       return std::move(err);
     }
     ret.param_lifetimes_.push_back(std::move(tmp));
   }
 
+  clang::TypeLoc return_type_loc;
+  if (func_type_loc) {
+    return_type_loc = func_type_loc.getReturnLoc();
+  }
   if (llvm::Error err =
           lifetime_factory
-              .CreateReturnLifetimes(type->getReturnType(),
+              .CreateReturnLifetimes(type->getReturnType(), return_type_loc,
                                      ret.param_lifetimes_, ret.this_lifetimes_)
               .moveInto(ret.return_lifetimes_)) {
     return std::move(err);
diff --git a/lifetime_annotations/function_lifetimes.h b/lifetime_annotations/function_lifetimes.h
index 6876b9e..960a24c 100644
--- a/lifetime_annotations/function_lifetimes.h
+++ b/lifetime_annotations/function_lifetimes.h
@@ -29,10 +29,14 @@
 class FunctionLifetimeFactory {
  public:
   virtual ~FunctionLifetimeFactory() {}
+
+  // Note: The `type_loc` parameter passed into `CreateParamLifetimes` and
+  // `CreateReturnLifetimes` may be null if no type location is available.
+
   virtual llvm::Expected<ValueLifetimes> CreateParamLifetimes(
-      clang::QualType type) const = 0;
+      clang::QualType type, clang::TypeLoc type_loc) const = 0;
   virtual llvm::Expected<ValueLifetimes> CreateReturnLifetimes(
-      clang::QualType type,
+      clang::QualType type, clang::TypeLoc type_loc,
       const llvm::SmallVector<ValueLifetimes>& param_lifetimes,
       const std::optional<ValueLifetimes>& this_lifetimes) const = 0;
 };
@@ -43,14 +47,14 @@
   FunctionLifetimeFactorySingleCallback(LifetimeFactory factory)
       : factory_(std::move(factory)) {}
   llvm::Expected<ValueLifetimes> CreateParamLifetimes(
-      clang::QualType type) const override {
-    return ValueLifetimes::Create(type, factory_);
+      clang::QualType type, clang::TypeLoc type_loc) const override {
+    return ValueLifetimes::Create(type, type_loc, factory_);
   }
   llvm::Expected<ValueLifetimes> CreateReturnLifetimes(
-      clang::QualType type,
+      clang::QualType type, clang::TypeLoc type_loc,
       const llvm::SmallVector<ValueLifetimes>& /*param_lifetimes*/,
       const std::optional<ValueLifetimes>& /*this_lifetimes*/) const override {
-    return ValueLifetimes::Create(type, factory_);
+    return ValueLifetimes::Create(type, type_loc, factory_);
   }
 
  private:
@@ -90,6 +94,9 @@
       const FunctionLifetimeFactory& lifetime_factory);
 
   static llvm::Expected<FunctionLifetimes> CreateForFunctionType(
+      const clang::FunctionProtoType* function, clang::TypeLoc func_type_loc,
+      const FunctionLifetimeFactory& lifetime_factory);
+  static llvm::Expected<FunctionLifetimes> CreateForFunctionType(
       const clang::FunctionProtoType* function,
       const FunctionLifetimeFactory& lifetime_factory);
 
@@ -148,7 +155,8 @@
   std::optional<ValueLifetimes> this_lifetimes_;
 
   static llvm::Expected<FunctionLifetimes> Create(
-      const clang::FunctionProtoType* type, const clang::QualType this_type,
+      const clang::FunctionProtoType* type, clang::TypeLoc type_loc,
+      const clang::QualType this_type,
       const FunctionLifetimeFactory& lifetime_factory);
 };
 
diff --git a/lifetime_annotations/lifetime_annotations.cc b/lifetime_annotations/lifetime_annotations.cc
index ae4c7f0..d3e4021 100644
--- a/lifetime_annotations/lifetime_annotations.cc
+++ b/lifetime_annotations/lifetime_annotations.cc
@@ -153,7 +153,7 @@
 
    private:
     llvm::Expected<ValueLifetimes> CreateParamLifetimes(
-        clang::QualType param_type) const override {
+        clang::QualType param_type, clang::TypeLoc) const override {
       // TODO(mboehme): parse lifetime annotations from `type` if present.
       return ValueLifetimes::Create(
           param_type, [this](const clang::Expr*) -> llvm::Expected<Lifetime> {
@@ -204,7 +204,7 @@
     }
 
     llvm::Expected<ValueLifetimes> CreateReturnLifetimes(
-        clang::QualType return_type,
+        clang::QualType return_type, clang::TypeLoc,
         const llvm::SmallVector<ValueLifetimes>& param_lifetimes,
         const std::optional<ValueLifetimes>& this_lifetimes) const override {
       // TODO(mboehme): parse lifetime annotations from `type` if present.
diff --git a/lifetime_annotations/pointee_type.cc b/lifetime_annotations/pointee_type.cc
index f33d48b..40ede20 100644
--- a/lifetime_annotations/pointee_type.cc
+++ b/lifetime_annotations/pointee_type.cc
@@ -18,6 +18,19 @@
   return clang::QualType();
 }
 
+clang::TypeLoc PointeeTypeLoc(clang::TypeLoc type_loc) {
+  type_loc = type_loc.getUnqualifiedLoc();
+
+  if (auto pointer_type_loc = type_loc.getAs<clang::PointerTypeLoc>()) {
+    return pointer_type_loc.getPointeeLoc();
+  } else if (auto reference_type_loc =
+                 type_loc.getAs<clang::ReferenceTypeLoc>()) {
+    return reference_type_loc.getPointeeLoc();
+  }
+
+  return clang::TypeLoc();
+}
+
 }  // namespace lifetimes
 }  // namespace tidy
 }  // namespace clang
diff --git a/lifetime_annotations/pointee_type.h b/lifetime_annotations/pointee_type.h
index b68d1ed..b340e6e 100644
--- a/lifetime_annotations/pointee_type.h
+++ b/lifetime_annotations/pointee_type.h
@@ -6,6 +6,7 @@
 #define CRUBIT_LIFETIME_ANNOTATIONS_POINTEE_TYPE_H_
 
 #include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
 
 namespace clang {
 namespace tidy {
@@ -20,6 +21,9 @@
 // lifetimes for it".
 clang::QualType PointeeType(clang::QualType type);
 
+// Analogous to `PointeeType` but operates on a `TypeLoc`.
+clang::TypeLoc PointeeTypeLoc(clang::TypeLoc type_loc);
+
 }  // namespace lifetimes
 }  // namespace tidy
 }  // namespace clang
diff --git a/lifetime_annotations/type_lifetimes.cc b/lifetime_annotations/type_lifetimes.cc
index 790ace4..b8237e2 100644
--- a/lifetime_annotations/type_lifetimes.cc
+++ b/lifetime_annotations/type_lifetimes.cc
@@ -126,27 +126,54 @@
 namespace {
 
 llvm::Error ForEachTemplateArgument(
-    clang::QualType type,
-    const std::function<llvm::Error(int, clang::QualType)>& callback) {
+    clang::QualType type, clang::TypeLoc type_loc,
+    const std::function<llvm::Error(int, clang::QualType, clang::TypeLoc)>&
+        callback) {
   llvm::SmallVector<llvm::ArrayRef<clang::TemplateArgument>> template_args =
       GetTemplateArgs(type);
-  for (size_t depth = 0; depth < template_args.size(); depth++) {
+  std::optional<llvm::SmallVector<llvm::SmallVector<clang::TypeLoc>>>
+      maybe_template_arg_locs;
+  if (type_loc) {
+    maybe_template_arg_locs = GetTemplateArgs(type_loc);
+  }
+  llvm::SmallVector<llvm::SmallVector<clang::TypeLoc>> template_arg_locs;
+  if (maybe_template_arg_locs) {
+    template_arg_locs = *std::move(maybe_template_arg_locs);
+  } else {
+    // Fill all of `template_arg_locs` with empty `TypeLoc`s so we don't need to
+    // make any case distinctions below.
+    template_arg_locs.resize(template_args.size());
+    for (size_t depth = 0; depth < template_args.size(); depth++) {
+      template_arg_locs[depth].resize(template_args[depth].size());
+    }
+  }
+  assert(template_arg_locs.size() == template_args.size());
+  for (int depth = 0; depth < template_args.size(); depth++) {
     const auto& args_at_depth = template_args[depth];
-    for (const clang::TemplateArgument& arg : args_at_depth) {
+    const auto& arg_locs_at_depth = template_arg_locs[depth];
+    assert(args_at_depth.size() == arg_locs_at_depth.size());
+    for (size_t i = 0; i < args_at_depth.size(); ++i) {
+      const clang::TemplateArgument& arg = args_at_depth[i];
       if (arg.getKind() == clang::TemplateArgument::Type) {
-        if (llvm::Error err = callback(depth, arg.getAsType())) {
+        if (llvm::Error err =
+                callback(depth, arg.getAsType(), arg_locs_at_depth[i])) {
           return err;
         }
       } else if (arg.getKind() == clang::TemplateArgument::Pack) {
         for (const clang::TemplateArgument& inner_arg : arg.getPackAsArray()) {
           if (inner_arg.getKind() == clang::TemplateArgument::Type) {
-            if (llvm::Error err = callback(depth, inner_arg.getAsType())) {
+            // TODO(mboehme): Pass on the correct TypeLoc() in this case (if
+            // that's even possible -- I have to admit I'm not sure about how
+            // this case would get triggered)
+            if (llvm::Error err =
+                    callback(depth, inner_arg.getAsType(), clang::TypeLoc())) {
               return err;
             }
           }
         }
       } else {
-        if (llvm::Error err = callback(depth, clang::QualType())) {
+        if (llvm::Error err =
+                callback(depth, clang::QualType(), clang::TypeLoc())) {
           return err;
         }
       }
@@ -155,11 +182,35 @@
   return llvm::Error::success();
 }
 
+QualType Undecay(clang::QualType type) {
+  if (auto decayed = type->getAs<clang::DecayedType>()) {
+    return decayed->getOriginalType();
+  }
+  return type;
+}
+
+bool SameType(clang::QualType type1, clang::QualType type2) {
+  // If both types are an AutoType, ignore the actual type and assume they're
+  // the same.
+  // An `AutoType` that came from a TypeLoc will have type `auto` (i.e. as
+  // written), whereas an `AutoType` that didn't come from a `TypeLoc` will be
+  // the actual deduced type. We still want these to compare equal though.
+  if (type1->getAs<clang::AutoType>() && type2->getAs<clang::AutoType>()) {
+    return true;
+  }
+  return Undecay(type1) == Undecay(type2);
+}
+
 }  // namespace
 
 llvm::Expected<ValueLifetimes> ValueLifetimes::Create(
-    clang::QualType type, LifetimeFactory lifetime_factory) {
+    clang::QualType type, clang::TypeLoc type_loc,
+    LifetimeFactory lifetime_factory) {
   assert(!type.isNull());
+  if (type_loc) {
+    assert(SameType(type_loc.getType(), type));
+  }
+
   type = type.IgnoreParens();
   ValueLifetimes ret(type);
 
@@ -168,8 +219,9 @@
     // parameter and return lifetimes.
     FunctionLifetimeFactorySingleCallback factory(lifetime_factory);
     FunctionLifetimes fn_lftm;
-    if (llvm::Error err = FunctionLifetimes::CreateForFunctionType(fn, factory)
-                              .moveInto(fn_lftm)) {
+    if (llvm::Error err =
+            FunctionLifetimes::CreateForFunctionType(fn, type_loc, factory)
+                .moveInto(fn_lftm)) {
       return std::move(err);
     }
     ret.function_lifetimes_ =
@@ -188,14 +240,16 @@
 
   // Add implicit lifetime parameters for type template parameters.
   if (llvm::Error err = ForEachTemplateArgument(
-          type,
-          [&ret, &lifetime_factory](int depth,
-                                    clang::QualType arg_type) -> llvm::Error {
+          type, type_loc,
+          [&ret, &lifetime_factory](
+              int depth, clang::QualType arg_type,
+              clang::TypeLoc arg_type_loc) -> llvm::Error {
             std::optional<ValueLifetimes> maybe_template_arg_lifetime;
             if (!arg_type.isNull()) {
               maybe_template_arg_lifetime.emplace();
               if (llvm::Error err =
-                      ValueLifetimes::Create(arg_type, lifetime_factory)
+                      ValueLifetimes::Create(arg_type, arg_type_loc,
+                                             lifetime_factory)
                           .moveInto(*maybe_template_arg_lifetime)) {
                 return err;
               }
@@ -212,31 +266,35 @@
 
   clang::QualType pointee = PointeeType(type);
   if (pointee.isNull()) return ret;
-  ObjectLifetimes obj_lftm;
-  if (llvm::Error err = ObjectLifetimes::Create(pointee, lifetime_factory)
-                            .moveInto(obj_lftm)) {
+
+  clang::TypeLoc pointee_type_loc;
+  if (type_loc) {
+    pointee_type_loc = PointeeTypeLoc(type_loc);
+    // Note: We can't assert that `pointee_type_loc` is non-null here. If
+    // `type_loc` is a `TypedefTypeLoc`, then there will be no `TypeLoc` for
+    // the pointee type because the pointee type never got spelled out at the
+    // location of the original `TypeLoc`.
+  }
+
+  ValueLifetimes value_lifetimes;
+  if (llvm::Error err =
+          ValueLifetimes::Create(pointee, pointee_type_loc, lifetime_factory)
+              .moveInto(value_lifetimes)) {
+    return std::move(err);
+  }
+
+  Lifetime object_lifetime;
+  // TODO(mboehme): Pass the correct lifetime name.
+  const clang::Expr* lifetime_name = nullptr;
+  if (llvm::Error err =
+          lifetime_factory(lifetime_name).moveInto(object_lifetime)) {
     return std::move(err);
   }
   ret.pointee_lifetimes_ =
-      std::make_unique<ObjectLifetimes>(std::move(obj_lftm));
+      std::make_unique<ObjectLifetimes>(object_lifetime, value_lifetimes);
   return ret;
 }
 
-llvm::Expected<ObjectLifetimes> ObjectLifetimes::Create(
-    clang::QualType type, LifetimeFactory lifetime_factory) {
-  ValueLifetimes v;
-  if (llvm::Error err =
-          ValueLifetimes::Create(type, lifetime_factory).moveInto(v)) {
-    return std::move(err);
-  }
-  Lifetime l;
-  // TODO(mboehme): Pass lifetime name.
-  if (llvm::Error err = lifetime_factory(nullptr).moveInto(l)) {
-    return std::move(err);
-  }
-  return ObjectLifetimes(l, v);
-}
-
 ValueLifetimes ValueLifetimes::ForLifetimeLessType(clang::QualType type) {
   assert(PointeeType(type).isNull() && !type->isRecordType());
   return ValueLifetimes(type);
diff --git a/lifetime_annotations/type_lifetimes.h b/lifetime_annotations/type_lifetimes.h
index f440970..e0d83ec 100644
--- a/lifetime_annotations/type_lifetimes.h
+++ b/lifetime_annotations/type_lifetimes.h
@@ -68,8 +68,21 @@
   // Creates a ValueLifetimes for a *value* of a given type.
   // Only fails if lifetime_factory fails.
   // Lifetimes will be created in post-order in the tree of lifetimes.
+  // If a non-null `type_loc` is passed in, passes any lifetime names
+  // found in `annotate_type` attributes on to `lifetime_factory`.
+  // Note: It's necessary to pass in both `type` and `type_loc` because we may
+  // not be able to traverse as deeply into `type_loc` as we can into `type`.
+  // For example, if the type is a typedef for a pointer type, we can traverse
+  // into that pointer type through `type`, but not through `type_loc`. This is
+  // because the pointer type is not spelled out in the place where the
+  // `type_loc` appears in the source code.
   static llvm::Expected<ValueLifetimes> Create(
-      clang::QualType type, LifetimeFactory lifetime_factory);
+      clang::QualType type, clang::TypeLoc type_loc,
+      LifetimeFactory lifetime_factory);
+  static llvm::Expected<ValueLifetimes> Create(
+      clang::QualType type, LifetimeFactory lifetime_factory) {
+    return Create(type, clang::TypeLoc(), lifetime_factory);
+  }
 
   // Returns a ValueLifetimes for a lifetime-less type.
   // `type` must not be a pointer-like type or a record type.