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
