// RUN: %check_clang_tidy %s readability-static-accessed-through-instance %t -- --fix-notes -- -isystem %S/Inputs/static-accessed-through-instance
#include <__clang_cuda_builtin_vars.h>

enum OutEnum {
  E0,
};

struct C {
  static void foo();
  static int x;
  int nsx;
  enum {
    Anonymous,
  };
  enum E {
    E1,
  };
  using enum OutEnum;
  void mf() {
    (void)&x;    // OK, x is accessed inside the struct.
    (void)&C::x; // OK, x is accessed using a qualified-id.
    foo();       // OK, foo() is accessed inside the struct.
  }
  void ns() const;
};

int C::x = 0;

struct CC {
  void foo();
  int x;
};

template <typename T> struct CT {
  static T foo();
  static T x;
  int nsx;
  void mf() {
    (void)&x;    // OK, x is accessed inside the struct.
    (void)&C::x; // OK, x is accessed using a qualified-id.
    foo();       // OK, foo() is accessed inside the struct.
  }
};

// Expressions with side effects
C &f(int, int, int, int);
void g() {
  f(1, 2, 3, 4).x;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member accessed through instance  [readability-static-accessed-through-instance]
  // CHECK-MESSAGES: :[[@LINE-2]]:3: note: member base expression may carry some side effects
  // CHECK-FIXES: C::x;
}

int i(int &);
void j(int);
C h();
bool a();
int k(bool);

void f(C c) {
  j(i(h().x));
  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: static member
  // CHECK-MESSAGES: :[[@LINE-2]]:7: note: member base expression may carry some side effects
  // CHECK-FIXES: j(i(C::x));

  // The execution of h() depends on the return value of a().
  j(k(a() && h().x));
  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: static member
  // CHECK-MESSAGES: :[[@LINE-2]]:14: note: member base expression may carry some side effects
  // CHECK-FIXES: j(k(a() && C::x));

  if ([c]() {
        c.ns();
        return c;
      }().x == 15)
    ;
  // CHECK-MESSAGES: :[[@LINE-5]]:7: warning: static member
  // CHECK-MESSAGES: :[[@LINE-6]]:7: note: member base expression may carry some side effects
  // CHECK-FIXES: if (C::x == 15)
}

// Nested specifiers
namespace N {
struct V {
  static int v;
  struct T {
    static int t;
    struct U {
      static int u;
    };
  };
};
}

void f(N::V::T::U u) {
  N::V v;
  v.v = 12;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: N::V::v = 12;

  N::V::T w;
  w.t = 12;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: N::V::T::t = 12;

  // u.u is not changed to N::V::T::U::u; because the nesting level is over 3.
  u.u = 12;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: u.u = 12;

  using B = N::V::T::U;
  B b;
  b.u;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: B::u;
}

// Templates
template <typename T> T CT<T>::x;

template <typename T> struct CCT {
  T foo();
  T x;
};

typedef C D;

using E = D;

#define FOO(c) c.foo()
#define X(c) c.x

template <typename T> void f(T t, C c) {
  t.x; // OK, t is a template parameter.
  c.x;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: C::x;
}

template <int N> struct S { static int x; };

template <> struct S<0> { int x; };

template <int N> void h() {
  S<N> sN;
  sN.x; // OK, value of N affects whether x is static or not.

  S<2> s2;
  s2.x;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: S<2>::x;
}

void static_through_instance() {
  C *c1 = new C();
  c1->foo(); // 1
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: C::foo(); // 1
  c1->x; // 2
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: C::x; // 2
  c1->Anonymous; // 3
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: C::Anonymous; // 3
  c1->E1; // 4
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: C::E1; // 4
  c1->E0; // 5
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: C::E0; // 5

  c1->nsx; // OK, nsx is a non-static member.

  const C *c2 = new C();
  c2->foo(); // 2
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: C::foo(); // 2

  C::foo(); // OK, foo() is accessed using a qualified-id.
  C::x;     // OK, x is accessed using a qualified-id.

  D d;
  d.foo();
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: D::foo();
  d.x;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: D::x;

  E e;
  e.foo();
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: E::foo();
  e.x;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: E::x;

  CC *cc = new CC;

  f(*c1, *c1);
  f(*cc, *c1);

  // Macros: OK, macros are not checked.
  FOO((*c1));
  X((*c1));
  FOO((*cc));
  X((*cc));

  // Templates
  CT<int> ct;
  ct.foo();
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: CT<int>::foo();
  ct.x;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: CT<int>::x;
  ct.nsx; // OK, nsx is a non-static member

  CCT<int> cct;
  cct.foo(); // OK, CCT has no static members.
  cct.x;     // OK, CCT has no static members.

  h<4>();
}

struct SP {
  static int I;
} P;

void usep() {
  P.I;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: SP::I;
}

namespace NSP {
struct SP {
  static int I;
} P;
} // namespace NSP

void usensp() {
  NSP::P.I;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: NSP::SP::I;
}

// Overloaded member access operator
struct Q {
  static int K;
  int y = 0;
};

int Q::K = 0;

struct Qptr {
  Q *q;

  explicit Qptr(Q *qq) : q(qq) {}

  Q *operator->() {
    ++q->y;
    return q;
  }
};

void func(Qptr qp) {
  qp->y = 10;
  qp->K = 10;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member accessed through instance [readability-static-accessed-through-instance]
  // CHECK-MESSAGES: :[[@LINE-2]]:3: note: member base expression may carry some side effects
  // CHECK-FIXES: Q::K = 10;
}

namespace {
  struct Anonymous {
    static int I;
  };
}

void use_anonymous() {
  Anonymous Anon;
  Anon.I;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: Anonymous::I;
}

namespace Outer {
  inline namespace Inline {
  struct S {
    static int I;
  };
  }
}

void use_inline() {
  Outer::S V;
  V.I;
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member
  // CHECK-FIXES: Outer::S::I;
}

// https://bugs.llvm.org/show_bug.cgi?id=48758
namespace Bugzilla_48758 {

unsigned int x1 = threadIdx.x;
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:10: warning: static member
unsigned int x2 = blockIdx.x;
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:10: warning: static member
unsigned int x3 = blockDim.x;
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:10: warning: static member
unsigned int x4 = gridDim.x;
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:10: warning: static member

} // namespace Bugzilla_48758

// https://github.com/llvm/llvm-project/issues/61736
namespace llvm_issue_61736
{

struct {
  static void f() {}
} AnonStruct, *AnonStructPointer;

class {
  public:
  static void f() {}
} AnonClass, *AnonClassPointer;

void testAnonymousStructAndClass() {
  AnonStruct.f();
  AnonStructPointer->f();

  AnonClass.f();
  AnonClassPointer->f();
}

struct Embedded {
  struct {
    static void f() {}
  } static EmbeddedStruct, *EmbeddedStructPointer;

  class {
    public:
      static void f() {}
  } static EmbeddedClass, *EmbeddedClassPointer;
};

void testEmbeddedAnonymousStructAndClass() {
  Embedded::EmbeddedStruct.f();
  Embedded::EmbeddedStructPointer->f();

  Embedded::EmbeddedClass.f();
  Embedded::EmbeddedClassPointer->f();

  Embedded E;
  E.EmbeddedStruct.f();
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member accessed through instance [readability-static-accessed-through-instance]
  // CHECK-FIXES: llvm_issue_61736::Embedded::EmbeddedStruct.f();
  E.EmbeddedStructPointer->f();
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member accessed through instance [readability-static-accessed-through-instance]
  // CHECK-FIXES: llvm_issue_61736::Embedded::EmbeddedStructPointer->f();

  E.EmbeddedClass.f();
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member accessed through instance [readability-static-accessed-through-instance]
  // CHECK-FIXES: llvm_issue_61736::Embedded::EmbeddedClass.f();
  E.EmbeddedClassPointer->f();
  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member accessed through instance [readability-static-accessed-through-instance]
  // CHECK-FIXES: llvm_issue_61736::Embedded::EmbeddedClassPointer->f();
}

} // namespace llvm_issue_61736

namespace PR51861 {
  class Foo {
    public:
      static Foo& getInstance();
      static int getBar();
  };

  inline int Foo::getBar() { return 42; }

  void test() {
    auto& params = Foo::getInstance();
    params.getBar();
    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: static member accessed through instance [readability-static-accessed-through-instance]
    // CHECK-FIXES: PR51861::Foo::getBar();
  }
}

namespace PR75163 {
  struct Static {
    static void call();
  };

  struct Ptr {
    Static* operator->();
  };

  void test(Ptr& ptr) {
    ptr->call();
    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: static member accessed through instance [readability-static-accessed-through-instance]
    // CHECK-MESSAGES: :[[@LINE-2]]:5: note: member base expression may carry some side effects
    // CHECK-FIXES: PR75163::Static::call();
  }
}
