// RUN: %clang_cc1 -fexperimental-lifetime-safety -mllvm -debug-only=LifetimeFacts -Wexperimental-lifetime-safety %s 2>&1 | FileCheck %s
// REQUIRES: asserts

struct MyObj {
  int id;
  ~MyObj() {} // Non-trivial destructor
};

// Simple Local Variable Address and Return
// CHECK-LABEL: Function: return_local_addr
MyObj* return_local_addr() {
  MyObj x {10};
// CHECK: Block B{{[0-9]+}}:
// CHECK:   Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] (Expr: DeclRefExpr, Decl: x))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_DRE_X]] (Expr: DeclRefExpr, Decl: x)
  MyObj* p = &x;
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_P:[0-9]+]] (Decl: p, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_ADDR_X]] (Expr: UnaryOperator, Type : MyObj *)
// CHECK:   Use ([[O_P]] (Decl: p, Type : MyObj *), Read)
  return p;
// CHECK:   Issue ({{[0-9]+}} (Path: p), ToOrigin: {{[0-9]+}} (Expr: DeclRefExpr, Decl: p))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_P]] (Decl: p, Type : MyObj *)
// CHECK:   Expire ([[L_X]] (Path: x))
// CHECK:   Expire ({{[0-9]+}} (Path: p))
// CHECK:   OriginEscapes ([[O_RET_VAL]] (Expr: ImplicitCastExpr, Type : MyObj *))
}

// Loan Expiration (Automatic Variable, C++)
// CHECK-LABEL: Function: loan_expires_cpp
void loan_expires_cpp() {
  MyObj obj{1};
// CHECK: Block B{{[0-9]+}}:
// CHECK:   Issue ([[L_OBJ:[0-9]+]] (Path: obj), ToOrigin: [[O_DRE_OBJ:[0-9]+]] (Expr: DeclRefExpr, Decl: obj))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_ADDR_OBJ:[0-9]+]] (Expr: UnaryOperator, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_DRE_OBJ]] (Expr: DeclRefExpr, Decl: obj)
  MyObj* pObj = &obj;
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Decl: pObj, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_ADDR_OBJ]] (Expr: UnaryOperator, Type : MyObj *)
// CHECK:   Expire ([[L_OBJ]] (Path: obj))
}


// CHECK-LABEL: Function: loan_expires_trivial
void loan_expires_trivial() {
  int trivial_obj = 1;
// CHECK: Block B{{[0-9]+}}:
// CHECK:   Issue ([[L_TRIVIAL_OBJ:[0-9]+]] (Path: trivial_obj), ToOrigin: [[O_DRE_TRIVIAL:[0-9]+]] (Expr: DeclRefExpr, Decl: trivial_obj))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_ADDR_TRIVIAL_OBJ:[0-9]+]] (Expr: UnaryOperator, Type : int *)
// CHECK-NEXT:       Src:  [[O_DRE_TRIVIAL]] (Expr: DeclRefExpr, Decl: trivial_obj)
  int* pTrivialObj = &trivial_obj;
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Decl: pTrivialObj, Type : int *)
// CHECK-NEXT:       Src:  [[O_ADDR_TRIVIAL_OBJ]] (Expr: UnaryOperator, Type : int *)
// CHECK:   Expire ([[L_TRIVIAL_OBJ]] (Path: trivial_obj))
// CHECK-NEXT: End of Block
}

// CHECK-LABEL: Function: overwrite_origin
void overwrite_origin() {
  MyObj s1;
  MyObj s2;
// CHECK: Block B{{[0-9]+}}:
  MyObj* p = &s1;
// CHECK:   Issue ([[L_S1:[0-9]+]] (Path: s1), ToOrigin: [[O_DRE_S1:[0-9]+]] (Expr: DeclRefExpr, Decl: s1))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_DRE_S1]] (Expr: DeclRefExpr, Decl: s1)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_P:[0-9]+]] (Decl: p, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_ADDR_S1]] (Expr: UnaryOperator, Type : MyObj *)
  p = &s2;
// CHECK:   Issue ([[L_S2:[0-9]+]] (Path: s2), ToOrigin: [[O_DRE_S2:[0-9]+]] (Expr: DeclRefExpr, Decl: s2))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_ADDR_S2:[0-9]+]] (Expr: UnaryOperator, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_DRE_S2]] (Expr: DeclRefExpr, Decl: s2)
// CHECK:   Use ([[O_P]] (Decl: p, Type : MyObj *), Write)
// CHECK:   Issue ({{[0-9]+}} (Path: p), ToOrigin: {{[0-9]+}} (Expr: DeclRefExpr, Decl: p))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_P]] (Decl: p, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_ADDR_S2]] (Expr: UnaryOperator, Type : MyObj *)
// CHECK:   Expire ([[L_S2]] (Path: s2))
// CHECK:   Expire ([[L_S1]] (Path: s1))
}

// CHECK-LABEL: Function: reassign_to_null
void reassign_to_null() {
  MyObj s1;
// CHECK: Block B{{[0-9]+}}:
  MyObj* p = &s1;
// CHECK:   Issue ([[L_S1:[0-9]+]] (Path: s1), ToOrigin: [[O_DRE_S1:[0-9]+]] (Expr: DeclRefExpr, Decl: s1))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_DRE_S1]] (Expr: DeclRefExpr, Decl: s1)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_P:[0-9]+]] (Decl: p, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_ADDR_S1]] (Expr: UnaryOperator, Type : MyObj *)
  p = nullptr;
// CHECK:   Use ([[O_P]] (Decl: p, Type : MyObj *), Write)
// CHECK:   Issue ({{[0-9]+}} (Path: p), ToOrigin: {{[0-9]+}} (Expr: DeclRefExpr, Decl: p))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_P]] (Decl: p, Type : MyObj *)
// CHECK-NEXT:       Src:  {{[0-9]+}} (Expr: ImplicitCastExpr, Type : MyObj *)
// CHECK:   Expire ([[L_S1]] (Path: s1))
}
// FIXME: Have a better representation for nullptr than just an empty origin. 
//        It should be a separate loan and origin kind.

// CHECK-LABEL: Function: pointer_indirection
void pointer_indirection() {
  int a;
  int *p = &a;
// CHECK: Block B{{[0-9]+}}:
// CHECK:   Issue ([[L_A:[0-9]+]] (Path: a), ToOrigin: [[O_DRE_A:[0-9]+]] (Expr: DeclRefExpr, Decl: a))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_ADDR_A:[0-9]+]] (Expr: UnaryOperator, Type : int *)
// CHECK-NEXT:       Src:  [[O_DRE_A]] (Expr: DeclRefExpr, Decl: a)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_P:[0-9]+]] (Decl: p, Type : int *)
// CHECK-NEXT:       Src:  [[O_ADDR_A]] (Expr: UnaryOperator, Type : int *)
  int **pp = &p;
// CHECK:   Use ([[O_P]] (Decl: p, Type : int *), Read)
// CHECK:   Issue ({{[0-9]+}} (Path: p), ToOrigin: {{[0-9]+}} (Expr: DeclRefExpr, Decl: p))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Expr: UnaryOperator, Type : int **)
// CHECK-NEXT:       Src:  {{[0-9]+}} (Expr: DeclRefExpr, Decl: p)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Expr: UnaryOperator, Type : int *)
// CHECK-NEXT:       Src:  [[O_P]] (Decl: p, Type : int *)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_PP_OUTER:[0-9]+]] (Decl: pp, Type : int **)
// CHECK-NEXT:       Src:  {{[0-9]+}} (Expr: UnaryOperator, Type : int **)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_PP_INNER:[0-9]+]] (Decl: pp, Type : int *)
// CHECK-NEXT:       Src:  {{[0-9]+}} (Expr: UnaryOperator, Type : int *)
  
  // FIXME: Propagate origins across dereference unary operator*
  int *q = *pp;
// CHECK:   Use ([[O_PP_OUTER]] (Decl: pp, Type : int **), [[O_PP_INNER]] (Decl: pp, Type : int *), Read)
// CHECK:   Issue ({{[0-9]+}} (Path: pp), ToOrigin: {{[0-9]+}} (Expr: DeclRefExpr, Decl: pp))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Expr: ImplicitCastExpr, Type : int **)
// CHECK-NEXT:       Src:  [[O_PP_OUTER]] (Decl: pp, Type : int **)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Expr: ImplicitCastExpr, Type : int *)
// CHECK-NEXT:       Src:  [[O_PP_INNER]] (Decl: pp, Type : int *)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Expr: UnaryOperator, Type : int *&)
// CHECK-NEXT:       Src:  {{[0-9]+}} (Expr: ImplicitCastExpr, Type : int **)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Expr: UnaryOperator, Type : int *)
// CHECK-NEXT:       Src:  {{[0-9]+}} (Expr: ImplicitCastExpr, Type : int *)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Expr: ImplicitCastExpr, Type : int *)
// CHECK-NEXT:       Src:  {{[0-9]+}} (Expr: UnaryOperator, Type : int *)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Decl: q, Type : int *)
// CHECK-NEXT:       Src:  {{[0-9]+}} (Expr: ImplicitCastExpr, Type : int *)
}

// CHECK-LABEL: Function: test_use_lifetimebound_call
MyObj* LifetimeBoundCall(MyObj* x [[clang::lifetimebound]], MyObj* y [[clang::lifetimebound]]);
void test_use_lifetimebound_call() {
  MyObj x, y;
  MyObj *p = &x;
// CHECK:   Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] (Expr: DeclRefExpr, Decl: x))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_DRE_X]] (Expr: DeclRefExpr, Decl: x)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_P:[0-9]+]] (Decl: p, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_ADDR_X]] (Expr: UnaryOperator, Type : MyObj *)
  MyObj *q = &y;
// CHECK:   Issue ([[L_Y:[0-9]+]] (Path: y), ToOrigin: [[O_DRE_Y:[0-9]+]] (Expr: DeclRefExpr, Decl: y))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_ADDR_Y:[0-9]+]] (Expr: UnaryOperator, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_DRE_Y]] (Expr: DeclRefExpr, Decl: y)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_Q:[0-9]+]] (Decl: q, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_ADDR_Y]] (Expr: UnaryOperator, Type : MyObj *)
  MyObj* r = LifetimeBoundCall(p, q);
// CHECK:   Use ([[O_P]] (Decl: p, Type : MyObj *), Read)
// CHECK:   Issue ({{[0-9]+}} (Path: p), ToOrigin: {{[0-9]+}} (Expr: DeclRefExpr, Decl: p))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_P_RVAL:[0-9]+]] (Expr: ImplicitCastExpr, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_P]] (Decl: p, Type : MyObj *)
// CHECK:   Use ([[O_Q]] (Decl: q, Type : MyObj *), Read)
// CHECK:   Issue ({{[0-9]+}} (Path: q), ToOrigin: {{[0-9]+}} (Expr: DeclRefExpr, Decl: q))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_Q_RVAL:[0-9]+]] (Expr: ImplicitCastExpr, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_Q]] (Decl: q, Type : MyObj *)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_CALL_EXPR:[0-9]+]] (Expr: CallExpr, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_P_RVAL]] (Expr: ImplicitCastExpr, Type : MyObj *)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_CALL_EXPR]] (Expr: CallExpr, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_Q_RVAL]] (Expr: ImplicitCastExpr, Type : MyObj *), Merge
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Decl: r, Type : MyObj *)
// CHECK-NEXT:       Src:  [[O_CALL_EXPR]] (Expr: CallExpr, Type : MyObj *)
// CHECK:   Expire ([[L_Y]] (Path: y))
// CHECK:   Expire ([[L_X]] (Path: x))
}

// CHECK-LABEL: Function: test_reference_variable
void test_reference_variable() {
  MyObj x;
  const MyObj* p;
  const MyObj& y = x;
// CHECK: Block B{{[0-9]+}}:
// CHECK:   Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] (Expr: DeclRefExpr, Decl: x))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_CAST_Y:[0-9]+]] (Expr: ImplicitCastExpr, Type : const MyObj &)
// CHECK-NEXT:       Src:  [[O_DRE_X]] (Expr: DeclRefExpr, Decl: x)
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_Y:[0-9]+]] (Decl: y, Type : const MyObj &)
// CHECK-NEXT:       Src:  [[O_CAST_Y]] (Expr: ImplicitCastExpr, Type : const MyObj &)
  const MyObj& z = y;
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: [[O_Z:[0-9]+]] (Decl: z, Type : const MyObj &)
// CHECK-NEXT:       Src:  [[O_Y]] (Decl: y, Type : const MyObj &)
  p = &z;
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Expr: UnaryOperator, Type : const MyObj *)
// CHECK-NEXT:       Src:  [[O_Z]] (Decl: z, Type : const MyObj &)
// CHECK:   Use ({{[0-9]+}} (Decl: p, Type : const MyObj *), Write)
// CHECK:   Issue ({{[0-9]+}} (Path: p), ToOrigin: {{[0-9]+}} (Expr: DeclRefExpr, Decl: p))
// CHECK:   OriginFlow:
// CHECK-NEXT:       Dest: {{[0-9]+}} (Decl: p, Type : const MyObj *)
// CHECK-NEXT:       Src:  {{[0-9]+}} (Expr: UnaryOperator, Type : const MyObj *)
// CHECK:   Expire ([[L_X]] (Path: x))
}
