================
@@ -1202,5 +1284,198 @@ TEST_F(LifetimeAnalysisTest, LivenessOutsideLoop) {
   EXPECT_THAT(Origins({"p"}), MaybeLiveAt("p1"));
 }
 
+TEST_F(LifetimeAnalysisTest, SimpleReturnStackAddress) {
+  SetupTest(R"(
+    MyObj* target() {
+      MyObj s;
+      MyObj* p = &s;
+      POINT(p1);
+      return p;
+    }
+  )");
+  EXPECT_THAT("s", HasLiveLoanAtExpiry("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, ConditionalAssignUnconditionalReturn) {
+  SetupTest(R"(
+    MyObj* target(bool c) {
+      MyObj s1;
+      MyObj* p = nullptr;
+      if (c) {
+        p = &s1;
+      }
+      POINT(P);
+      return p;
+    }
+  )");
+  EXPECT_THAT("s1", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, MultipleAssignments) {
+  SetupTest(R"(
+    MyObj* target() {
+      MyObj s;
+      MyObj* p1 = &s;
+      MyObj* p2 = &s;
+      POINT(P);
+      return p2;
+    }
+  )");
+  // Test if atleast one loan to "s" is live;
+  EXPECT_THAT("s", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, ConditionalAssignBothBranches) {
+  SetupTest(R"(
+    MyObj* target(bool c) {
+      MyObj s1;
+      static MyObj s2;
+      MyObj* p = nullptr;
+      if (c) {
+        p = &s1;
+      } else {
+       p = &s2;
+      }
+      POINT(P);
+      return p;
+    }
+  )");
+  EXPECT_THAT("s1", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, ReassignFromSafeToLocalThenReturn) {
+  SetupTest(R"(
+    MyObj* target() {
+      static MyObj safe_obj;
+      MyObj local_obj;
+      MyObj* p = &safe_obj;
+
+      p = &local_obj;
+      POINT(P); 
+      return p;
+    }
+  )");
+  EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, PointerChainToLocal) {
+  SetupTest(R"(
+    MyObj* target() {
+      MyObj local_obj;
+      MyObj* p1 = &local_obj;
+      MyObj* p2 = p1;
+      POINT(P);
+      return p2;
+    }
+  )");
+  EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, MultipleAssignmentMultipleReturn) {
+  SetupTest(R"(
+    MyObj* target(bool c1, bool c2) {
+      static MyObj global_obj;
+      MyObj local_obj1;
+      MyObj local_obj2;
+      MyObj* p = nullptr;
+      if(c1){
+        p = &local_obj1;
+        POINT(C1);
+        return p;
+      }
+      else if(c2){
+        p = &local_obj2;
+        POINT(C2);
+        return p;
+      }
+      p = &global_obj;
+      POINT(C3);
+      return p;
+    }
+  )");
+
+  EXPECT_THAT("local_obj1", HasLiveLoanAtExpiry("C1"));
+  EXPECT_THAT("local_obj2", HasLiveLoanAtExpiry("C2"));
+
+  EXPECT_THAT("local_obj1", testing::Not(HasLiveLoanAtExpiry("C3")));
+  EXPECT_THAT("local_obj2", testing::Not(HasLiveLoanAtExpiry("C3")));
+}
+
+TEST_F(LifetimeAnalysisTest, MultipleAssignmentsSingleReturn) {
+  SetupTest(R"(
+    MyObj* target(bool c1, bool c2) {
+    static MyObj global_obj;
+      MyObj local_obj1;
+      MyObj local_obj2;
+      MyObj* p = nullptr;
+      if(c1){
+        p = &local_obj1;
+      }
+      else if(c2){
+        p = &local_obj2;
+      }
+      else{
+      p = &global_obj;
+      }
+      POINT(P);
+      return p;
+    }
+  )");
+  // Expect permissive warning at return statement with both local_obj1 and
+  // local_obj2 as culprit loans
+  EXPECT_THAT("local_obj1", HasLiveLoanAtExpiry("P"));
+  EXPECT_THAT("local_obj2", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, UseAfterScopeThenReturn) {
+  SetupTest(R"(
+    MyObj* target() {
+      MyObj* p;
+      {
+        MyObj local_obj;
+        p = &local_obj;
+        POINT(p1);
+      }
+      POINT(p2);
+      return p;
+    }
+  )");
+  // Expect a Use-after-scope warning because `p` in `return p` is a use even
+  // before return
+  EXPECT_THAT(Origin("p"), HasLoansTo({"local_obj"}, "p2"));
+  EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p2"));
+
+  EXPECT_THAT(Origin("p"), HasLoansTo({"local_obj"}, "p1"));
+  EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p1"));
+
+  EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("p2"));
+}
+
+TEST_F(LifetimeAnalysisTest, ReturnBeforeUseAfterScope) {
+  SetupTest(R"(
+    MyObj* target(bool c) {
+      MyObj* p;
+      static MyObj global_obj;
+      {
+        MyObj local_obj;
+        p = &local_obj;
+        if(c){
+          POINT(p1);
+          return p;
+        }
+      }
+      POINT(p2);
+      return &global_obj;
+    }
+  )");
+  // Expect a return stack address warning as return is before use after scope
+  EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("p1"));
+
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p2"));
----------------
kashika0112 wrote:

Yes, thank you for catching this. This should give a live origin for global_obj 
but in the test they are no Origins live at P2.

I ran a similar test case:
```
#include <iostream>
#include <string>

std::string_view target(bool c)
{
    std::string_view p;
    static std::string global_obj;
    {
        std::string_view local_obj;
        p = local_obj;
        if (c)
        {
            return p;
        }
    }
    return global_obj;
}
```
and the Facts generated looks like:

```
Function: target
  Block B4:
  End of Block
  Block B3:
    OriginFlow (Dest: 0 (Decl: p), Src: 1 (Expr: CXXConstructExpr))
    OriginFlow (Dest: 2 (Decl: local_obj), Src: 3 (Expr: CXXConstructExpr))
    Issue (0 (Path: operator=), ToOrigin: 4 (Expr: DeclRefExpr))
    OriginFlow (Dest: 5 (Expr: ImplicitCastExpr), Src: 4 (Expr: DeclRefExpr))
    Use (0 (Decl: p), Write)
    Use (2 (Decl: local_obj), Read)
    OriginFlow (Dest: 6 (Expr: ImplicitCastExpr), Src: 2 (Decl: local_obj))
    OriginFlow (Dest: 0 (Decl: p), Src: 6 (Expr: ImplicitCastExpr))
    Issue (1 (Path: c), ToOrigin: 7 (Expr: DeclRefExpr))
  End of Block
  Block B2:
    Use (0 (Decl: p), Read)
    OriginFlow (Dest: 8 (Expr: ImplicitCastExpr), Src: 0 (Decl: p))
    OriginFlow (Dest: 9 (Expr: CXXConstructExpr), Src: 8 (Expr: 
ImplicitCastExpr))
    OriginEscapes (9 (Expr: CXXConstructExpr))
  End of Block
  Block B1:
    Issue (2 (Path: global_obj), ToOrigin: 10 (Expr: DeclRefExpr))
    OriginFlow (Dest: 11 (Expr: ImplicitCastExpr), Src: 10 (Expr: DeclRefExpr))
    OriginFlow (Dest: 12 (Expr: CXXMemberCallExpr), Src: 11 (Expr: 
ImplicitCastExpr))
    OriginFlow (Dest: 13 (Expr: ImplicitCastExpr), Src: 12 (Expr: 
CXXMemberCallExpr))
    OriginEscapes (13 (Expr: ImplicitCastExpr))
  End of Block
  Block B0:
  End of Block
  ```
  `global_obj` is present in the OriginEscapes fact. 

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

Reply via email to