#include "../clang-tidy/utils/FixItHintUtils.h"
#include "ClangTidyDiagnosticConsumer.h"
#include "ClangTidyTest.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
#include <optional>

namespace clang {
namespace tidy {

namespace {
using namespace clang::ast_matchers;
using namespace utils::fixit;

template <QualifierTarget CT, QualifierPolicy CP>
class ConstTransform : public ClangTidyCheck {
public:
  ConstTransform(StringRef CheckName, ClangTidyContext *Context)
      : ClangTidyCheck(CheckName, Context) {}

  void registerMatchers(MatchFinder *Finder) override {
    Finder->addMatcher(varDecl(hasName("target")).bind("var"), this);
  }

  void check(const MatchFinder::MatchResult &Result) override {
    const auto *D = Result.Nodes.getNodeAs<VarDecl>("var");
    using utils::fixit::addQualifierToVarDecl;
    std::optional<FixItHint> Fix =
        addQualifierToVarDecl(*D, *Result.Context, Qualifiers::Const, CT, CP);
    auto Diag = diag(D->getBeginLoc(), "doing const transformation");
    if (Fix)
      Diag << *Fix;
  }
};
} // namespace

namespace test {
using PointeeLTransform =
    ConstTransform<QualifierTarget::Pointee, QualifierPolicy::Left>;
using PointeeRTransform =
    ConstTransform<QualifierTarget::Pointee, QualifierPolicy::Right>;

using ValueLTransform =
    ConstTransform<QualifierTarget::Value, QualifierPolicy::Left>;
using ValueRTransform =
    ConstTransform<QualifierTarget::Value, QualifierPolicy::Right>;

// ----------------------------------------------------------------------------
// Test Value-like types. Everything with indirection is done later.
// ----------------------------------------------------------------------------

TEST(Values, Builtin) {
  StringRef Snippet = "int target = 0;";

  EXPECT_EQ("const int target = 0;", runCheckOnCode<ValueLTransform>(Snippet));
  EXPECT_EQ("const int target = 0;",
            runCheckOnCode<PointeeLTransform>(Snippet));

  EXPECT_EQ("int const target = 0;", runCheckOnCode<ValueRTransform>(Snippet));
  EXPECT_EQ("int const target = 0;",
            runCheckOnCode<PointeeRTransform>(Snippet));
}
TEST(Values, TypedefBuiltin) {
  StringRef T = "typedef int MyInt;";
  StringRef S = "MyInt target = 0;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const MyInt target = 0;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const MyInt target = 0;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("MyInt const target = 0;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("MyInt const target = 0;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Values, TypedefBuiltinPointer) {
  StringRef T = "typedef int* MyInt;";
  StringRef S = "MyInt target = nullptr;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const MyInt target = nullptr;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const MyInt target = nullptr;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("MyInt const target = nullptr;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("MyInt const target = nullptr;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Values, UsingBuiltin) {
  StringRef T = "using MyInt = int;";
  StringRef S = "MyInt target = 0;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const MyInt target = 0;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const MyInt target = 0;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("MyInt const target = 0;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("MyInt const target = 0;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Values, UsingBuiltinPointer) {
  StringRef T = "using MyInt = int*;";
  StringRef S = "MyInt target = nullptr;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const MyInt target = nullptr;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const MyInt target = nullptr;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("MyInt const target = nullptr;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("MyInt const target = nullptr;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Values, AutoValue) {
  StringRef T = "int f() { return 42; }\n";
  StringRef S = "auto target = f();";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const auto target = f();"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const auto target = f();"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("auto const target = f();"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("auto const target = f();"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Values, AutoPointer) {
  StringRef T = "int* f() { return nullptr; }\n";
  StringRef S = "auto target = f();";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const auto target = f();"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const auto target = f();"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("auto const target = f();"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("auto const target = f();"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Values, AutoReference) {
  StringRef T = "static int global = 42; int& f() { return global; }\n";
  StringRef S = "auto target = f();";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const auto target = f();"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const auto target = f();"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("auto const target = f();"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("auto const target = f();"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Values, DeclTypeValue) {
  StringRef T = "int f() { return 42; }\n";
  StringRef S = "decltype(f()) target = f();";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const decltype(f()) target = f();"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const decltype(f()) target = f();"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("decltype(f()) const target = f();"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("decltype(f()) const target = f();"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Values, DeclTypePointer) {
  // The pointer itself will be changed to 'const'. There is no
  // way to make the pointee 'const' with this syntax.
  StringRef T = "int* f() { return nullptr; }\n";
  StringRef S = "decltype(f()) target = f();";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const decltype(f()) target = f();"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const decltype(f()) target = f();"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("decltype(f()) const target = f();"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("decltype(f()) const target = f();"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Values, DeclTypeReference) {
  // Same as pointer, but the reference itself will be marked 'const'.
  // This has no effect and will result in a warning afterwards. The
  // transformation itself is still correct.
  StringRef T = "static int global = 42; int& f() { return global; }\n";
  StringRef S = "decltype(f()) target = f();";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const decltype(f()) target = f();"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const decltype(f()) target = f();"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("decltype(f()) const target = f();"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("decltype(f()) const target = f();"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Values, Parens) {
  StringRef Snippet = "int ((target)) = 0;";

  EXPECT_EQ("const int ((target)) = 0;",
            runCheckOnCode<ValueLTransform>(Snippet));
  EXPECT_EQ("const int ((target)) = 0;",
            runCheckOnCode<PointeeLTransform>(Snippet));

  EXPECT_EQ("int const ((target)) = 0;",
            runCheckOnCode<ValueRTransform>(Snippet));
  EXPECT_EQ("int const ((target)) = 0;",
            runCheckOnCode<PointeeRTransform>(Snippet));
}

// ----------------------------------------------------------------------------
// Test builtin-arrays
// ----------------------------------------------------------------------------

TEST(Arrays, Builtin) {
  StringRef Snippet = "int target[][1] = {{1}, {2}, {3}};";

  EXPECT_EQ("const int target[][1] = {{1}, {2}, {3}};",
            runCheckOnCode<PointeeLTransform>(Snippet));
  EXPECT_EQ("const int target[][1] = {{1}, {2}, {3}};",
            runCheckOnCode<ValueLTransform>(Snippet));

  EXPECT_EQ("int const target[][1] = {{1}, {2}, {3}};",
            runCheckOnCode<PointeeRTransform>(Snippet));
  EXPECT_EQ("int const target[][1] = {{1}, {2}, {3}};",
            runCheckOnCode<ValueRTransform>(Snippet));
}
TEST(Arrays, BuiltinParens) {
  StringRef Snippet = "int ((target))[][1] = {{1}, {2}, {3}};";

  EXPECT_EQ("const int ((target))[][1] = {{1}, {2}, {3}};",
            runCheckOnCode<PointeeLTransform>(Snippet));
  EXPECT_EQ("const int ((target))[][1] = {{1}, {2}, {3}};",
            runCheckOnCode<ValueLTransform>(Snippet));

  EXPECT_EQ("int const ((target))[][1] = {{1}, {2}, {3}};",
            runCheckOnCode<PointeeRTransform>(Snippet));
  EXPECT_EQ("int const ((target))[][1] = {{1}, {2}, {3}};",
            runCheckOnCode<ValueRTransform>(Snippet));
}
TEST(Arrays, Pointers) {
  StringRef Snippet = "int x; int* target[] = {&x, &x, &x};";

  EXPECT_EQ("int x; const int* target[] = {&x, &x, &x};",
            runCheckOnCode<PointeeLTransform>(Snippet));
  EXPECT_EQ("int x; int const* target[] = {&x, &x, &x};",
            runCheckOnCode<PointeeRTransform>(Snippet));

  EXPECT_EQ("int x; int* const target[] = {&x, &x, &x};",
            runCheckOnCode<ValueLTransform>(Snippet));
  EXPECT_EQ("int x; int* const target[] = {&x, &x, &x};",
            runCheckOnCode<ValueRTransform>(Snippet));
}
TEST(Arrays, PointerPointers) {
  StringRef Snippet = "int* x = nullptr; int** target[] = {&x, &x, &x};";

  EXPECT_EQ("int* x = nullptr; int* const* target[] = {&x, &x, &x};",
            runCheckOnCode<PointeeLTransform>(Snippet));
  EXPECT_EQ("int* x = nullptr; int** const target[] = {&x, &x, &x};",
            runCheckOnCode<ValueLTransform>(Snippet));

  EXPECT_EQ("int* x = nullptr; int* const* target[] = {&x, &x, &x};",
            runCheckOnCode<PointeeRTransform>(Snippet));
  EXPECT_EQ("int* x = nullptr; int** const target[] = {&x, &x, &x};",
            runCheckOnCode<ValueRTransform>(Snippet));
}
TEST(Arrays, PointersParens) {
  StringRef Snippet = "int x; int* (target)[] = {&x, &x, &x};";

  EXPECT_EQ("int x; const int* (target)[] = {&x, &x, &x};",
            runCheckOnCode<PointeeLTransform>(Snippet));
  EXPECT_EQ("int x; int const* (target)[] = {&x, &x, &x};",
            runCheckOnCode<PointeeRTransform>(Snippet));

  EXPECT_EQ("int x; int* const (target)[] = {&x, &x, &x};",
            runCheckOnCode<ValueLTransform>(Snippet));
  EXPECT_EQ("int x; int* const (target)[] = {&x, &x, &x};",
            runCheckOnCode<ValueRTransform>(Snippet));
}

// ----------------------------------------------------------------------------
// Test reference types. This does not include pointers and arrays.
// ----------------------------------------------------------------------------

TEST(Reference, LValueBuiltin) {
  StringRef Snippet = "int x = 42; int& target = x;";

  EXPECT_EQ("int x = 42; const int& target = x;",
            runCheckOnCode<ValueLTransform>(Snippet));
  EXPECT_EQ("int x = 42; const int& target = x;",
            runCheckOnCode<PointeeLTransform>(Snippet));

  EXPECT_EQ("int x = 42; int const& target = x;",
            runCheckOnCode<ValueRTransform>(Snippet));
  EXPECT_EQ("int x = 42; int const& target = x;",
            runCheckOnCode<PointeeRTransform>(Snippet));
}
TEST(Reference, RValueBuiltin) {
  StringRef Snippet = "int&& target = 42;";
  EXPECT_EQ("const int&& target = 42;",
            runCheckOnCode<ValueLTransform>(Snippet));
  EXPECT_EQ("const int&& target = 42;",
            runCheckOnCode<PointeeLTransform>(Snippet));

  EXPECT_EQ("int const&& target = 42;",
            runCheckOnCode<ValueRTransform>(Snippet));
  EXPECT_EQ("int const&& target = 42;",
            runCheckOnCode<PointeeRTransform>(Snippet));
}
TEST(Reference, LValueToPointer) {
  StringRef Snippet = "int* p; int *& target = p;";
  EXPECT_EQ("int* p; int * const& target = p;",
            runCheckOnCode<ValueLTransform>(Snippet));
  EXPECT_EQ("int* p; int * const& target = p;",
            runCheckOnCode<PointeeLTransform>(Snippet));

  EXPECT_EQ("int* p; int * const& target = p;",
            runCheckOnCode<ValueRTransform>(Snippet));
  EXPECT_EQ("int* p; int * const& target = p;",
            runCheckOnCode<PointeeRTransform>(Snippet));
}
TEST(Reference, LValueParens) {
  StringRef Snippet = "int x = 42; int ((& target)) = x;";

  EXPECT_EQ("int x = 42; const int ((& target)) = x;",
            runCheckOnCode<ValueLTransform>(Snippet));
  EXPECT_EQ("int x = 42; const int ((& target)) = x;",
            runCheckOnCode<PointeeLTransform>(Snippet));

  EXPECT_EQ("int x = 42; int  const((& target)) = x;",
            runCheckOnCode<ValueRTransform>(Snippet));
  EXPECT_EQ("int x = 42; int  const((& target)) = x;",
            runCheckOnCode<PointeeRTransform>(Snippet));
}
TEST(Reference, ToArray) {
  StringRef ArraySnippet = "int a[4] = {1, 2, 3, 4};";
  StringRef Snippet = "int (&target)[4] = a;";
  auto Cat = [&ArraySnippet](StringRef S) { return (ArraySnippet + S).str(); };

  EXPECT_EQ(Cat("const int (&target)[4] = a;"),
            runCheckOnCode<ValueLTransform>(Cat(Snippet)));
  EXPECT_EQ(Cat("const int (&target)[4] = a;"),
            runCheckOnCode<PointeeLTransform>(Cat(Snippet)));

  EXPECT_EQ(Cat("int  const(&target)[4] = a;"),
            runCheckOnCode<ValueRTransform>(Cat(Snippet)));
  EXPECT_EQ(Cat("int  const(&target)[4] = a;"),
            runCheckOnCode<PointeeRTransform>(Cat(Snippet)));
}
TEST(Reference, Auto) {
  StringRef T = "static int global = 42; int& f() { return global; }\n";
  StringRef S = "auto& target = f();";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const auto& target = f();"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("auto const& target = f();"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("const auto& target = f();"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("auto const& target = f();"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}

// ----------------------------------------------------------------------------
// Test pointers types.
// ----------------------------------------------------------------------------

TEST(Pointers, SingleBuiltin) {
  StringRef Snippet = "int* target = nullptr;";

  EXPECT_EQ("int* const target = nullptr;",
            runCheckOnCode<ValueLTransform>(Snippet));
  EXPECT_EQ("int* const target = nullptr;",
            runCheckOnCode<ValueRTransform>(Snippet));

  EXPECT_EQ("const int* target = nullptr;",
            runCheckOnCode<PointeeLTransform>(Snippet));
  EXPECT_EQ("int const* target = nullptr;",
            runCheckOnCode<PointeeRTransform>(Snippet));
}
TEST(Pointers, MultiBuiltin) {
  StringRef Snippet = "int** target = nullptr;";

  EXPECT_EQ("int** const target = nullptr;",
            runCheckOnCode<ValueLTransform>(Snippet));
  EXPECT_EQ("int** const target = nullptr;",
            runCheckOnCode<ValueRTransform>(Snippet));

  EXPECT_EQ("int* const* target = nullptr;",
            runCheckOnCode<PointeeLTransform>(Snippet));
  EXPECT_EQ("int* const* target = nullptr;",
            runCheckOnCode<PointeeRTransform>(Snippet));
}
TEST(Pointers, ToArray) {
  StringRef ArraySnippet = "int a[4] = {1, 2, 3, 4};";
  StringRef Snippet = "int (*target)[4] = &a;";
  auto Cat = [&ArraySnippet](StringRef S) { return (ArraySnippet + S).str(); };

  EXPECT_EQ(Cat("int (*const target)[4] = &a;"),
            runCheckOnCode<ValueLTransform>(Cat(Snippet)));
  EXPECT_EQ(Cat("const int (*target)[4] = &a;"),
            runCheckOnCode<PointeeLTransform>(Cat(Snippet)));

  EXPECT_EQ(Cat("int (*const target)[4] = &a;"),
            runCheckOnCode<ValueRTransform>(Cat(Snippet)));
  EXPECT_EQ(Cat("int  const(*target)[4] = &a;"),
            runCheckOnCode<PointeeRTransform>(Cat(Snippet)));
}
TEST(Pointers, Parens) {
  StringRef Snippet = "int ((**target)) = nullptr;";

  EXPECT_EQ("int ((**const target)) = nullptr;",
            runCheckOnCode<ValueLTransform>(Snippet));
  EXPECT_EQ("int ((**const target)) = nullptr;",
            runCheckOnCode<ValueRTransform>(Snippet));

  EXPECT_EQ("int ((* const*target)) = nullptr;",
            runCheckOnCode<PointeeLTransform>(Snippet));
  EXPECT_EQ("int ((* const*target)) = nullptr;",
            runCheckOnCode<PointeeRTransform>(Snippet));
}
TEST(Pointers, Auto) {
  StringRef T = "int* f() { return nullptr; }\n";
  StringRef S = "auto* target = f();";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("auto* const target = f();"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("auto* const target = f();"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("const auto* target = f();"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("auto const* target = f();"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Pointers, AutoParens) {
  StringRef T = "int* f() { return nullptr; }\n";
  StringRef S = "auto (((* target))) = f();";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("auto (((* const target))) = f();"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("auto (((* const target))) = f();"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("const auto (((* target))) = f();"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("auto  const(((* target))) = f();"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Pointers, FunctionPointer) {
  StringRef S = "int (*target)(float, int, double) = nullptr;";

  EXPECT_EQ("int (*const target)(float, int, double) = nullptr;",
            runCheckOnCode<ValueLTransform>(S));
  EXPECT_EQ("int (*const target)(float, int, double) = nullptr;",
            runCheckOnCode<ValueRTransform>(S));

  EXPECT_EQ("int (*const target)(float, int, double) = nullptr;",
            runCheckOnCode<PointeeLTransform>(S));
  EXPECT_EQ("int (*const target)(float, int, double) = nullptr;",
            runCheckOnCode<PointeeRTransform>(S));

  S = "int (((*target)))(float, int, double) = nullptr;";
  EXPECT_EQ("int (((*const target)))(float, int, double) = nullptr;",
            runCheckOnCode<PointeeRTransform>(S));
}
TEST(Pointers, MemberFunctionPointer) {
  StringRef T = "struct A { int f() { return 1; } };";
  StringRef S = "int (A::*target)() = &A::f;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("int (A::*const target)() = &A::f;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("int (A::*const target)() = &A::f;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("int (A::*const target)() = &A::f;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("int (A::*const target)() = &A::f;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));

  S = "int (A::*((target)))() = &A::f;";
  EXPECT_EQ(Cat("int (A::*const ((target)))() = &A::f;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
}
TEST(Pointers, MemberDataPointer) {
  StringRef T = "struct A { int member = 0; };";
  StringRef S = "int A::*target = &A::member;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("int A::*const target = &A::member;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("int A::*const target = &A::member;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("int A::*const target = &A::member;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("int A::*const target = &A::member;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));

  S = "int A::*((target)) = &A::member;";
  EXPECT_EQ(Cat("int A::*const ((target)) = &A::member;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}

// ----------------------------------------------------------------------------
// Test TagTypes (struct, class, unions, enums)
// ----------------------------------------------------------------------------

TEST(TagTypes, Struct) {
  StringRef T = "struct Foo { int data; int method(); };\n";
  StringRef S = "struct Foo target{0};";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const struct Foo target{0};"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const struct Foo target{0};"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("struct Foo const target{0};"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("struct Foo const target{0};"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));

  S = "Foo target{0};";
  EXPECT_EQ(Cat("const Foo target{0};"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const Foo target{0};"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("Foo const target{0};"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("Foo const target{0};"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));

  S = "Foo (target){0};";
  EXPECT_EQ(Cat("const Foo (target){0};"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const Foo (target){0};"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("Foo const (target){0};"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("Foo const (target){0};"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));

  S = "struct S { int i; } target = { 0 };";
  EXPECT_EQ("const struct S { int i; } target = { 0 };",
            runCheckOnCode<ValueLTransform>(S));
  EXPECT_EQ("const struct S { int i; } target = { 0 };",
            runCheckOnCode<PointeeLTransform>(S));

  EXPECT_EQ("struct S { int i; } const target = { 0 };",
            runCheckOnCode<PointeeRTransform>(S));
  EXPECT_EQ("struct S { int i; } const target = { 0 };",
            runCheckOnCode<PointeeRTransform>(S));

  S = "struct { int i; } target = { 0 };";
  EXPECT_EQ("const struct { int i; } target = { 0 };",
            runCheckOnCode<ValueLTransform>(S));
  EXPECT_EQ("const struct { int i; } target = { 0 };",
            runCheckOnCode<PointeeLTransform>(S));

  EXPECT_EQ("struct { int i; } const target = { 0 };",
            runCheckOnCode<PointeeRTransform>(S));
  EXPECT_EQ("struct { int i; } const target = { 0 };",
            runCheckOnCode<PointeeRTransform>(S));
}
TEST(TagTypes, Class) {
  StringRef T = "class Foo { int data; int method(); };\n";
  StringRef S = "class Foo target;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const class Foo target;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const class Foo target;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("class Foo const target;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("class Foo const target;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));

  S = "Foo target;";
  EXPECT_EQ(Cat("const Foo target;"), runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const Foo target;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("Foo const target;"), runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("Foo const target;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));

  S = "Foo (target);";
  EXPECT_EQ(Cat("const Foo (target);"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const Foo (target);"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("Foo const (target);"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("Foo const (target);"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(TagTypes, Enum) {
  StringRef T = "enum Foo { N_ONE, N_TWO, N_THREE };\n";
  StringRef S = "enum Foo target;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const enum Foo target;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const enum Foo target;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("enum Foo const target;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("enum Foo const target;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));

  S = "Foo target;";
  EXPECT_EQ(Cat("const Foo target;"), runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const Foo target;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("Foo const target;"), runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("Foo const target;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));

  S = "Foo (target);";
  EXPECT_EQ(Cat("const Foo (target);"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const Foo (target);"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("Foo const (target);"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("Foo const (target);"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(TagTypes, Union) {
  StringRef T = "union Foo { int yay; float nej; };\n";
  StringRef S = "union Foo target;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("const union Foo target;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const union Foo target;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("union Foo const target;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("union Foo const target;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));

  S = "Foo target;";
  EXPECT_EQ(Cat("const Foo target;"), runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const Foo target;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("Foo const target;"), runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("Foo const target;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));

  S = "Foo (target);";
  EXPECT_EQ(Cat("const Foo (target);"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("const Foo (target);"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("Foo const (target);"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
  EXPECT_EQ(Cat("Foo const (target);"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}

// ----------------------------------------------------------------------------
// Test Macro expansions.
// ----------------------------------------------------------------------------

TEST(Macro, AllInMacro) {
  StringRef T = "#define DEFINE_VARIABLE int target = 42\n";
  StringRef S = "DEFINE_VARIABLE;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("DEFINE_VARIABLE;"), runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("DEFINE_VARIABLE;"), runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("DEFINE_VARIABLE;"), runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("DEFINE_VARIABLE;"), runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Macro, MacroParameter) {
  StringRef T = "#define DEFINE_VARIABLE(X) int X = 42\n";
  StringRef S = "DEFINE_VARIABLE(target);";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("DEFINE_VARIABLE(target);"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("DEFINE_VARIABLE(target);"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("DEFINE_VARIABLE(target);"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("DEFINE_VARIABLE(target);"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Macro, MacroTypeValue) {
  StringRef T = "#define BAD_TYPEDEF int\n";
  StringRef S = "BAD_TYPEDEF target = 42;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("BAD_TYPEDEF target = 42;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("BAD_TYPEDEF target = 42;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));

  EXPECT_EQ(Cat("BAD_TYPEDEF const target = 42;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
  EXPECT_EQ(Cat("BAD_TYPEDEF const target = 42;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));
}
TEST(Macro, MacroTypePointer) {
  StringRef T = "#define BAD_TYPEDEF int *\n";
  StringRef S = "BAD_TYPEDEF target = nullptr;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("BAD_TYPEDEF const target = nullptr;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("BAD_TYPEDEF const target = nullptr;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  // FIXME: Failing even all parts seem to bail-out in for isMacroID()
  // The macro itself is changed here and below which is not intended.
  EXPECT_NE(Cat("BAD_TYPEDEF target = nullptr;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
  EXPECT_EQ(Cat("BAD_TYPEDEF target = nullptr;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
}
TEST(Macro, MacroTypeReference) {
  StringRef T = "static int g = 42;\n#define BAD_TYPEDEF int&\n";
  StringRef S = "BAD_TYPEDEF target = g;";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("BAD_TYPEDEF target = g;"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  // FIXME: Failing even all parts seem to bail-out in for isMacroID()
  EXPECT_NE(Cat("BAD_TYPEDEF target = g;"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("BAD_TYPEDEF target = g;"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  // FIXME: Failing even all parts seem to bail-out in for isMacroID()
  EXPECT_NE(Cat("BAD_TYPEDEF target = g;"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
// This failed in LLVM.
TEST(Macro, Variable) {
  StringRef M = "#define DEBUG(X) do { if (1) { X; } } while (0)\n";
  StringRef F = "void foo() ";
  StringRef V = "{ DEBUG(int target = 42;); }";

  auto Cat = [&](StringRef S) { return (M + F + V).str(); };

  EXPECT_EQ(Cat("{ DEBUG(const int target = 42;); }"),
            runCheckOnCode<ValueLTransform>(Cat(V)));
  EXPECT_EQ(Cat("{ DEBUG(int const target = 42;); }"),
            runCheckOnCode<ValueRTransform>(Cat(V)));
}
TEST(Macro, RangeLoop) {
  StringRef M = "#define DEBUG(X) do { if (1) { X; }} while (false)\n";
  StringRef F = "void foo() { char array[] = {'a', 'b', 'c'}; ";
  StringRef V = "DEBUG( for(auto& target: array) 10 + target; );";
  StringRef E = "}";

  auto Cat = [&](StringRef S) { return (M + F + V + E).str(); };

  EXPECT_EQ(Cat("DEBUG( for(const auto& target: array); );"),
            runCheckOnCode<ValueLTransform>(Cat(V)));
  EXPECT_EQ(Cat("DEBUG( for(auto const& target: array); );"),
            runCheckOnCode<ValueRTransform>(Cat(V)));
}

// ----------------------------------------------------------------------------
// Test template code.
// ----------------------------------------------------------------------------

TEST(Template, TemplateVariable) {
  StringRef T = "template <typename T> T target = 3.1415;";

  EXPECT_EQ("template <typename T> const T target = 3.1415;",
            runCheckOnCode<ValueLTransform>(T));
  EXPECT_EQ("template <typename T> T const target = 3.1415;",
            runCheckOnCode<ValueRTransform>(T));

  EXPECT_EQ("template <typename T> const T target = 3.1415;",
            runCheckOnCode<PointeeLTransform>(T));
  EXPECT_EQ("template <typename T> T const target = 3.1415;",
            runCheckOnCode<PointeeRTransform>(T));
}
TEST(Template, FunctionValue) {
  StringRef T = "template <typename T> void f(T v) \n";
  StringRef S = "{ T target = v; }";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("{ const T target = v; }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const target = v; }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const T target = v; }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const target = v; }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Template, FunctionPointer) {
  StringRef T = "template <typename T> void f(T* v) \n";
  StringRef S = "{ T* target = v; }";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("{ T* const target = v; }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T* const target = v; }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const T* target = v; }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const* target = v; }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Template, FunctionReference) {
  StringRef T = "template <typename T> void f(T& v) \n";
  StringRef S = "{ T& target = v; }";
  auto Cat = [&T](StringRef S) { return (T + S).str(); };

  EXPECT_EQ(Cat("{ const T& target = v; }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const& target = v; }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const T& target = v; }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const& target = v; }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Template, MultiInstantiationsFunction) {
  StringRef T = "template <typename T> void f(T v) \n";
  StringRef S = "{ T target = v; }";
  StringRef InstantStart = "void calls() {\n";
  StringRef InstValue = "f<int>(42);\n";
  StringRef InstConstValue = "f<const int>(42);\n";
  StringRef InstPointer = "f<int*>(nullptr);\n";
  StringRef InstPointerConst = "f<int* const>(nullptr);\n";
  StringRef InstConstPointer = "f<const int*>(nullptr);\n";
  StringRef InstConstPointerConst = "f<const int* const>(nullptr);\n";
  StringRef InstRef = "int i = 42;\nf<int&>(i);\n";
  StringRef InstConstRef = "f<const int&>(i);\n";
  StringRef InstantEnd = "}";
  auto Cat = [&](StringRef Target) {
    return (T + Target + InstantStart + InstValue + InstConstValue +
            InstPointer + InstPointerConst + InstConstPointer +
            InstConstPointerConst + InstRef + InstConstRef + InstantEnd)
        .str();
  };

  EXPECT_EQ(Cat("{ const T target = v; }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const target = v; }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const T target = v; }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const target = v; }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}

TEST(Template, StructValue) {
  StringRef T = "template <typename T> struct S { void f(T& v) \n";
  StringRef S = "{ T target = v; }";
  StringRef End = "\n};";
  auto Cat = [&T, &End](StringRef S) { return (T + S + End).str(); };

  EXPECT_EQ(Cat("{ const T target = v; }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const target = v; }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const T target = v; }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const target = v; }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Template, StructPointer) {
  StringRef T = "template <typename T> struct S { void f(T* v) \n";
  StringRef S = "{ T* target = v; }";
  StringRef End = "\n};";
  auto Cat = [&T, &End](StringRef S) { return (T + S + End).str(); };

  EXPECT_EQ(Cat("{ T* const target = v; }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T* const target = v; }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const T* target = v; }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const* target = v; }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Template, StructReference) {
  StringRef T = "template <typename T> struct S { void f(T& v) \n";
  StringRef S = "{ T& target = v; }";
  StringRef End = "\n};";
  auto Cat = [&T, &End](StringRef S) { return (T + S + End).str(); };

  EXPECT_EQ(Cat("{ const T& target = v; }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const& target = v; }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const T& target = v; }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ T const& target = v; }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Template, DependentReturnFunction) {
  StringRef TS = "template <typename T> struct TS { using value_type = T; };";
  StringRef T = "template <typename T> void foo() ";
  StringRef S = "{ typename T::value_type target; }";
  auto Cat = [&TS, &T](StringRef S) { return (TS + T + S).str(); };

  EXPECT_EQ(Cat("{ const typename T::value_type target; }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ typename T::value_type const target; }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const typename T::value_type target; }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ typename T::value_type const target; }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Template, DependentReturnPointerFunction) {
  StringRef TS = "template <typename T> struct TS { using value_type = T; };";
  StringRef T = "template <typename T> void foo() ";
  StringRef S = "{ typename T::value_type *target; }";
  auto Cat = [&TS, &T](StringRef S) { return (TS + T + S).str(); };

  EXPECT_EQ(Cat("{ typename T::value_type *const target; }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ typename T::value_type *const target; }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const typename T::value_type *target; }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ typename T::value_type  const*target; }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Template, DependentReturnReferenceFunction) {
  StringRef TS = "template <typename T> struct TS { using value_type = T; };";
  StringRef T = "template <typename T> void foo(T& f) ";
  StringRef S = "{ typename T::value_type &target = f; }";
  auto Cat = [&TS, &T](StringRef S) { return (TS + T + S).str(); };

  EXPECT_EQ(Cat("{ const typename T::value_type &target = f; }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ typename T::value_type  const&target = f; }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const typename T::value_type &target = f; }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ typename T::value_type  const&target = f; }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Template, VectorLikeType) {
  StringRef TS = "template <typename T> struct TS { TS(const T&) {} }; ";
  StringRef T = "void foo() ";
  StringRef S = "{ TS<int> target(42); }";
  auto Cat = [&TS, &T](StringRef S) { return (TS + T + S).str(); };

  EXPECT_EQ(Cat("{ const TS<int> target(42); }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ TS<int> const target(42); }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const TS<int> target(42); }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ TS<int> const target(42); }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}
TEST(Template, SpecializedTemplate) {
  StringRef TS = "template <typename T = int> struct TS { TS(const T&) {} }; ";
  StringRef TS2 = "template <> struct TS<double> { TS(const double&) {} }; ";
  StringRef T = "void foo() ";
  StringRef S = "{ TS<double> target(42.42); }";
  auto Cat = [&](StringRef S) { return (TS + TS2 + T + S).str(); };

  EXPECT_EQ(Cat("{ const TS<double> target(42.42); }"),
            runCheckOnCode<ValueLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ TS<double> const target(42.42); }"),
            runCheckOnCode<ValueRTransform>(Cat(S)));

  EXPECT_EQ(Cat("{ const TS<double> target(42.42); }"),
            runCheckOnCode<PointeeLTransform>(Cat(S)));
  EXPECT_EQ(Cat("{ TS<double> const target(42.42); }"),
            runCheckOnCode<PointeeRTransform>(Cat(S)));
}

// -----------------------------------------------------------------------------
// ObjC Pointers
// -----------------------------------------------------------------------------

TEST(ObjC, SimplePointers) {
  StringRef S = "int * target = 0;";
  EXPECT_EQ(runCheckOnCode<PointeeLTransform>(S, nullptr, "input.m"),
            "const int * target = 0;");
  EXPECT_EQ(runCheckOnCode<PointeeRTransform>(S, nullptr, "input.m"),
            "int  const* target = 0;");
  EXPECT_EQ(runCheckOnCode<ValueLTransform>(S, nullptr, "input.m"),
            "int * const target = 0;");
  EXPECT_EQ(runCheckOnCode<ValueRTransform>(S, nullptr, "input.m"),
            "int * const target = 0;");
}
TEST(ObjC, ClassPointer) {
  StringRef TB = "@class Object;\nint main() {\n";
  StringRef S = "Object *target;";
  StringRef TE = "\n}";
  auto Cat = [&](StringRef S) { return (TB + S + TE).str(); };

  // FIXME: Not done properly for some reason.
  EXPECT_NE(runCheckOnCode<PointeeLTransform>(Cat(S), nullptr, "input.m"),
            Cat("const Object *target;"));
  EXPECT_NE(runCheckOnCode<PointeeRTransform>(Cat(S), nullptr, "input.m"),
            Cat("Object  const*target;"));
  EXPECT_NE(runCheckOnCode<ValueLTransform>(Cat(S), nullptr, "input.m"),
            Cat("Object *const target;"));
  EXPECT_NE(runCheckOnCode<ValueRTransform>(Cat(S), nullptr, "input.m"),
            Cat("Object *const target;"));
}
TEST(ObjC, InterfacePointer) {
  StringRef TB = "@interface I\n";
  StringRef S = "- (void) foo: (int *) target;";
  StringRef TE = "\n@end";
  auto Cat = [&](StringRef S) { return (TB + S + TE).str(); };

  EXPECT_EQ(runCheckOnCode<PointeeLTransform>(Cat(S), nullptr, "input.m"),
            Cat("- (void) foo: (const int *) target;"));
  EXPECT_EQ(runCheckOnCode<PointeeRTransform>(Cat(S), nullptr, "input.m"),
            Cat("- (void) foo: (int  const*) target;"));
  // FIXME: These transformations are incorrect. ObjC seems to need
  // RParenSkipping which is not implemented.
  EXPECT_NE(runCheckOnCode<ValueLTransform>(Cat(S), nullptr, "input.m"),
            Cat("- (void) foo: (int * const) target;"));
  EXPECT_NE(runCheckOnCode<ValueRTransform>(Cat(S), nullptr, "input.m"),
            Cat("- (void) foo: (int * const) target;"));
}

} // namespace test
} // namespace tidy
} // namespace clang
