@@ -125,6 +125,13 @@ class LifetimeTestHelper {
125125 return Analysis.getExpiredLoansAtPoint (PP);
126126 }
127127
128+ std::optional<OriginSet> getLiveOriginsAtPoint (llvm::StringRef Annotation) {
129+ ProgramPoint PP = Runner.getProgramPoint (Annotation);
130+ if (!PP)
131+ return std::nullopt ;
132+ return Analysis.getLiveOriginsAtPoint (PP);
133+ }
134+
128135private:
129136 template <typename DeclT> DeclT *findDecl (llvm::StringRef Name) {
130137 auto &Ctx = Runner.getASTContext ();
@@ -162,6 +169,15 @@ class OriginInfo {
162169 LifetimeTestHelper &Helper;
163170};
164171
172+ // A helper class to represent a set of origins, identified by variable names.
173+ class OriginsInfo {
174+ public:
175+ OriginsInfo (const std::vector<std::string> &Vars, LifetimeTestHelper &H)
176+ : OriginVars(Vars), Helper(H) {}
177+ std::vector<std::string> OriginVars;
178+ LifetimeTestHelper &Helper;
179+ };
180+
165181// / Matcher to verify the set of loans held by an origin at a specific
166182// / program point.
167183// /
@@ -232,6 +248,34 @@ MATCHER_P(AreExpiredAt, Annotation, "") {
232248 ActualExpiredLoans, result_listener);
233249}
234250
251+ // / Matcher to verify the complete set of live origins at a program point.
252+ MATCHER_P (AreLiveAt, Annotation, " " ) {
253+ const OriginsInfo &Info = arg;
254+ auto &Helper = Info.Helper ;
255+
256+ auto ActualLiveSetOpt = Helper.getLiveOriginsAtPoint (Annotation);
257+ if (!ActualLiveSetOpt) {
258+ *result_listener << " could not get a valid live origin set at point '"
259+ << Annotation << " '" ;
260+ return false ;
261+ }
262+ std::vector<OriginID> ActualLiveOrigins (ActualLiveSetOpt->begin (),
263+ ActualLiveSetOpt->end ());
264+
265+ std::vector<OriginID> ExpectedLiveOrigins;
266+ for (const auto &VarName : Info.OriginVars ) {
267+ auto OriginIDOpt = Helper.getOriginForDecl (VarName);
268+ if (!OriginIDOpt) {
269+ *result_listener << " could not find an origin for variable '" << VarName
270+ << " '" ;
271+ return false ;
272+ }
273+ ExpectedLiveOrigins.push_back (*OriginIDOpt);
274+ }
275+ return ExplainMatchResult (UnorderedElementsAreArray (ExpectedLiveOrigins),
276+ ActualLiveOrigins, result_listener);
277+ }
278+
235279// Base test fixture to manage the runner and helper.
236280class LifetimeAnalysisTest : public ::testing::Test {
237281protected:
@@ -244,6 +288,13 @@ class LifetimeAnalysisTest : public ::testing::Test {
244288 return OriginInfo (OriginVar, *Helper);
245289 }
246290
291+ // / Factory function that hides the std::vector creation.
292+ OriginsInfo Origins (std::initializer_list<std::string> OriginVars) {
293+ return OriginsInfo ({OriginVars}, *Helper);
294+ }
295+
296+ OriginsInfo NoOrigins () { return Origins ({}); }
297+
247298 // / Factory function that hides the std::vector creation.
248299 LoanSetInfo LoansTo (std::initializer_list<std::string> LoanVars) {
249300 return LoanSetInfo ({LoanVars}, *Helper);
@@ -706,5 +757,90 @@ TEST_F(LifetimeAnalysisTest, ReassignedPointerThenOriginalExpires) {
706757 EXPECT_THAT (LoansTo ({" s1" , " s2" }), AreExpiredAt (" p_after_s1_expires" ));
707758}
708759
760+ TEST_F (LifetimeAnalysisTest, LivenessDeadPointer) {
761+ SetupTest (R"(
762+ void target() {
763+ POINT(p2);
764+ MyObj s;
765+ MyObj* p = &s;
766+ POINT(p1);
767+ }
768+ )" );
769+ EXPECT_THAT (NoOrigins (), AreLiveAt (" p1" ));
770+ EXPECT_THAT (NoOrigins (), AreLiveAt (" p2" ));
771+ }
772+
773+ TEST_F (LifetimeAnalysisTest, LivenessSimpleReturn) {
774+ SetupTest (R"(
775+ MyObj* target() {
776+ MyObj s;
777+ MyObj* p = &s;
778+ POINT(p1);
779+ return p;
780+ }
781+ )" );
782+ EXPECT_THAT (Origins ({" p" }), AreLiveAt (" p1" ));
783+ }
784+
785+ TEST_F (LifetimeAnalysisTest, LivenessKilledByReassignment) {
786+ SetupTest (R"(
787+ MyObj* target() {
788+ MyObj s1, s2;
789+ MyObj* p = &s1;
790+ POINT(p1);
791+ p = &s2;
792+ POINT(p2);
793+ return p;
794+ }
795+ )" );
796+ EXPECT_THAT (Origins ({" p" }), AreLiveAt (" p2" ));
797+ EXPECT_THAT (NoOrigins (), AreLiveAt (" p1" ));
798+ }
799+
800+ TEST_F (LifetimeAnalysisTest, LivenessAcrossBranches) {
801+ SetupTest (R"(
802+ MyObj* target(bool c) {
803+ MyObj x, y;
804+ MyObj* p = nullptr;
805+ POINT(p1);
806+ if (c) {
807+ p = &x;
808+ POINT(p2);
809+ } else {
810+ p = &y;
811+ POINT(p3);
812+ }
813+ return p;
814+ }
815+ )" );
816+ EXPECT_THAT (Origins ({" p" }), AreLiveAt (" p2" ));
817+ EXPECT_THAT (Origins ({" p" }), AreLiveAt (" p3" ));
818+ // Before the `if`, the value of `p` (`nullptr`) is always overwritten before
819+ EXPECT_THAT (NoOrigins (), AreLiveAt (" p1" ));
820+ }
821+
822+ TEST_F (LifetimeAnalysisTest, LivenessInLoop) {
823+ SetupTest (R"(
824+ MyObj* target(bool c) {
825+ MyObj s1, s2;
826+ MyObj* p = &s1;
827+ MyObj* q = &s2;
828+ POINT(p1);
829+ while(c) {
830+ POINT(p2);
831+ p = q;
832+ POINT(p3);
833+ }
834+ POINT(p4);
835+ return p;
836+ }
837+ )" );
838+
839+ EXPECT_THAT (Origins ({" p" }), AreLiveAt (" p4" ));
840+ EXPECT_THAT (Origins ({" p" , " q" }), AreLiveAt (" p3" ));
841+ EXPECT_THAT (Origins ({" q" }), AreLiveAt (" p2" ));
842+ EXPECT_THAT (Origins ({" p" , " q" }), AreLiveAt (" p1" ));
843+ }
844+
709845} // anonymous namespace
710846} // namespace clang::lifetimes::internal
0 commit comments