JonasToth updated this revision to Diff 174828.
JonasToth added a comment.
add tests for different template instantiations
Repository:
rCTE Clang Tools Extra
https://reviews.llvm.org/D54395
Files:
clang-tidy/performance/ForRangeCopyCheck.cpp
clang-tidy/performance/UnnecessaryCopyInitialization.cpp
clang-tidy/performance/UnnecessaryValueParamCheck.cpp
clang-tidy/utils/FixItHintUtils.cpp
clang-tidy/utils/FixItHintUtils.h
clang-tidy/utils/LexerUtils.cpp
clang-tidy/utils/LexerUtils.h
unittests/clang-tidy/AddConstTest.cpp
unittests/clang-tidy/CMakeLists.txt
Index: unittests/clang-tidy/CMakeLists.txt
===================================================================
--- unittests/clang-tidy/CMakeLists.txt
+++ 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: unittests/clang-tidy/AddConstTest.cpp
===================================================================
--- /dev/null
+++ unittests/clang-tidy/AddConstTest.cpp
@@ -0,0 +1,857 @@
+#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 <ConstTarget CT = ConstTarget::Pointee,
+ ConstPolicy CP = ConstPolicy::Left>
+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::changeVarDeclToConst;
+ Optional<FixItHint> Fix = changeVarDeclToConst(*D, CT, CP, Result.Context);
+ auto Diag = diag(D->getBeginLoc(), "doing const transformation");
+ if (Fix)
+ Diag << *Fix;
+ }
+};
+} // namespace
+
+namespace test {
+using PointeeLTransform =
+ ConstTransform<ConstTarget::Pointee, ConstPolicy::Left>;
+using PointeeRTransform =
+ ConstTransform<ConstTarget::Pointee, ConstPolicy::Right>;
+
+using ValueLTransform = ConstTransform<ConstTarget::Value, ConstPolicy::Left>;
+using ValueRTransform = ConstTransform<ConstTarget::Value, ConstPolicy::Right>;
+
+// ----------------------------------------------------------------------------
+// Test Value-like types. Everything with indirection is done later.
+// ----------------------------------------------------------------------------
+
+// TODO: Template-code
+
+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, 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, 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)));
+}
+} // namespace test
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/utils/LexerUtils.h
===================================================================
--- clang-tidy/utils/LexerUtils.h
+++ clang-tidy/utils/LexerUtils.h
@@ -39,14 +39,16 @@
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())
return SourceLocation();
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-tidy/utils/LexerUtils.cpp
===================================================================
--- clang-tidy/utils/LexerUtils.cpp
+++ clang-tidy/utils/LexerUtils.cpp
@@ -48,6 +48,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-tidy/utils/FixItHintUtils.h
===================================================================
--- clang-tidy/utils/FixItHintUtils.h
+++ clang-tidy/utils/FixItHintUtils.h
@@ -21,8 +21,33 @@
/// \brief Creates fix to make ``VarDecl`` a reference by adding ``&``.
FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context);
-/// \brief Creates fix to make ``VarDecl`` const qualified.
-FixItHint changeVarDeclToConst(const VarDecl &Var);
+/// This enum defines where the 'const' shall be preferably added.
+enum class ConstPolicy {
+ 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 ConstTarget {
+ 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> changeVarDeclToConst(const VarDecl &Var,
+ ConstTarget CT = ConstTarget::Pointee,
+ ConstPolicy CP = ConstPolicy::Left,
+ const ASTContext *Context = nullptr);
} // namespace fixit
} // namespace utils
Index: clang-tidy/utils/FixItHintUtils.cpp
===================================================================
--- clang-tidy/utils/FixItHintUtils.cpp
+++ clang-tidy/utils/FixItHintUtils.cpp
@@ -27,10 +27,186 @@
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);
+}
+
+static Optional<FixItHint> changeValue(const VarDecl &Var, ConstTarget CT,
+ ConstPolicy CP,
+ const ASTContext &Context) {
+ switch (CP) {
+ case ConstPolicy::Left:
+ return fixIfNotDangerous(Var.getTypeSpecStartLoc(), "const ");
+ case ConstPolicy::Right:
+ Optional<SourceLocation> IgnoredParens =
+ skipLParensBackwards(Var.getLocation(), Context);
+
+ if (IgnoredParens)
+ return fixIfNotDangerous(*IgnoredParens, "const ");
+ return None;
+ }
+}
+
+static Optional<FixItHint> changePointerItself(const VarDecl &Var,
+ const ASTContext &Context) {
+ if (locDangerous(Var.getLocation()))
+ return None;
+
+ Optional<SourceLocation> IgnoredParens =
+ skipLParensBackwards(Var.getLocation(), Context);
+ if (IgnoredParens)
+ return fixIfNotDangerous(*IgnoredParens, "const ");
+ return None;
+}
+
+static Optional<FixItHint> changePointer(const VarDecl &Var,
+ const Type *Pointee, ConstTarget CT,
+ ConstPolicy CP,
+ const ASTContext &Context) {
+ // The pointer itself shall be marked as `const`. This is always right
+ // of the '*' or in front of the identifier.
+ if (CT == ConstTarget::Value)
+ return changePointerItself(Var, Context);
+
+ // Mark the pointee `const` that is a normal value (`int* p = nullptr;`).
+ if (CT == ConstTarget::Pointee && isValueType(Pointee)) {
+ // Adding the `const` on the left side is just the beginning of the type
+ // specification. (`const int* p = nullptr;`)
+ if (CP == ConstPolicy::Left)
+ return fixIfNotDangerous(Var.getTypeSpecStartLoc(), "const ");
+
+ // 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 (CP == ConstPolicy::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, " const");
+ return None;
+ }
+ }
+
+ if (CT == ConstTarget::Pointee && isPointerType(Pointee)) {
+ // Adding the `const` to the pointee if the pointee is a pointer
+ // is the same as 'CP == 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, " const");
+ }
+
+ llvm_unreachable("All paths should have been handled");
+}
+
+static Optional<FixItHint> changeReferencee(const VarDecl &Var,
+ QualType Pointee, ConstTarget CT,
+ ConstPolicy CP,
+ const ASTContext &Context) {
+ if (CP == ConstPolicy::Left && isValueType(Pointee))
+ return fixIfNotDangerous(Var.getTypeSpecStartLoc(), "const ");
+
+ 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, " const");
+
+ return None;
+}
+
+Optional<FixItHint> changeVarDeclToConst(const VarDecl &Var, ConstTarget CT,
+ ConstPolicy CP,
+ const ASTContext *Context) {
+ assert((CP == ConstPolicy::Left || CP == ConstPolicy::Right) &&
+ "Unexpected Insertion Policy");
+ assert((CT == ConstTarget::Pointee || CT == ConstTarget::Value) &&
+ "Unexpected Target");
+
+ QualType ParenStrippedType = Var.getType().IgnoreParens();
+ if (isValueType(ParenStrippedType))
+ return changeValue(Var, CT, CP, *Context);
+
+ if (isReferenceType(ParenStrippedType))
+ return changeReferencee(Var, Var.getType()->getPointeeType(), CT, CP,
+ *Context);
+
+ if (isMemberOrFunctionPointer(ParenStrippedType))
+ return changePointerItself(Var, *Context);
+
+ if (isPointerType(ParenStrippedType))
+ return changePointer(Var, ParenStrippedType->getPointeeType().getTypePtr(),
+ CT, CP, *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, CT, CP, *Context);
+
+ if (isPointerType(AT))
+ return changePointer(Var, AT->getPointeeType().getTypePtr(), CT, CP,
+ *Context);
+ }
+
+ llvm_unreachable(
+ "All possible combinations should have been handled already");
+}
} // namespace fixit
} // namespace utils
} // namespace tidy
Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===================================================================
--- clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -164,7 +164,7 @@
// 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::changeVarDeclToConst(CurrentParam);
}
}
Index: clang-tidy/performance/UnnecessaryCopyInitialization.cpp
===================================================================
--- clang-tidy/performance/UnnecessaryCopyInitialization.cpp
+++ clang-tidy/performance/UnnecessaryCopyInitialization.cpp
@@ -23,7 +23,7 @@
DiagnosticBuilder &Diagnostic) {
Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context);
if (!Var.getType().isLocalConstQualified())
- Diagnostic << utils::fixit::changeVarDeclToConst(Var);
+ Diagnostic << *utils::fixit::changeVarDeclToConst(Var);
}
} // namespace
Index: clang-tidy/performance/ForRangeCopyCheck.cpp
===================================================================
--- clang-tidy/performance/ForRangeCopyCheck.cpp
+++ clang-tidy/performance/ForRangeCopyCheck.cpp
@@ -79,7 +79,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::changeVarDeclToConst(LoopVar);
return true;
}
@@ -105,7 +105,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::changeVarDeclToConst(LoopVar)
<< utils::fixit::changeVarDeclToReference(LoopVar, Context);
return true;
}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits