Author: Utkarsh Saxena
Date: 2026-01-14T17:06:54+01:00
New Revision: 01934fb49597eddb4b513a96da4d97ba3521ef11

URL: 
https://github.com/llvm/llvm-project/commit/01934fb49597eddb4b513a96da4d97ba3521ef11
DIFF: 
https://github.com/llvm/llvm-project/commit/01934fb49597eddb4b513a96da4d97ba3521ef11.diff

LOG: [LifetimeSafety] See through implicit object arguments of GSL pointer type 
(#174741)

Add support for tracking pointer flows through GSL pointer implicit object 
arguments in method calls.

This PR enhances the lifetime safety analysis to properly track pointer flows 
through GSL pointer implicit object arguments in method calls. It adds a new 
helper function `shouldTrackPointerImplicitObjectArg` that determines if an 
implicit object argument should be tracked as a GSL pointer. When such an 
argument is detected, the analysis now correctly propagates the origin 
information through the pointer reference.

This resolves false-positives like

```cpp
const char* p;
{
  std::string_view a = getSomeView();
  p = a.data(); // 'a' does not live long enough (false-positive).
}
use(p);

```

Added: 
    

Modified: 
    clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
    clang/unittests/Analysis/LifetimeSafetyTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index fe5fabc6d6405..b10c61f1cb6b7 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -485,6 +485,14 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
     }
     return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
   };
+  auto shouldTrackPointerImplicitObjectArg = [FD](unsigned I) -> bool {
+    const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+    if (!Method || !Method->isInstance())
+      return false;
+    return I == 0 &&
+           isGslPointerType(Method->getFunctionObjectParameterType()) &&
+           shouldTrackImplicitObjectArg(Method);
+  };
   if (Args.empty())
     return;
   bool KillSrc = true;
@@ -505,6 +513,14 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
         flow(CallList, ArgList, KillSrc);
         KillSrc = false;
       }
+    } else if (shouldTrackPointerImplicitObjectArg(I)) {
+      assert(ArgList->getLength() >= 2 &&
+             "Object arg of pointer type should have atleast two origins");
+      // See through the GSLPointer reference to see the pointer's value.
+      CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
+          CallList->getOuterOriginID(),
+          ArgList->peelOuterOrigin()->getOuterOriginID(), KillSrc));
+      KillSrc = false;
     } else if (IsArgLifetimeBound(I)) {
       // Lifetimebound on a non-GSL-ctor function means the returned
       // pointer/reference itself must not outlive the arguments. This

diff  --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp 
b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 3afb44656d78a..f5e1ce3ae80ed 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -1751,6 +1751,57 @@ TEST_F(LifetimeAnalysisTest, 
TrackImplicitObjectArg_MapFind) {
   EXPECT_THAT(Origin("it"), HasLoansTo({"m"}, "p1"));
 }
 
+TEST_F(LifetimeAnalysisTest, TrackImplicitObjectArg_GSLPointerArg) {
+  SetupTest(R"(
+    namespace std {
+
+    template<typename T>
+    struct basic_string_view {
+      basic_string_view();
+      basic_string_view(const T *);
+      const T *begin() const;
+      const T *data() const;
+    };
+    using string_view = basic_string_view<char>;
+
+    template<typename T>
+    struct basic_string {
+      basic_string();
+      basic_string(const T *);
+      const T *c_str() const;
+      operator basic_string_view<T> () const;
+      const T *data() const;
+    };
+    using string = basic_string<char>;
+    }
+
+    void target() {
+      std::string s1;
+      std::string_view sv1 = s1;
+      
+      std::string s2;
+      const char* sv2 = std::string_view(s2).begin();
+      
+      std::string s3;
+      const char* sv3 = std::string_view(s3).data();
+      
+      std::string s4;
+      std::string_view sv4 = std::string_view{std::string_view(s4).data()};
+            
+      std::string s5;
+      const char* data5 = std::string_view(s5).data();
+      std::string_view sv5 = data5;
+      POINT(end);
+    }
+  )");
+  EXPECT_THAT(Origin("sv1"), HasLoansTo({"s1"}, "end"));
+  EXPECT_THAT(Origin("sv2"), HasLoansTo({"s2"}, "end"));
+  EXPECT_THAT(Origin("sv3"), HasLoansTo({"s3"}, "end"));
+  // FIXME: Handle GSL pointer construction from raw pointers.
+  EXPECT_THAT(Origin("sv4"), HasLoansTo({}, "end"));
+  EXPECT_THAT(Origin("sv5"), HasLoansTo({}, "end"));
+}
+
 // ========================================================================= //
 //                    Tests for shouldTrackFirstArgument
 // ========================================================================= //


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to