// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-static-cast-downcast %t

class Base {
};

class Derived : public Base {
};

class Base2 {
};

class MultiDerived : public Base, public Base2 {
};

class PolymorphicBase {
public:
  virtual ~PolymorphicBase();
};

class PolymorphicDerived : public PolymorphicBase {
};

class PolymorphicMultiDerived : public Base, public PolymorphicBase {
};

void pointers() {

  auto P0 = static_cast<Derived*>(new Base());
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]

  const Base* B0;
  auto PC0 = static_cast<const Derived*>(B0);
  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]

  auto P1 = static_cast<Base*>(new Derived()); // OK, upcast to a public base
  auto P2 = static_cast<Base*>(new MultiDerived()); // OK, upcast to a public base
  auto P3 = static_cast<Base2*>(new MultiDerived()); // OK, upcast to a public base
}

void pointers_polymorphic() {

  auto PP0 = static_cast<PolymorphicDerived*>(new PolymorphicBase());
  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
  // CHECK-FIXES: auto PP0 = dynamic_cast<PolymorphicDerived*>(new PolymorphicBase());

  const PolymorphicBase* B0;
  auto PPC0 = static_cast<const PolymorphicDerived*>(B0);
  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
  // CHECK-FIXES: auto PPC0 = dynamic_cast<const PolymorphicDerived*>(B0);


  auto B1 = static_cast<PolymorphicBase*>(new PolymorphicDerived()); // OK, upcast to a public base
  auto B2 = static_cast<PolymorphicBase*>(new PolymorphicMultiDerived()); // OK, upcast to a public base
  auto B3 = static_cast<Base*>(new PolymorphicMultiDerived()); // OK, upcast to a public base
}

void arrays() {
  Base ArrayOfBase[10];
  auto A0 = static_cast<Derived*>(ArrayOfBase);
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
}

void arrays_polymorphic() {
  PolymorphicBase ArrayOfPolymorphicBase[10];
  auto AP0 = static_cast<PolymorphicDerived*>(ArrayOfPolymorphicBase);
  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead
  // CHECK-FIXES: auto AP0 = dynamic_cast<PolymorphicDerived*>(ArrayOfPolymorphicBase);
}

void references() {
  Base B0;
  auto R0 = static_cast<Derived&>(B0);
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
  Base& RefToBase = B0;
  auto R1 = static_cast<Derived&>(RefToBase);
  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]

  const Base& ConstRefToBase = B0;
  auto RC1 = static_cast<const Derived&>(ConstRefToBase);
  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]


  Derived RD1;
  auto R2 = static_cast<Base&>(RD1); // OK, upcast to a public base
}

void references_polymorphic() {
  PolymorphicBase B0;
  auto RP0 = static_cast<PolymorphicDerived&>(B0);
  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead
  // CHECK-FIXES: auto RP0 = dynamic_cast<PolymorphicDerived&>(B0);

  PolymorphicBase& RefToPolymorphicBase = B0;
  auto RP1 = static_cast<PolymorphicDerived&>(RefToPolymorphicBase);
  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
  // CHECK-FIXES: auto RP1 = dynamic_cast<PolymorphicDerived&>(RefToPolymorphicBase);

  const PolymorphicBase& ConstRefToPolymorphicBase = B0;
  auto RPC2 = static_cast<const PolymorphicDerived&>(ConstRefToPolymorphicBase);
  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
  // CHECK-FIXES: auto RPC2 = dynamic_cast<const PolymorphicDerived&>(ConstRefToPolymorphicBase);

  PolymorphicDerived d1;
  auto RP2 = static_cast<PolymorphicBase&>(d1); // OK, upcast to a public base
}

template<class B, class D>
void templ() {
  auto B0 = static_cast<B*>(new D());
}

void templ_bad_call() {
  templ<Derived, Base>(); //FIXME: this should trigger a warning
}

void templ_good_call() {
  templ<Base, Derived>(); // OK, upcast to a public base
}
