@@ -125,6 +125,13 @@ class LifetimeTestHelper {
125
125
return Analysis.getExpiredLoansAtPoint (PP);
126
126
}
127
127
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
+
128
135
private:
129
136
template <typename DeclT> DeclT *findDecl (llvm::StringRef Name) {
130
137
auto &Ctx = Runner.getASTContext ();
@@ -162,6 +169,15 @@ class OriginInfo {
162
169
LifetimeTestHelper &Helper;
163
170
};
164
171
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
+
165
181
// / Matcher to verify the set of loans held by an origin at a specific
166
182
// / program point.
167
183
// /
@@ -232,6 +248,34 @@ MATCHER_P(AreExpiredAt, Annotation, "") {
232
248
ActualExpiredLoans, result_listener);
233
249
}
234
250
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
+
235
279
// Base test fixture to manage the runner and helper.
236
280
class LifetimeAnalysisTest : public ::testing::Test {
237
281
protected:
@@ -244,6 +288,13 @@ class LifetimeAnalysisTest : public ::testing::Test {
244
288
return OriginInfo (OriginVar, *Helper);
245
289
}
246
290
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
+
247
298
// / Factory function that hides the std::vector creation.
248
299
LoanSetInfo LoansTo (std::initializer_list<std::string> LoanVars) {
249
300
return LoanSetInfo ({LoanVars}, *Helper);
@@ -706,5 +757,90 @@ TEST_F(LifetimeAnalysisTest, ReassignedPointerThenOriginalExpires) {
706
757
EXPECT_THAT (LoansTo ({" s1" , " s2" }), AreExpiredAt (" p_after_s1_expires" ));
707
758
}
708
759
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
+
709
845
} // anonymous namespace
710
846
} // namespace clang::lifetimes::internal
0 commit comments