JonasToth updated this revision to Diff 230048.
JonasToth added a comment.

- port patch to monorepo
- Merge branch 'master' into feature_transform_const.patch
- merge current master and new version for previous steps into this patch
- fix compilation issues after type renaming
- work on fixing support and find out issues


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D54943/new/

https://reviews.llvm.org/D54943

Files:
  clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
  clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.cpp
  clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.h
  clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
  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/docs/ReleaseNotes.rst
  
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-const-correctness.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-pointer-as-values.cpp
  
clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-transform-pointer-as-values.cpp
  
clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-transform-values.cpp
  
clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-values.cpp
  clang-tools-extra/unittests/clang-tidy/AddConstTest.cpp
  clang-tools-extra/unittests/clang-tidy/CMakeLists.txt

Index: clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
===================================================================
--- clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
+++ clang-tools-extra/unittests/clang-tidy/CMakeLists.txt
@@ -7,6 +7,7 @@
 include_directories(${CLANG_LINT_SOURCE_DIR})
 
 add_extra_unittest(ClangTidyTests
+  AddConstTest.cpp
   ClangTidyDiagnosticConsumerTest.cpp
   ClangTidyOptionsTest.cpp
   IncludeInserterTest.cpp
Index: clang-tools-extra/unittests/clang-tidy/AddConstTest.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/unittests/clang-tidy/AddConstTest.cpp
@@ -0,0 +1,982 @@
+#include "../clang-tidy/utils/FixItHintUtils.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, DeclSpec::TQ::TQ_const,
+                                                    CT, CP, Result.Context);
+    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)));
+}
+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()
+  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)));
+}
+
+// ----------------------------------------------------------------------------
+// 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)));
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-values.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-values.cpp
@@ -0,0 +1,563 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-const-correctness %t
+
+// ------- Provide test samples for primitive builtins ---------
+// - every 'p_*' variable is a 'potential_const_*' variable
+// - every 'np_*' variable is a 'non_potential_const_*' variable
+
+bool global;
+char np_global = 0; // globals can't be known to be const
+
+namespace foo {
+int scoped;
+float np_scoped = 1; // namespace variables are like globals
+} // namespace foo
+
+// Lambdas should be ignored, because they do not follow the normal variable
+// semantic (e.g. the type is only known to the compiler).
+void lambdas() {
+  auto Lambda = [](int i) { return i < 0; };
+}
+
+void some_function(double, wchar_t);
+
+void some_function(double np_arg0, wchar_t np_arg1) {
+  int p_local0 = 2;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
+
+  int np_local0;
+  const int np_local1 = 42;
+
+  unsigned int np_local2 = 3;
+  np_local2 <<= 4;
+
+  int np_local3 = 4;
+  ++np_local3;
+  int np_local4 = 4;
+  np_local4++;
+
+  int np_local5 = 4;
+  --np_local5;
+  int np_local6 = 4;
+  np_local6--;
+}
+
+void nested_scopes() {
+  int p_local0 = 2;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
+  int np_local0 = 42;
+
+  {
+    int p_local1 = 42;
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: variable 'p_local1' of type 'int' can be declared 'const'
+    np_local0 *= 2;
+  }
+}
+
+void some_lambda_environment_capture_all_by_reference(double np_arg0) {
+  int np_local0 = 0;
+  int p_local0 = 1;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
+
+  int np_local2;
+  const int np_local3 = 2;
+
+  // Capturing all variables by reference prohibits making them const.
+  [&]() { ++np_local0; };
+
+  int p_local1 = 0;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'int' can be declared 'const'
+}
+
+void some_lambda_environment_capture_all_by_value(double np_arg0) {
+  int np_local0 = 0;
+  int p_local0 = 1;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
+
+  int np_local1;
+  const int np_local2 = 2;
+
+  // Capturing by value has no influence on them.
+  [=]() { (void)p_local0; };
+
+  np_local0 += 10;
+}
+
+void function_inout_pointer(int *inout);
+void function_in_pointer(const int *in);
+
+void some_pointer_taking(int *out) {
+  int np_local0 = 42;
+  const int *const p0_np_local0 = &np_local0;
+  int *const p1_np_local0 = &np_local0;
+
+  int np_local1 = 42;
+  const int *const p0_np_local1 = &np_local1;
+  int *const p1_np_local1 = &np_local1;
+  *p1_np_local0 = 43;
+
+  int np_local2 = 42;
+  function_inout_pointer(&np_local2);
+
+  // Prevents const.
+  int np_local3 = 42;
+  out = &np_local3; // This returns and invalid address, its just about the AST
+
+  int p_local1 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'int' can be declared 'const'
+  const int *const p0_p_local1 = &p_local1;
+
+  int p_local2 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'int' can be declared 'const'
+  function_in_pointer(&p_local2);
+}
+
+void function_inout_ref(int &inout);
+void function_in_ref(const int &in);
+
+void some_reference_taking() {
+  int np_local0 = 42;
+  const int &r0_np_local0 = np_local0;
+  int &r1_np_local0 = np_local0;
+  r1_np_local0 = 43;
+  const int &r2_np_local0 = r1_np_local0;
+
+  int np_local1 = 42;
+  function_inout_ref(np_local1);
+
+  int p_local0 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
+  const int &r0_p_local0 = p_local0;
+
+  int p_local1 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'int' can be declared 'const'
+  function_in_ref(p_local1);
+}
+
+double *non_const_pointer_return() {
+  double p_local0 = 0.0;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double' can be declared 'const'
+  double np_local0 = 24.4;
+
+  return &np_local0;
+}
+
+const double *const_pointer_return() {
+  double p_local0 = 0.0;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double' can be declared 'const'
+  double p_local1 = 24.4;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'double' can be declared 'const'
+  return &p_local1;
+}
+
+double &non_const_ref_return() {
+  double p_local0 = 0.0;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double' can be declared 'const'
+  double np_local0 = 42.42;
+  return np_local0;
+}
+
+const double &const_ref_return() {
+  double p_local0 = 0.0;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double' can be declared 'const'
+  double p_local1 = 24.4;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'double' can be declared 'const'
+  return p_local1;
+}
+
+double *&return_non_const_pointer_ref() {
+  double *np_local0 = nullptr;
+  return np_local0;
+}
+
+void overloaded_arguments(const int &in);
+void overloaded_arguments(int &inout);
+void overloaded_arguments(const int *in);
+void overloaded_arguments(int *inout);
+
+void function_calling() {
+  int np_local0 = 42;
+  overloaded_arguments(np_local0);
+
+  const int np_local1 = 42;
+  overloaded_arguments(np_local1);
+
+  int np_local2 = 42;
+  overloaded_arguments(&np_local2);
+
+  const int np_local3 = 42;
+  overloaded_arguments(&np_local3);
+}
+
+template <typename T>
+void define_locals(T np_arg0, T &np_arg1, int np_arg2) {
+  T np_local0 = 0;
+  np_local0 += np_arg0 * np_arg1;
+
+  T np_local1 = 42;
+  np_local0 += np_local1;
+
+  // Used as argument to an overloaded function with const and non-const.
+  T np_local2 = 42;
+  overloaded_arguments(np_local2);
+
+  int np_local4 = 42;
+  // non-template values are ok still.
+  int p_local0 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
+  np_local4 += p_local0;
+}
+
+void template_instantiation() {
+  const int np_local0 = 42;
+  int np_local1 = 42;
+
+  define_locals(np_local0, np_local1, np_local0);
+  define_locals(np_local1, np_local1, np_local1);
+}
+
+struct ConstNonConstClass {
+  ConstNonConstClass();
+  ConstNonConstClass(double &np_local0);
+  double nonConstMethod() {}
+  double constMethod() const {}
+  double modifyingMethod(double &np_arg0) const;
+
+  double NonConstMember;
+  const double ConstMember;
+
+  double &NonConstMemberRef;
+  const double &ConstMemberRef;
+
+  double *NonConstMemberPtr;
+  const double *ConstMemberPtr;
+};
+
+void direct_class_access() {
+  ConstNonConstClass np_local0;
+
+  np_local0.constMethod();
+  np_local0.nonConstMethod();
+
+  ConstNonConstClass p_local0;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'ConstNonConstClass' can be declared 'const'
+  p_local0.constMethod();
+
+  ConstNonConstClass p_local1;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'ConstNonConstClass' can be declared 'const'
+  double np_local1;
+  p_local1.modifyingMethod(np_local1);
+
+  double np_local2;
+  ConstNonConstClass p_local2(np_local2);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'ConstNonConstClass' can be declared 'const'
+
+  ConstNonConstClass np_local3;
+  np_local3.NonConstMember = 42.;
+
+  ConstNonConstClass np_local4;
+  np_local4.NonConstMemberRef = 42.;
+
+  ConstNonConstClass p_local3;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local3' of type 'ConstNonConstClass' can be declared 'const'
+  const double val0 = p_local3.NonConstMember;
+  const double val1 = p_local3.NonConstMemberRef;
+  const double val2 = *p_local3.NonConstMemberPtr;
+
+  ConstNonConstClass p_local4;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local4' of type 'ConstNonConstClass' can be declared 'const'
+  *np_local4.NonConstMemberPtr = 42.;
+}
+
+void class_access_array() {
+  ConstNonConstClass np_local0[2];
+  np_local0[0].constMethod();
+  np_local0[1].constMethod();
+  np_local0[1].nonConstMethod();
+
+  ConstNonConstClass p_local0[2];
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'ConstNonConstClass [2]' can be declared 'const'
+  p_local0[0].constMethod();
+  np_local0[1].constMethod();
+}
+
+struct OperatorsAsConstAsPossible {
+  OperatorsAsConstAsPossible &operator+=(const OperatorsAsConstAsPossible &rhs);
+  OperatorsAsConstAsPossible operator+(const OperatorsAsConstAsPossible &rhs) const;
+};
+
+struct NonConstOperators {
+};
+NonConstOperators operator+(NonConstOperators &lhs, NonConstOperators &rhs);
+NonConstOperators operator-(NonConstOperators lhs, NonConstOperators rhs);
+
+void internal_operator_calls() {
+  OperatorsAsConstAsPossible np_local0;
+  OperatorsAsConstAsPossible np_local1;
+  OperatorsAsConstAsPossible p_local0;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'OperatorsAsConstAsPossible' can be declared 'const'
+  OperatorsAsConstAsPossible p_local1;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'OperatorsAsConstAsPossible' can be declared 'const'
+
+  np_local0 += p_local0;
+  np_local1 = p_local0 + p_local1;
+
+  NonConstOperators np_local2;
+  NonConstOperators np_local3;
+  NonConstOperators np_local4;
+
+  np_local2 = np_local3 + np_local4;
+
+  NonConstOperators p_local2;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'NonConstOperators' can be declared 'const'
+  NonConstOperators p_local3 = p_local2 - p_local2;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local3' of type 'NonConstOperators' can be declared 'const'
+}
+
+struct MyVector {
+  double *begin();
+  const double *begin() const;
+
+  double *end();
+  const double *end() const;
+
+  double &operator[](int index);
+  double operator[](int index) const;
+
+  double values[100];
+};
+
+void vector_usage() {
+  double np_local0[10];
+  np_local0[5] = 42.;
+
+  MyVector np_local1;
+  np_local1[5] = 42.;
+
+  double p_local0[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double [10]' can be declared 'const'
+  double p_local1 = p_local0[5];
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'double' can be declared 'const'
+
+  // The following subscript calls suprisingly choose the non-const operator
+  // version.
+  MyVector np_local2;
+  double p_local2 = np_local2[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'double' can be declared 'const'
+
+  MyVector np_local3;
+  const double np_local4 = np_local3[42];
+
+  // This subscript results in const overloaded operator.
+  const MyVector np_local5{};
+  double p_local3 = np_local5[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local3' of type 'double' can be declared 'const'
+}
+
+void const_handle(const double &np_local0);
+void const_handle(const double *np_local0);
+
+void non_const_handle(double &np_local0);
+void non_const_handle(double *np_local0);
+
+void handle_from_array() {
+  // Non-const handle from non-const array forbids declaring the array as const
+  double np_local0[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  double *p_local0 = &np_local0[1]; // Could be `double *const`, but warning deactivated by default
+
+  double np_local1[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  double &non_const_ref = np_local1[1];
+  non_const_ref = 42.;
+
+  double np_local2[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  double *np_local3;
+  np_local3 = &np_local2[5];
+
+  double np_local4[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  non_const_handle(np_local4[2]);
+  double np_local5[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  non_const_handle(&np_local5[2]);
+
+  // Constant handles are ok
+  double p_local1[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'double [10]' can be declared 'const'
+  const double *p_local2 = &p_local1[2]; // Could be `const double *const`, but warning deactivated by default
+
+  double p_local3[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local3' of type 'double [10]' can be declared 'const'
+  const double &const_ref = p_local3[2];
+
+  double p_local4[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local4' of type 'double [10]' can be declared 'const'
+  const double *const_ptr;
+  const_ptr = &p_local4[2];
+
+  double p_local5[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local5' of type 'double [10]' can be declared 'const'
+  const_handle(p_local5[2]);
+  double p_local6[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local6' of type 'double [10]' can be declared 'const'
+  const_handle(&p_local6[2]);
+}
+
+void range_for() {
+  int np_local0[2] = {1, 2};
+  for (int &non_const_ref : np_local0) {
+    non_const_ref = 42;
+  }
+
+  int np_local1[2] = {1, 2};
+  for (auto &non_const_ref : np_local1) {
+    non_const_ref = 43;
+  }
+
+  int np_local2[2] = {1, 2};
+  for (auto &&non_const_ref : np_local2) {
+    non_const_ref = 44;
+  }
+
+  // FIXME the warning message is suboptimal. It could be defined as
+  // `int *const np_local3[2]` because the pointers are not reseated.
+  // But this is not easily deducable from the warning.
+  int *np_local3[2] = {&np_local0[0], &np_local0[1]};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'np_local3' of type 'int *[2]' can be declared 'const'
+  for (int *non_const_ptr : np_local3) {
+    *non_const_ptr = 45;
+  }
+
+  // FIXME same as above, but silenced
+  int *const np_local4[2] = {&np_local0[0], &np_local0[1]};
+  for (auto *non_const_ptr : np_local4) {
+    *non_const_ptr = 46;
+  }
+
+  int p_local0[2] = {1, 2};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int [2]' can be declared 'const'
+  for (int value : p_local0) {
+    // CHECK-MESSAGES: [[@LINE-1]]:8: warning: variable 'value' of type 'int' can be declared 'const'
+  }
+
+  int p_local1[2] = {1, 2};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'int [2]' can be declared 'const'
+  for (const int &const_ref : p_local1) {
+  }
+
+  int *p_local2[2] = {&np_local0[0], &np_local0[1]};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'int *[2]' can be declared 'const'
+  for (const int *con_ptr : p_local2) {
+  }
+
+  int *p_local3[2] = {nullptr, nullptr};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local3' of type 'int *[2]' can be declared 'const'
+  for (const auto *con_ptr : p_local3) {
+  }
+}
+
+inline void *operator new(decltype(sizeof(void *)), void *p) { return p; }
+
+struct Value {
+};
+void placement_new() {
+  Value Mem;
+  Value *V = new (&Mem) Value;
+}
+
+struct ModifyingConversion {
+  operator int() { return 15; }
+};
+struct NonModifyingConversion {
+  operator int() const { return 15; }
+};
+void conversion_operators() {
+  ModifyingConversion np_local0;
+  NonModifyingConversion p_local0;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'NonModifyingConversion' can be declared 'const'
+
+  int np_local1 = np_local0;
+  np_local1 = p_local0;
+}
+
+void casts() {
+  decltype(sizeof(void *)) p_local0 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'decltype(sizeof(void *))' (aka 'unsigned long') can be declared 'const'
+  auto np_local0 = reinterpret_cast<void *>(p_local0);
+  np_local0 = nullptr;
+
+  int p_local1 = 43;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'int' can be declared 'const'
+  short p_local2 = static_cast<short>(p_local1);
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'short' can be declared 'const'
+
+  int np_local1 = p_local2;
+  int &np_local2 = static_cast<int &>(np_local1);
+  np_local2 = 5;
+}
+
+void ternary_operator() {
+  int np_local0 = 1, np_local1 = 2;
+  int &np_local2 = true ? np_local0 : np_local1;
+  np_local2 = 2;
+
+  int p_local0 = 3, np_local3 = 5;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
+  const int &np_local4 = true ? p_local0 : ++np_local3;
+
+  int np_local5[3] = {1, 2, 3};
+  int &np_local6 = np_local5[1] < np_local5[2] ? np_local5[0] : np_local5[2];
+  np_local6 = 42;
+
+  int np_local7[3] = {1, 2, 3};
+  int *np_local8 = np_local7[1] < np_local7[2] ? &np_local7[0] : &np_local7[2];
+  *np_local8 = 42;
+}
+
+// taken from http://www.cplusplus.com/reference/type_traits/integral_constant/
+template <typename T, T v>
+struct integral_constant {
+  static constexpr T value = v;
+  using value_type = T;
+  using type = integral_constant<T, v>;
+  constexpr operator T() { return v; }
+};
+
+template <typename T>
+struct is_integral : integral_constant<bool, false> {};
+template <>
+struct is_integral<int> : integral_constant<bool, true> {};
+
+template <typename T>
+struct not_integral : integral_constant<bool, false> {};
+template <>
+struct not_integral<double> : integral_constant<bool, true> {};
+
+// taken from http://www.cplusplus.com/reference/type_traits/enable_if/
+template <bool Cond, typename T = void>
+struct enable_if {};
+
+template <typename T>
+struct enable_if<true, T> { using type = T; };
+
+template <typename T>
+struct TMPClass {
+  T alwaysConst() const { return T{}; }
+
+  template <typename T2 = T, typename = typename enable_if<is_integral<T2>::value>::type>
+  T sometimesConst() const { return T{}; }
+
+  template <typename T2 = T, typename = typename enable_if<not_integral<T2>::value>::type>
+  T sometimesConst() { return T{}; }
+};
+
+void meta_type() {
+  TMPClass<int> p_local0;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'TMPClass<int>' can be declared 'const'
+  p_local0.alwaysConst();
+  p_local0.sometimesConst();
+
+  TMPClass<double> p_local1;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'TMPClass<double>' can be declared 'const'
+  p_local1.alwaysConst();
+
+  TMPClass<double> np_local0;
+  np_local0.alwaysConst();
+  np_local0.sometimesConst();
+}
Index: clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-transform-values.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-transform-values.cpp
@@ -0,0 +1,166 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-const-correctness %t -- \
+// RUN:   -config="{CheckOptions: [\
+// RUN:   {key: 'cppcoreguidelines-const-correctness.TransformValues', value: 1},\
+// RUN:   ]}" --
+
+bool global;
+char np_global = 0; // globals can't be known to be const
+
+namespace foo {
+int scoped;
+float np_scoped = 1; // namespace variables are like globals
+} // namespace foo
+
+// Lambdas should be ignored, because they do not follow the normal variable
+// semantic (e.g. the type is only known to the compiler).
+void lambdas() {
+  auto Lambda = [](int i) { return i < 0; };
+}
+
+void some_function(double, wchar_t);
+
+void some_function(double np_arg0, wchar_t np_arg1) {
+  int p_local0 = 2;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
+  // CHECK-FIXES: const
+}
+
+void nested_scopes() {
+  {
+    int p_local1 = 42;
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: variable 'p_local1' of type 'int' can be declared 'const'
+    // CHECK-FIXES: const
+  }
+}
+
+template <typename T>
+void define_locals(T np_arg0, T &np_arg1, int np_arg2) {
+  T p_local0 = 0;
+  int p_local1 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'int' can be declared 'const'
+  // CHECK-FIXES: const
+}
+
+void template_instantiation() {
+  const int np_local0 = 42;
+  int np_local1 = 42;
+
+  define_locals(np_local0, np_local1, np_local0);
+  define_locals(np_local1, np_local1, np_local1);
+}
+
+struct ConstNonConstClass {
+  ConstNonConstClass();
+  ConstNonConstClass(double &np_local0);
+  double nonConstMethod() {}
+  double constMethod() const {}
+  double modifyingMethod(double &np_arg0) const;
+
+  double NonConstMember;
+  const double ConstMember;
+
+  double &NonConstMemberRef;
+  const double &ConstMemberRef;
+
+  double *NonConstMemberPtr;
+  const double *ConstMemberPtr;
+};
+
+void direct_class_access() {
+  ConstNonConstClass p_local0;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'ConstNonConstClass' can be declared 'const'
+  // CHECK-FIXES: const
+  p_local0.constMethod();
+}
+
+void class_access_array() {
+  ConstNonConstClass p_local0[2];
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'ConstNonConstClass [2]' can be declared 'const'
+  // CHECK-FIXES: const
+  p_local0[0].constMethod();
+}
+
+struct MyVector {
+  double *begin();
+  const double *begin() const;
+
+  double *end();
+  const double *end() const;
+
+  double &operator[](int index);
+  double operator[](int index) const;
+
+  double values[100];
+};
+
+void vector_usage() {
+  double p_local0[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double [10]' can be declared 'const'
+  // CHECK-FIXES: const
+}
+
+void range_for() {
+  int np_local0[2] = {1, 2};
+  int *np_local3[2] = {&np_local0[0], &np_local0[1]};
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'np_local3' of type 'int *[2]' can be declared 'const'
+  // CHECK-FIXES: const
+  for (int *non_const_ptr : np_local3) {
+    *non_const_ptr = 45;
+  }
+}
+
+void casts() {
+  decltype(sizeof(void *)) p_local0 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'decltype(sizeof(void *))' (aka 'unsigned long') can be declared 'const'
+  // CHECK-FIXES: const
+}
+
+// taken from http://www.cplusplus.com/reference/type_traits/integral_constant/
+template <typename T, T v>
+struct integral_constant {
+  static constexpr T value = v;
+  using value_type = T;
+  using type = integral_constant<T, v>;
+  constexpr operator T() { return v; }
+};
+
+template <typename T>
+struct is_integral : integral_constant<bool, false> {};
+template <>
+struct is_integral<int> : integral_constant<bool, true> {};
+
+template <typename T>
+struct not_integral : integral_constant<bool, false> {};
+template <>
+struct not_integral<double> : integral_constant<bool, true> {};
+
+// taken from http://www.cplusplus.com/reference/type_traits/enable_if/
+template <bool Cond, typename T = void>
+struct enable_if {};
+
+template <typename T>
+struct enable_if<true, T> { using type = T; };
+
+template <typename T>
+struct TMPClass {
+  T alwaysConst() const { return T{}; }
+
+  template <typename T2 = T, typename = typename enable_if<is_integral<T2>::value>::type>
+  T sometimesConst() const { return T{}; }
+
+  template <typename T2 = T, typename = typename enable_if<not_integral<T2>::value>::type>
+  T sometimesConst() { return T{}; }
+};
+
+void meta_type() {
+  TMPClass<int> p_local0;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'TMPClass<int>' can be declared 'const'
+  // CHECK-FIXES: const
+  p_local0.alwaysConst();
+  p_local0.sometimesConst();
+
+  TMPClass<double> p_local1;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'TMPClass<double>' can be declared 'const'
+  // CHECK-FIXES: const
+  p_local1.alwaysConst();
+}
Index: clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-transform-pointer-as-values.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-transform-pointer-as-values.cpp
@@ -0,0 +1,13 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-const-correctness %t \
+// RUN: -config='{CheckOptions: \
+// RUN:  [{key: "cppcoreguidelines-const-correctness.AnalyzeValues", value: 1},\
+// RUN:   {key: "cppcoreguidelines-const-correctness.WarnPointersAsValues", value: 1}, \
+// RUN:   {key: "cppcoreguidelines-const-correctness.TransformPointersAsValues", value: 1},\
+// RUN:  ]}' --
+
+void potential_const_pointer() {
+  double np_local0[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  double *p_local0 = &np_local0[1];
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double *' can be declared 'const'
+  // CHECK-FIXES: const
+}
Index: clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-pointer-as-values.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/cppcoreguidelines-const-correctness-pointer-as-values.cpp
@@ -0,0 +1,11 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-const-correctness %t \
+// RUN: -config='{CheckOptions: \
+// RUN:  [{key: "cppcoreguidelines-const-correctness.AnalyzeValues", value: 1},\
+// RUN:   {key: "cppcoreguidelines-const-correctness.WarnPointersAsValues", value: 1}]}' \
+// RUN: --
+
+void potential_const_pointer() {
+  double np_local0[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  double *p_local0 = &np_local0[1];
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double *' can be declared 'const'
+}
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -195,6 +195,7 @@
    cppcoreguidelines-avoid-goto
    cppcoreguidelines-avoid-magic-numbers (redirects to readability-magic-numbers) <cppcoreguidelines-avoid-magic-numbers>
    cppcoreguidelines-c-copy-assignment-signature (redirects to misc-unconventional-assign-operator) <cppcoreguidelines-c-copy-assignment-signature>
+   cppcoreguidelines-const-correctness
    cppcoreguidelines-explicit-virtual-functions (redirects to modernize-use-override) <cppcoreguidelines-explicit-virtual-functions>
    cppcoreguidelines-init-variables
    cppcoreguidelines-interfaces-global-init
Index: clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-const-correctness.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-const-correctness.rst
@@ -0,0 +1,68 @@
+.. title:: clang-tidy - cppcoreguidelines-const-correctness
+
+cppcoreguidelines-const-correctness
+===================================
+
+This check implements detection of local variables which could be declared as
+``const``, but are not. Declaring variables as ``const`` is required by many
+coding guidelines, such as:
+`CppCoreGuidelines ES.25 <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es25-declare-an-object-const-or-constexpr-unless-you-want-to-modify-its-value-later-on>`_
+and `High Integrity C++ 7.1.2 <http://www.codingstandard.com/rule/7-1-2-use-const-whenever-possible/>`_.
+
+Please note that this analysis is type-based only. Variables that are not modified
+but non-const handles might escape out of the scope are not diagnosed as potential
+``const``.
+
+.. code-block:: c++
+  
+  // Declare a variable, which is not ``const`` ...
+  int i = 42;
+  // but use it as read-only. This means that `i` can be declared ``const``.
+  int result = i * i;
+
+The check analyzes values, pointers and references (if configured that way).
+For better understanding some code samples:
+
+.. code-block:: c++
+
+  // Normal values like built-ins or objects.
+  int potential_const_int = 42;
+  int copy_of_value = potential_const_int;
+
+  MyClass could_be_const;
+  could_be_const.const_qualified_method();
+
+  // References can be declared const as well.
+  int &reference_value = potential_const_int;
+  int another_copy = reference_value;
+
+  // Similar behaviour for pointers.
+  int *pointer_variable = &potential_const_int;
+  int last_copy = *pointer_variable;
+
+
+Options
+-------
+
+.. option:: AnalyzeValues (default = 1)
+
+  Enable or disable the analysis of ordinary value variables, like ``int i = 42;``
+
+.. option:: AnalyzeReferences (default = 1)
+
+  Enable or disable the analysis of reference variables, like ``int &ref = i;``
+
+.. option:: WarnPointersAsValues (default = 0)
+
+  This option enables the suggestion for ``const`` of the pointer itself.
+  Pointer values have two possibilities to be ``const``, the pointer itself
+  and the value pointing to. 
+
+  .. code-block:: c++
+
+    const int value = 42;
+    const int * const pointer_variable = &value;
+    
+    // The following operations are forbidden for `pointer_variable`.
+    // *pointer_variable = 44;
+    // pointer_variable = nullptr;
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -102,6 +102,11 @@
 - New :doc:`cppcoreguidelines-init-variables
   <clang-tidy/checks/cppcoreguidelines-init-variables>` check.
 
+- New :doc:`cppcoreguidelines-const-correctness
+  <clang-tidy/checks/cppcoreguidelines-const-correctness>` check.
+
+  Suggest adding ``const`` to unmodified local variables.
+
 - New :doc:`darwin-dispatch-once-nonstatic
   <clang-tidy/checks/darwin-dispatch-once-nonstatic>` check.
 
Index: clang-tools-extra/clang-tidy/utils/LexerUtils.h
===================================================================
--- clang-tools-extra/clang-tidy/utils/LexerUtils.h
+++ clang-tools-extra/clang-tidy/utils/LexerUtils.h
@@ -39,6 +39,8 @@
                                         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 @@
 
     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...))
Index: clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
===================================================================
--- clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
+++ clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
@@ -47,6 +47,9 @@
                                      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: clang-tools-extra/clang-tidy/utils/FixItHintUtils.h
===================================================================
--- clang-tools-extra/clang-tidy/utils/FixItHintUtils.h
+++ clang-tools-extra/clang-tidy/utils/FixItHintUtils.h
@@ -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,35 @@
 /// Creates fix to make ``VarDecl`` const qualified.
 FixItHint changeVarDeclToConst(const VarDecl &Var);
 
+/// This enum defines where the 'const' shall be preferably added.
+enum class QualifierPolicy {
+  Left,  // Add the `const` always to the left side, if that is possible.
+  Right, // Add the `const` always to the right side.
+};
+
+/// This enum defines which entity is the target for adding the 'const'. 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 goes for 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 make ``VarDecl`` const qualified. Only valid if
+/// `Var` is isolated in written code. `int foo = 42;`
+///
+/// If the 'FixItHint' would be applied inside a macro or at an invalid
+/// \c SourceLocation it is not returned.
+Optional<FixItHint>
+addQualifierToVarDecl(const VarDecl &Var, DeclSpec::TQ Qualifier,
+                      QualifierTarget CT = QualifierTarget::Pointee,
+                      QualifierPolicy CP = QualifierPolicy::Left,
+                      const ASTContext *Context = nullptr);
+
 } // namespace fixit
 } // namespace utils
 } // namespace tidy
Index: clang-tools-extra/clang-tidy/utils/FixItHintUtils.cpp
===================================================================
--- clang-tools-extra/clang-tidy/utils/FixItHintUtils.cpp
+++ clang-tools-extra/clang-tidy/utils/FixItHintUtils.cpp
@@ -26,10 +26,209 @@
   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));
 }
+static bool isValueType(QualType QT) { return isValueType(QT.getTypePtr()); }
+static bool isArrayType(QualType QT) { return isa<ArrayType>(QT.getTypePtr()); }
+static bool isReferenceType(QualType QT) {
+  return isa<ReferenceType>(QT.getTypePtr());
+}
+static bool isPointerType(const Type *T) { return isa<PointerType>(T); }
+static bool isPointerType(QualType QT) {
+  return isPointerType(QT.getTypePtr());
+}
+static bool isMemberOrFunctionPointer(QualType QT) {
+  return (isPointerType(QT) && 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) {
+  Token T;
+  auto PreviousTokenLParen = [&]() {
+    T = lexer::getPreviousToken(Start, Context.getSourceManager(),
+                                Context.getLangOpts());
+    return T.is(tok::l_paren);
+  };
+  while (PreviousTokenLParen()) {
+    if (locDangerous(Start))
+      return None;
+    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)) + llvm::Twine(' '))
+      .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 && isPointerType(Pointee)) {
+    // 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));
+  }
+
+  llvm_unreachable("All paths should have been handled");
+}
+
+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,
+                                          DeclSpec::TQ Qualifier,
+                                          QualifierTarget QualTarget,
+                                          QualifierPolicy QualPolicy,
+                                          const ASTContext *Context) {
+  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 (isReferenceType(ParenStrippedType))
+    return changeReferencee(Var, Qualifier, Var.getType()->getPointeeType(),
+                            QualTarget, QualPolicy, *Context);
+
+  if (isMemberOrFunctionPointer(ParenStrippedType))
+    return changePointerItself(Var, Qualifier, *Context);
+
+  if (isPointerType(ParenStrippedType))
+    return changePointer(Var, Qualifier,
+                         ParenStrippedType->getPointeeType().getTypePtr(),
+                         QualTarget, QualPolicy, *Context);
+
+  if (isArrayType(ParenStrippedType)) {
+    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 (isPointerType(AT))
+      return changePointer(Var, Qualifier, AT->getPointeeType().getTypePtr(),
+                           QualTarget, QualPolicy, *Context);
+  }
+
+  return {};
+  llvm_unreachable(
+      "All possible combinations should have been handled already");
+}
 } // namespace fixit
 } // namespace utils
 } // namespace tidy
Index: clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -163,14 +163,15 @@
     // whether it is const or not as constness can differ between definition and
     // declaration.
     if (!CurrentParam.getType().getCanonicalType().isConstQualified())
-      Diag << utils::fixit::changeVarDeclToConst(CurrentParam);
+      Diag << *utils::fixit::addQualifierToVarDecl(CurrentParam,
+                                                   DeclSpec::TQ::TQ_const);
   }
 }
 
 void UnnecessaryValueParamCheck::registerPPCallbacks(
     const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
-  Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
-                                                       IncludeStyle);
+  Inserter =
+      std::make_unique<utils::IncludeInserter>(SM, getLangOpts(), IncludeStyle);
   PP->addPPCallbacks(Inserter->CreatePPCallbacks());
 }
 
Index: clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
===================================================================
--- clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
+++ clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
@@ -22,7 +22,7 @@
                  DiagnosticBuilder &Diagnostic) {
   Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context);
   if (!Var.getType().isLocalConstQualified())
-    Diagnostic << utils::fixit::changeVarDeclToConst(Var);
+    Diagnostic << *utils::fixit::addQualifierToVarDecl(Var, DeclSpec::TQ::TQ_const);
 }
 
 } // namespace
Index: clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp
+++ clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp
@@ -78,7 +78,7 @@
            "copy in each iteration; consider making this a reference")
       << utils::fixit::changeVarDeclToReference(LoopVar, Context);
   if (!LoopVar.getType().isConstQualified())
-    Diagnostic << utils::fixit::changeVarDeclToConst(LoopVar);
+    Diagnostic << *utils::fixit::addQualifierToVarDecl(LoopVar, DeclSpec::TQ::TQ_const);
   return true;
 }
 
@@ -104,7 +104,7 @@
     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::addQualifierToVarDecl(LoopVar, DeclSpec::TQ::TQ_const)
         << utils::fixit::changeVarDeclToReference(LoopVar, Context);
     return true;
   }
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -15,6 +15,7 @@
 #include "../modernize/UseOverrideCheck.h"
 #include "../readability/MagicNumbersCheck.h"
 #include "AvoidGotoCheck.h"
+#include "ConstCorrectnessCheck.h"
 #include "InitVariablesCheck.h"
 #include "InterfacesGlobalInitCheck.h"
 #include "MacroUsageCheck.h"
@@ -48,6 +49,8 @@
         "cppcoreguidelines-avoid-goto");
     CheckFactories.registerCheck<readability::MagicNumbersCheck>(
         "cppcoreguidelines-avoid-magic-numbers");
+    CheckFactories.registerCheck<ConstCorrectnessCheck>(
+        "cppcoreguidelines-const-correctness");
     CheckFactories.registerCheck<modernize::UseOverrideCheck>(
         "cppcoreguidelines-explicit-virtual-functions");
     CheckFactories.registerCheck<InitVariablesCheck>(
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.h
@@ -0,0 +1,60 @@
+//===--- ConstCorrectnessCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_CONSTCORRECTNESSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_CONSTCORRECTNESSCHECK_H
+
+#include "../ClangTidy.h"
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+
+namespace clang {
+namespace tidy {
+
+namespace cppcoreguidelines {
+
+/// This check warns on variables which could be declared const but are not.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-const.html
+class ConstCorrectnessCheck : public ClangTidyCheck {
+public:
+  ConstCorrectnessCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context),
+        AnalyzeValues(Options.get("AnalyzeValues", 1)),
+        AnalyzeReferences(Options.get("AnalyzeReferences", 1)),
+        WarnPointersAsValues(Options.get("WarnPointersAsValues", 0)),
+        TransformValues(Options.get("TransformValues", 1)),
+        TransformReferences(Options.get("TransformReferences", 1)),
+        // TransformPointees(Options.get("TransformPointees", 0)),
+        TransformPointersAsValues(Options.get("TransformPointersAsValues", 0)) {}
+
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void registerScope(const CompoundStmt *LocalScope, ASTContext *Context);
+
+  using MutationAnalyzer = std::unique_ptr<ExprMutationAnalyzer>;
+  llvm::DenseMap<const CompoundStmt *, MutationAnalyzer> ScopesCache;
+
+  const bool AnalyzeValues;
+  const bool AnalyzeReferences;
+  const bool WarnPointersAsValues;
+
+  const bool TransformValues;
+  const bool TransformReferences;
+  const bool TransformPointees = false;
+  const bool TransformPointersAsValues;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_CONSTCORRECTNESSCHECK_H
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.cpp
@@ -0,0 +1,160 @@
+//===--- ConstCorrectnessCheck.cpp - clang-tidy -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ConstCorrectnessCheck.h"
+#include "../utils/FixItHintUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+namespace {
+// FIXME: This matcher exists in some other code-review as well.
+// It should probably move to ASTMatchers.
+AST_MATCHER(VarDecl, isLocal) { return Node.isLocalVarDecl(); }
+AST_MATCHER_P(DeclStmt, containsDeclaration2,
+              ast_matchers::internal::Matcher<Decl>, InnerMatcher) {
+  return ast_matchers::internal::matchesFirstInPointerRange(
+      InnerMatcher, Node.decl_begin(), Node.decl_end(), Finder, Builder);
+}
+AST_MATCHER(ReferenceType, isSpelledAsLValue) {
+  return Node.isSpelledAsLValue();
+}
+} // namespace
+
+void ConstCorrectnessCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "AnalyzeValues", AnalyzeValues);
+  Options.store(Opts, "AnalyzeReferences", AnalyzeReferences);
+  Options.store(Opts, "WarnPointersAsValues", WarnPointersAsValues);
+
+  Options.store(Opts, "TransformValues", TransformValues);
+  Options.store(Opts, "TransformReferences", TransformReferences);
+  // FIXME: Pointee analysis not proper yet?
+  // Options.store(Opts, "TransformPointees", TransformPointees);
+  Options.store(Opts, "TransformPointersAsValues", TransformPointersAsValues);
+}
+
+void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
+  const auto ConstType = hasType(isConstQualified());
+  const auto ConstReference = hasType(references(isConstQualified()));
+  const auto RValueReference = hasType(
+      referenceType(anyOf(rValueReferenceType(), unless(isSpelledAsLValue()))));
+  const auto TemplateType = anyOf(hasType(templateTypeParmType()),
+                                  hasType(substTemplateTypeParmType()));
+
+  // Match local variables which could be 'const' if not modified later.
+  // Example: `int i = 10` would match `int i`.
+  const auto LocalValDecl = varDecl(allOf(
+      isLocal(), hasInitializer(anything()),
+      unless(anyOf(ConstType, ConstReference, TemplateType, RValueReference,
+                   hasType(cxxRecordDecl(isLambda())), isImplicit()))));
+
+  // Match the function scope for which the analysis of all local variables
+  // shall be run.
+  const auto FunctionScope = functionDecl(hasBody(
+      compoundStmt(findAll(declStmt(containsDeclaration2(
+                                        LocalValDecl.bind("new-local-value")))
+                               .bind("decl-stmt")))
+          .bind("scope")));
+
+  Finder->addMatcher(FunctionScope, this);
+}
+
+/// Classify for a variable in what the Const-Check is interested.
+enum class VariableCategory { Value, Reference, Pointer };
+
+void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *LocalScope = Result.Nodes.getNodeAs<CompoundStmt>("scope");
+  assert(LocalScope && "Did not match scope for local variable");
+  registerScope(LocalScope, Result.Context);
+
+  const auto *Variable = Result.Nodes.getNodeAs<VarDecl>("new-local-value");
+  assert(Variable && "Did not match local variable definition");
+
+  VariableCategory VC = VariableCategory::Value;
+  if (Variable->getType()->isReferenceType())
+    VC = VariableCategory::Reference;
+  if (Variable->getType()->isPointerType())
+    VC = VariableCategory::Pointer;
+
+  // Each variable can only in one category: Value, Pointer, Reference.
+  // Analysis can be controlled for every category.
+  if (VC == VariableCategory::Reference && !AnalyzeReferences)
+    return;
+
+  if (VC == VariableCategory::Pointer && !WarnPointersAsValues)
+    return;
+
+  if (VC == VariableCategory::Value && !AnalyzeValues)
+    return;
+
+  // Offload const-analysis to utility function.
+  if (ScopesCache[LocalScope]->isMutated(Variable))
+    return;
+
+  auto Diag = diag(Variable->getBeginLoc(),
+                   "variable %0 of type %1 can be declared 'const'")
+              << Variable << Variable->getType();
+
+  const auto *VarDeclStmt = Result.Nodes.getNodeAs<DeclStmt>("decl-stmt");
+
+  // It can not be guaranteed that the variable is declared isolated, therefore
+  // a transformation might effect the other variables as well and be incorrect.
+  if (VarDeclStmt == nullptr || !VarDeclStmt->isSingleDecl())
+    return;
+
+  using namespace utils::fixit;
+  using llvm::Optional;
+  if (VC == VariableCategory::Value && TransformValues) {
+    if (Optional<FixItHint> Fix = addQualifierToVarDecl(
+            *Variable, DeclSpec::TQ_const, QualifierTarget::Value,
+            QualifierPolicy::Right, Result.Context))
+      Diag << *Fix;
+    return;
+  }
+
+  if (VC == VariableCategory::Reference && TransformReferences) {
+    if (Optional<FixItHint> Fix = addQualifierToVarDecl(
+            *Variable, DeclSpec::TQ_const, QualifierTarget::Value,
+            QualifierPolicy::Right, Result.Context))
+      Diag << *Fix;
+    return;
+  }
+
+  if (VC == VariableCategory::Pointer) {
+    if (WarnPointersAsValues && TransformPointersAsValues) {
+      if (Optional<FixItHint> Fix = addQualifierToVarDecl(
+              *Variable, DeclSpec::TQ_const, QualifierTarget::Value,
+              QualifierPolicy::Right, Result.Context))
+        Diag << *Fix;
+    }
+    if (TransformPointees) {
+      if (Optional<FixItHint> Fix = addQualifierToVarDecl(
+              *Variable, DeclSpec::TQ_const, QualifierTarget::Pointee,
+              QualifierPolicy::Right, Result.Context))
+        Diag << *Fix;
+    }
+    return;
+  }
+}
+
+void ConstCorrectnessCheck::registerScope(const CompoundStmt *LocalScope,
+                                          ASTContext *Context) {
+  if (ScopesCache.find(LocalScope) == ScopesCache.end())
+    ScopesCache.insert(std::make_pair(
+        LocalScope,
+        std::make_unique<ExprMutationAnalyzer>(*LocalScope, *Context)));
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 add_clang_library(clangTidyCppCoreGuidelinesModule
   AvoidGotoCheck.cpp
+  ConstCorrectnessCheck.cpp
   CppCoreGuidelinesTidyModule.cpp
   InitVariablesCheck.cpp
   InterfacesGlobalInitCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to