[clang-tidy] implement utility-function to add 'const' to variables
authorJonas Toth <development@jonas-toth.eu>
Fri, 3 Jan 2020 19:36:49 +0000 (20:36 +0100)
committerJonas Toth <development@jonas-toth.eu>
Fri, 3 Jan 2020 19:37:47 +0000 (20:37 +0100)
Summary:
This patch extends the already existing facility to add 'const' to variables
to be more flexible and correct. The previous version did not consider pointers
as value AND pointee. For future automatic introduction for const-correctness
this shortcoming needs to be fixed.
It always allows configuration where the 'const' token is inserted, either on
the left side (if possible) or the right side.
It adds many unit-tests to the utility-function that did not exist before, as
the function was implicitly tested through clang-tidy checks. These
tests were not changed, as the API is still compatible.

Reviewers: aaron.ballman, hokein, alexfh, shuaiwang, lebedev.ri

Reviewed By: aaron.ballman

Subscribers: jdoerfert, mgorny, xazax.hun, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D54395

clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp
clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
clang-tools-extra/clang-tidy/utils/FixItHintUtils.cpp
clang-tools-extra/clang-tidy/utils/FixItHintUtils.h
clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
clang-tools-extra/clang-tidy/utils/LexerUtils.h
clang-tools-extra/unittests/clang-tidy/AddConstTest.cpp [new file with mode: 0644]
clang-tools-extra/unittests/clang-tidy/CMakeLists.txt

index e004009..67a6202 100644 (file)
@@ -13,6 +13,7 @@
 #include "../utils/OptionsUtils.h"
 #include "../utils/TypeTraits.h"
 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Basic/Diagnostic.h"
 
 using namespace clang::ast_matchers;
 
@@ -77,8 +78,11 @@ bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
            "the loop variable's type is not a reference type; this creates a "
            "copy in each iteration; consider making this a reference")
       << utils::fixit::changeVarDeclToReference(LoopVar, Context);
-  if (!LoopVar.getType().isConstQualified())
-    Diagnostic << utils::fixit::changeVarDeclToConst(LoopVar);
+  if (!LoopVar.getType().isConstQualified()) {
+    if (llvm::Optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
+            LoopVar, Context, DeclSpec::TQ::TQ_const))
+      Diagnostic << *Fix;
+  }
   return true;
 }
 
@@ -101,11 +105,15 @@ bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
       !utils::decl_ref_expr::allDeclRefExprs(LoopVar, *ForRange.getBody(),
                                              Context)
            .empty()) {
-    diag(LoopVar.getLocation(),
-         "loop variable is copied but only used as const reference; consider "
-         "making it a const reference")
-        << utils::fixit::changeVarDeclToConst(LoopVar)
-        << utils::fixit::changeVarDeclToReference(LoopVar, Context);
+    auto Diag = diag(
+        LoopVar.getLocation(),
+        "loop variable is copied but only used as const reference; consider "
+        "making it a const reference");
+
+    if (llvm::Optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
+            LoopVar, Context, DeclSpec::TQ::TQ_const))
+      Diag << *Fix << utils::fixit::changeVarDeclToReference(LoopVar, Context);
+
     return true;
   }
   return false;
index 7e36b37..c9ae37d 100644 (file)
@@ -12,6 +12,7 @@
 #include "../utils/FixItHintUtils.h"
 #include "../utils/Matchers.h"
 #include "../utils/OptionsUtils.h"
+#include "clang/Basic/Diagnostic.h"
 
 namespace clang {
 namespace tidy {
@@ -21,8 +22,11 @@ namespace {
 void recordFixes(const VarDecl &Var, ASTContext &Context,
                  DiagnosticBuilder &Diagnostic) {
   Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context);
-  if (!Var.getType().isLocalConstQualified())
-    Diagnostic << utils::fixit::changeVarDeclToConst(Var);
+  if (!Var.getType().isLocalConstQualified()) {
+    if (llvm::Optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
+            Var, Context, DeclSpec::TQ::TQ_const))
+      Diagnostic << *Fix;
+  }
 }
 
 } // namespace
index c5bbcdb..c840bd6 100644 (file)
@@ -162,15 +162,18 @@ void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
     // The parameter of each declaration needs to be checked individually as to
     // whether it is const or not as constness can differ between definition and
     // declaration.
-    if (!CurrentParam.getType().getCanonicalType().isConstQualified())
-      Diag << utils::fixit::changeVarDeclToConst(CurrentParam);
+    if (!CurrentParam.getType().getCanonicalType().isConstQualified()) {
+      if (llvm::Optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
+              CurrentParam, *Result.Context, DeclSpec::TQ::TQ_const))
+        Diag << *Fix;
+    }
   }
 }
 
 void UnnecessaryValueParamCheck::registerPPCallbacks(
     const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
   Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
-                                                       IncludeStyle);
+                                                      IncludeStyle);
   PP->addPPCallbacks(Inserter->CreatePPCallbacks());
 }
 
index c30a59e..a9c95e9 100644 (file)
@@ -9,6 +9,7 @@
 #include "FixItHintUtils.h"
 #include "LexerUtils.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/Type.h"
 
 namespace clang {
 namespace tidy {
@@ -26,10 +27,201 @@ FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context) {
   return FixItHint::CreateInsertion(AmpLocation, "&");
 }
 
-FixItHint changeVarDeclToConst(const VarDecl &Var) {
-  return FixItHint::CreateInsertion(Var.getTypeSpecStartLoc(), "const ");
+static bool isValueType(const Type *T) {
+  return !(isa<PointerType>(T) || isa<ReferenceType>(T) || isa<ArrayType>(T) ||
+           isa<MemberPointerType>(T) || isa<ObjCObjectPointerType>(T));
 }
+static bool isValueType(QualType QT) { return isValueType(QT.getTypePtr()); }
+static bool isMemberOrFunctionPointer(QualType QT) {
+  return (QT->isPointerType() && QT->isFunctionPointerType()) ||
+         isa<MemberPointerType>(QT.getTypePtr());
+}
+
+static bool locDangerous(SourceLocation S) {
+  return S.isInvalid() || S.isMacroID();
+}
+
+static Optional<SourceLocation>
+skipLParensBackwards(SourceLocation Start, const ASTContext &Context) {
+  if (locDangerous(Start))
+    return None;
+
+  auto PreviousTokenLParen = [&Start, &Context]() {
+    Token T;
+    T = lexer::getPreviousToken(Start, Context.getSourceManager(),
+                                Context.getLangOpts());
+    return T.is(tok::l_paren);
+  };
+
+  while (Start.isValid() && PreviousTokenLParen())
+    Start = lexer::findPreviousTokenStart(Start, Context.getSourceManager(),
+                                          Context.getLangOpts());
+
+  if (locDangerous(Start))
+    return None;
+  return Start;
+}
+
+static Optional<FixItHint> fixIfNotDangerous(SourceLocation Loc,
+                                             StringRef Text) {
+  if (locDangerous(Loc))
+    return None;
+  return FixItHint::CreateInsertion(Loc, Text);
+}
+
+// Build a string that can be emitted as FixIt with either a space in before
+// or after the qualifier, either ' const' or 'const '.
+static std::string buildQualifier(DeclSpec::TQ Qualifier,
+                                  bool WhitespaceBefore = false) {
+  if (WhitespaceBefore)
+    return (llvm::Twine(' ') + DeclSpec::getSpecifierName(Qualifier)).str();
+  return (llvm::Twine(DeclSpec::getSpecifierName(Qualifier)) + " ").str();
+}
+
+static Optional<FixItHint> changeValue(const VarDecl &Var,
+                                       DeclSpec::TQ Qualifier,
+                                       QualifierTarget QualTarget,
+                                       QualifierPolicy QualPolicy,
+                                       const ASTContext &Context) {
+  switch (QualPolicy) {
+  case QualifierPolicy::Left:
+    return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
+                             buildQualifier(Qualifier));
+  case QualifierPolicy::Right:
+    Optional<SourceLocation> IgnoredParens =
+        skipLParensBackwards(Var.getLocation(), Context);
+
+    if (IgnoredParens)
+      return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
+    return None;
+  }
+}
+
+static Optional<FixItHint> changePointerItself(const VarDecl &Var,
+                                               DeclSpec::TQ Qualifier,
+                                               const ASTContext &Context) {
+  if (locDangerous(Var.getLocation()))
+    return None;
+
+  Optional<SourceLocation> IgnoredParens =
+      skipLParensBackwards(Var.getLocation(), Context);
+  if (IgnoredParens)
+    return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
+  return None;
+}
+
+static Optional<FixItHint>
+changePointer(const VarDecl &Var, DeclSpec::TQ Qualifier, const Type *Pointee,
+              QualifierTarget QualTarget, QualifierPolicy QualPolicy,
+              const ASTContext &Context) {
+  // The pointer itself shall be marked as `const`. This is always to the right
+  // of the '*' or in front of the identifier.
+  if (QualTarget == QualifierTarget::Value)
+    return changePointerItself(Var, Qualifier, Context);
+
+  // Mark the pointee `const` that is a normal value (`int* p = nullptr;`).
+  if (QualTarget == QualifierTarget::Pointee && isValueType(Pointee)) {
+    // Adding the `const` on the left side is just the beginning of the type
+    // specification. (`const int* p = nullptr;`)
+    if (QualPolicy == QualifierPolicy::Left)
+      return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
+                               buildQualifier(Qualifier));
 
+    // Adding the `const` on the right side of the value type requires finding
+    // the `*` token and placing the `const` left of it.
+    // (`int const* p = nullptr;`)
+    if (QualPolicy == QualifierPolicy::Right) {
+      SourceLocation BeforeStar = lexer::findPreviousTokenKind(
+          Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
+          tok::star);
+      if (locDangerous(BeforeStar))
+        return None;
+
+      Optional<SourceLocation> IgnoredParens =
+          skipLParensBackwards(BeforeStar, Context);
+
+      if (IgnoredParens)
+        return fixIfNotDangerous(*IgnoredParens,
+                                 buildQualifier(Qualifier, true));
+      return None;
+    }
+  }
+
+  if (QualTarget == QualifierTarget::Pointee && Pointee->isPointerType()) {
+    // Adding the `const` to the pointee if the pointee is a pointer
+    // is the same as 'QualPolicy == Right && isValueType(Pointee)'.
+    // The `const` must be left of the last `*` token.
+    // (`int * const* p = nullptr;`)
+    SourceLocation BeforeStar = lexer::findPreviousTokenKind(
+        Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
+        tok::star);
+    return fixIfNotDangerous(BeforeStar, buildQualifier(Qualifier, true));
+  }
+
+  return None;
+}
+
+static Optional<FixItHint>
+changeReferencee(const VarDecl &Var, DeclSpec::TQ Qualifier, QualType Pointee,
+                 QualifierTarget QualTarget, QualifierPolicy QualPolicy,
+                 const ASTContext &Context) {
+  if (QualPolicy == QualifierPolicy::Left && isValueType(Pointee))
+    return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
+                             buildQualifier(Qualifier));
+
+  SourceLocation BeforeRef = lexer::findPreviousAnyTokenKind(
+      Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
+      tok::amp, tok::ampamp);
+  Optional<SourceLocation> IgnoredParens =
+      skipLParensBackwards(BeforeRef, Context);
+  if (IgnoredParens)
+    return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier, true));
+
+  return None;
+}
+
+Optional<FixItHint> addQualifierToVarDecl(const VarDecl &Var,
+                                          const ASTContext &Context,
+                                          DeclSpec::TQ Qualifier,
+                                          QualifierTarget QualTarget,
+                                          QualifierPolicy QualPolicy) {
+  assert((QualPolicy == QualifierPolicy::Left ||
+          QualPolicy == QualifierPolicy::Right) &&
+         "Unexpected Insertion Policy");
+  assert((QualTarget == QualifierTarget::Pointee ||
+          QualTarget == QualifierTarget::Value) &&
+         "Unexpected Target");
+
+  QualType ParenStrippedType = Var.getType().IgnoreParens();
+  if (isValueType(ParenStrippedType))
+    return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);
+
+  if (ParenStrippedType->isReferenceType())
+    return changeReferencee(Var, Qualifier, Var.getType()->getPointeeType(),
+                            QualTarget, QualPolicy, Context);
+
+  if (isMemberOrFunctionPointer(ParenStrippedType))
+    return changePointerItself(Var, Qualifier, Context);
+
+  if (ParenStrippedType->isPointerType())
+    return changePointer(Var, Qualifier,
+                         ParenStrippedType->getPointeeType().getTypePtr(),
+                         QualTarget, QualPolicy, Context);
+
+  if (ParenStrippedType->isArrayType()) {
+    const Type *AT = ParenStrippedType->getBaseElementTypeUnsafe();
+    assert(AT && "Did not retrieve array element type for an array.");
+
+    if (isValueType(AT))
+      return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);
+
+    if (AT->isPointerType())
+      return changePointer(Var, Qualifier, AT->getPointeeType().getTypePtr(),
+                           QualTarget, QualPolicy, Context);
+  }
+
+  return None;
+}
 } // namespace fixit
 } // namespace utils
 } // namespace tidy
index e55f19c..4fa5e0a 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
+#include "clang/Sema/DeclSpec.h"
 
 namespace clang {
 namespace tidy {
@@ -23,6 +24,31 @@ FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context);
 /// Creates fix to make ``VarDecl`` const qualified.
 FixItHint changeVarDeclToConst(const VarDecl &Var);
 
+/// This enum defines where the qualifier shall be preferably added.
+enum class QualifierPolicy {
+  Left,  // Add the qualifier always to the left side, if that is possible.
+  Right, // Add the qualifier always to the right side.
+};
+
+/// This enum defines which entity is the target for adding the qualifier. This
+/// makes only a difference for pointer-types. Other types behave identical
+/// for either value of \c ConstTarget.
+enum class QualifierTarget {
+  Pointee, /// Transforming a pointer attaches to the pointee and not the
+           /// pointer itself. For references and normal values this option has
+           /// no effect. `int * p = &i;` -> `const int * p = &i` or `int const
+           /// * p = &i`.
+  Value,   /// Transforming pointers will consider the pointer itself.
+           /// `int * p = &i;` -> `int * const = &i`
+};
+
+/// \brief Creates fix to qualify ``VarDecl`` with the specified \c Qualifier.
+/// Requires that `Var` is isolated in written code like in `int foo = 42;`.
+Optional<FixItHint>
+addQualifierToVarDecl(const VarDecl &Var, const ASTContext &Context,
+                      DeclSpec::TQ Qualifier,
+                      QualifierTarget CT = QualifierTarget::Pointee,
+                      QualifierPolicy CP = QualifierPolicy::Left);
 } // namespace fixit
 } // namespace utils
 } // namespace tidy
index 17838fe..79d2499 100644 (file)
@@ -17,7 +17,11 @@ Token getPreviousToken(SourceLocation Location, const SourceManager &SM,
                        const LangOptions &LangOpts, bool SkipComments) {
   Token Token;
   Token.setKind(tok::unknown);
+
   Location = Location.getLocWithOffset(-1);
+  if (Location.isInvalid())
+      return Token;
+
   auto StartOfFile = SM.getLocForStartOfFile(SM.getFileID(Location));
   while (Location != StartOfFile) {
     Location = Lexer::GetBeginningOfToken(Location, SM, LangOpts);
@@ -47,6 +51,9 @@ SourceLocation findPreviousTokenKind(SourceLocation Start,
                                      const SourceManager &SM,
                                      const LangOptions &LangOpts,
                                      tok::TokenKind TK) {
+  if (Start.isInvalid() || Start.isMacroID())
+    return SourceLocation();
+
   while (true) {
     SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts);
     if (L.isInvalid() || L.isMacroID())
index fcf9ada..8781b05 100644 (file)
@@ -39,6 +39,8 @@ SourceLocation findPreviousAnyTokenKind(SourceLocation Start,
                                         const SourceManager &SM,
                                         const LangOptions &LangOpts,
                                         TokenKind TK, TokenKinds... TKs) {
+  if (Start.isInvalid() || Start.isMacroID())
+    return SourceLocation();
   while (true) {
     SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts);
     if (L.isInvalid() || L.isMacroID())
@@ -46,7 +48,7 @@ SourceLocation findPreviousAnyTokenKind(SourceLocation Start,
 
     Token T;
     // Returning 'true' is used to signal failure to retrieve the token.
-    if (Lexer::getRawToken(L, T, SM, LangOpts))
+    if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true))
       return SourceLocation();
 
     if (T.isOneOf(TK, TKs...))
diff --git a/clang-tools-extra/unittests/clang-tidy/AddConstTest.cpp b/clang-tools-extra/unittests/clang-tidy/AddConstTest.cpp
new file mode 100644 (file)
index 0000000..31a444f
--- /dev/null
@@ -0,0 +1,1081 @@
+#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"
+
+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;
+    Optional<FixItHint> Fix = addQualifierToVarDecl(
+        *D, *Result.Context, DeclSpec::TQ::TQ_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
index 6c58b98..297c793 100644 (file)
@@ -7,6 +7,7 @@ get_filename_component(CLANG_LINT_SOURCE_DIR
 include_directories(${CLANG_LINT_SOURCE_DIR})
 
 add_extra_unittest(ClangTidyTests
+  AddConstTest.cpp
   ClangTidyDiagnosticConsumerTest.cpp
   ClangTidyOptionsTest.cpp
   IncludeInserterTest.cpp