// RUN: %check_clang_tidy -std=c++20 %s modernize-use-starts-ends-with %t -- \
// RUN:   -- -isystem %clang_tidy_headers

#include <string.h>
#include <string>

std::string foo(std::string);
std::string bar();

class sub_string : public std::string {};
class sub_sub_string : public sub_string {};

struct string_like {
  bool starts_with(const char *s) const;
  size_t find(const char *s, size_t pos = 0) const;
};

struct string_like_camel {
  bool startsWith(const char *s) const;
  size_t find(const char *s, size_t pos = 0) const;
};

struct prefer_underscore_version {
  bool starts_with(const char *s) const;
  bool startsWith(const char *s) const;
  size_t find(const char *s, size_t pos = 0) const;
};

struct prefer_underscore_version_flip {
  bool startsWith(const char *s) const;
  bool starts_with(const char *s) const;
  size_t find(const char *s, size_t pos = 0) const;
};

struct prefer_underscore_version_inherit : public string_like {
  bool startsWith(const char *s) const;
};

void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
          string_like sl, string_like_camel slc, prefer_underscore_version puv,
          prefer_underscore_version_flip puvf,
          prefer_underscore_version_inherit puvi) {
  s.find("a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of find() == 0
  // CHECK-FIXES: s.starts_with("a");

  (((((s)).find("a")))) == ((0));
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: ((s)).starts_with("a");

  (s + "a").find("a") == ((0));
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: (s + "a").starts_with("a");

  s.find(s) == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with(s);

  s.find("aaa") != 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: !s.starts_with("aaa");

  s.find(foo(foo(bar()))) != 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: !s.starts_with(foo(foo(bar())));

  if (s.find("....") == 0) { /* do something */ }
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: if (s.starts_with("...."))

  0 != s.find("a");
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: !s.starts_with("a");

  s.rfind("a", 0) == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of rfind() == 0
  // CHECK-FIXES: s.starts_with("a");

  s.rfind(s, 0) == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with(s);

  s.rfind("aaa", 0) != 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: !s.starts_with("aaa");

  s.rfind(foo(foo(bar())), 0) != 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: !s.starts_with(foo(foo(bar())));

  if (s.rfind("....", 0) == 0) { /* do something */ }
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: if (s.starts_with("...."))

  0 != s.rfind("a", 0);
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: !s.starts_with("a");

  #define STR(x) std::string(x)
  0 == STR(s).find("a");
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: STR(s).starts_with("a");

  #define STRING s
  if (0 == STRING.find("ala")) { /* do something */}
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: if (STRING.starts_with("ala"))

  #define FIND find
  s.FIND("a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with("a")

  #define PREFIX "a"
  s.find(PREFIX) == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with(PREFIX)

  #define ZERO 0
  s.find("a") == ZERO;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with("a")

  sv.find("a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: sv.starts_with("a");

  sv.rfind("a", 0) != 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: !sv.starts_with("a");

  ss.find("a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: ss.starts_with("a");

  sss.find("a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: ss.starts_with("a");

  sl.find("a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: sl.starts_with("a");

  slc.find("a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use startsWith
  // CHECK-FIXES: slc.startsWith("a");

  puv.find("a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: puv.starts_with("a");

  puvf.find("a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: puvf.starts_with("a");

  // Here, the subclass has startsWith, the superclass has starts_with.
  // We prefer the version from the subclass.
  puvi.find("a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use startsWith
  // CHECK-FIXES: puvi.startsWith("a");

  s.compare(0, 1, "a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of compare() == 0
  // CHECK-FIXES: s.starts_with("a");

  s.compare(0, 1, "a") != 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of compare() != 0
  // CHECK-FIXES: !s.starts_with("a");

  s.compare(0, strlen("a"), "a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with("a");

  s.compare(0, std::strlen("a"), "a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with("a");

  s.compare(0, std::strlen(("a")), "a") == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with("a");

  s.compare(0, std::strlen(("a")), (("a"))) == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with("a");

  s.compare(0, s.size(), s) == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with(s);

  s.compare(0, s.length(), s) == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with(s);

  0 != s.compare(0, sv.length(), sv);
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with(sv);

  #define LENGTH(x) (x).length()
  s.compare(0, LENGTH(s), s) == 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with(s);

  s.compare(ZERO, LENGTH(s), s) == ZERO;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: s.starts_with(s);

  s.compare(ZERO, LENGTH(sv), sv) != 0;
  // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
  // CHECK-FIXES: !s.starts_with(sv);

  // Expressions that don't trigger the check are here.
  #define EQ(x, y) ((x) == (y))
  EQ(s.find("a"), 0);

  #define DOTFIND(x, y) (x).find(y)
  DOTFIND(s, "a") == 0;

  #define STARTS_WITH_COMPARE(x, y) (x).compare(0, (x).size(), (y))
  STARTS_WITH_COMPARE(s, s) == 0;

  s.compare(0, 1, "ab") == 0;
}
