/*
 * Copyright (c) 2018-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include <iostream>
#include <memory>
#include <string>

namespace use_after_destructor {

struct S {
  int* f;
  S(int i) {
    f = new int;
    *f = i;
  }

  // missing: operator= to copy the pointer. double delete can happen if
  // operator= is called

  ~S() { delete f; }
};

// destructor called at end of function, no issues
void normal_scope_destructor_ok() { S s(1); }

void nested_scope_destructor_ok() {
  { S s(1); }
}

int reinit_after_explicit_destructor_ok() {
  S s(1);
  s.~S();
  s = S(2);
  return *s.f;
}

int reinit_after_explicit_destructor2_bad() {
  S s(1);
  S s2(2);
  s.~S();
  s = s2; // operator=
  return *s.f;
  // [s2.f] is [s.f] so gets deleted twice
}

void placement_new_explicit_destructor_ok() {
  char buf[sizeof(S)];
  {
    S* s = new (buf) S(1);
    s->~S();
  }
  {
    // this use of [buf] shouldn't be flagged
    S* s = new (buf) S(2);
    s->~S();
  }
}

void double_destructor_bad() {
  S s(1);
  s.~S();
  // destructor will be called again after S goes out of scope, which is
  // undefined behavior
}

int use_after_destructor_bad() {
  S s(1);
  s.~S();
  int ret = *s.f;
  s = S{2};
  return ret;
}

// S::operator= doesn't copy resources correctly
void use_after_scope1_bad() {
  S s(1);
  {
    S tmp(2);
    s = tmp; // translated as operator=(s, tmp)
  } // destructor for tmp runs here
  // destructor for s here; second time the value held by s has been destructed
}

void FN_use_after_scope2_bad() {
  S s(1);
  {
    s = S(1);
  } // destructor runs here, but our frontend currently doesn't insert it
}

struct POD {
  int f;
};

// this code is ok since double-destructing POD struct is ok
void destruct_twice_ok() {
  POD p{1};
  {
    POD tmp{2};
    p = tmp;
  } // destructor for tmp
} // destructor for p runs here, but it's harmless

class Subclass : virtual POD {
  int* f;
  Subclass() { f = new int; }

  /** frontend code for this destructor will be:
   * ~Subclass:
   *  __infer_inner_destructor_~Subclass(this)
   *  __infer_inner_destructor_~POD(this)
   *
   * __infer_inner_destructor_~Subclass:
   *  delete f;
   *
   * We need to be careful not to warn that this has been double-destructed
   */
  ~Subclass() { delete f; }
};

void basic_placement_new_ok() {
  S* ptr = new S(1);
  S* tptr = new (ptr) S(1);
  tptr->~S();
  delete[] ptr;
}

int* destruct_pointer_contents_then_placement_new1_ok(S* s) {
  s->~S();
  new (s) S(1);
  return s->f;
}

int* destruct_pointer_contents_then_placement_new2_ok(S* s) {
  s->~S();
  new (&(s->f)) S(1);
  return s->f;
}

int* placement_new_aliasing1_bad() {
  S* s = new S(1);
  s->~S();
  auto alias = new (s) S(2);
  delete alias; // this deletes s too
  return s->f; // bad, accessing freed memory
}

int* placement_new_aliasing2_bad() {
  S* s = new S(1);
  s->~S();
  auto alias = new (s) S(2);
  delete s; // this deletes alias too
  return alias->f; // bad, accessing freed memory
}

int* placement_new_aliasing3_bad() {
  S* s = new S(1);
  s->~S();
  S* alias = s;
  auto alias_placement = new (s) S(2);
  delete s; // this deletes alias too
  return alias->f; // bad, accessing freed memory
}

void placement_new_non_var_ok() {
  struct M {
    S* s;
  } m;
  m.s = new S(1);
  m.s->~S();
  new (m.s) S(2);
  delete m.s;
}

void return_placement_new_ok() {
  auto mem = new S(1);
  return new (mem) S(2);
}

void destructor_in_loop_ok() {
  for (int i = 0; i < 10; i++) {
    S s(1);
  }
}

int FN_use_after_scope3_bad() {
  int* p;
  {
    int value = 3;
    p = &value;
  } // we do not know in the plugin that value is out of scope
  return *p;
}

struct C {
  C(int v) : f(v){};
  ~C();
  int f;
};

int use_after_scope4_bad() {
  C* pc;
  {
    C c(3);
    pc = &c;
  }
  return pc->f;
}

struct B {
  ~B();
};

struct A {
  ~A() { (void)*f; }
  const B* f;
};

void FN_destructor_order_bad() {
  A a;
  B b;
  a.f = &b;
}

struct A2 {
  ~A2() {}
  const B* f;
};

void destructor_order_empty_destructor_ok() {
  A2 a;
  B b;
  a.f = &b;
}

std::unique_ptr<A>* allocate_in_branch_ok(bool b) {
  std::unique_ptr<A> a1;
  std::unique_ptr<A> a2;
  std::unique_ptr<A>* a3 = &a1;

  if (b) {
    a2 = std::make_unique<A>(*a1);
    a3 = &a2;
  }

  // read a3
  const B* read = (*a3)->f;
}

std::string mk_string();

bool variable_init_ternary_ok(bool b) {
  // this can cause issues because of the way the frontend treatment of ternary ?: interacts with
  // the treatment of passing return values by reference as parameters
  std::string newPath = b ? "" : mk_string();
}

void move_data(POD* from, POD* to);

struct Moveable {
  Moveable() = default;
  Moveable(const Moveable&) = delete; // not copyable

  // move constructor
  Moveable(Moveable&& that) noexcept { move_data(&that.data, &data); }

  Moveable& operator=(Moveable&& that) noexcept {
    this->~Moveable();
    ::new (this) Moveable(std::move(that));
    return *this;
  }

  ~Moveable() {}

  Moveable& operator=(const Moveable&) = delete;

  POD data;
};

void move_moveable_ok(Moveable& src) {
  Moveable x;
  x = std::move(src);
}

} // namespace use_after_destructor
