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; } 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/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/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 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(T) || isa(T) || isa(T) || + isa(T)); } +static bool isValueType(QualType QT) { return isValueType(QT.getTypePtr()); } +static bool isArrayType(QualType QT) { return isa(QT.getTypePtr()); } +static bool isReferenceType(QualType QT) { + return isa(QT.getTypePtr()); +} +static bool isPointerType(const Type *T) { return isa(T); } +static bool isPointerType(QualType QT) { + return isPointerType(QT.getTypePtr()); +} +static bool isMemberOrFunctionPointer(QualType QT) { + return (isPointerType(QT) && QT->isFunctionPointerType()) || + isa(QT.getTypePtr()); +} +static bool locDangerous(SourceLocation S) { + return S.isInvalid() || S.isMacroID(); +} + +static Optional +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 fixIfNotDangerous(SourceLocation Loc, + StringRef Text) { + if (locDangerous(Loc)) + return None; + return FixItHint::CreateInsertion(Loc, Text); +} + +static Optional 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 IgnoredParens = + skipLParensBackwards(Var.getLocation(), Context); + + if (IgnoredParens) + return fixIfNotDangerous(*IgnoredParens, "const "); + return None; + } +} + +static Optional changePointerItself(const VarDecl &Var, + const ASTContext &Context) { + if (locDangerous(Var.getLocation())) + return None; + + Optional IgnoredParens = + skipLParensBackwards(Var.getLocation(), Context); + if (IgnoredParens) + return fixIfNotDangerous(*IgnoredParens, "const "); + return None; +} + +static Optional 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 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 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 IgnoredParens = + skipLParensBackwards(BeforeRef, Context); + if (IgnoredParens) + return fixIfNotDangerous(*IgnoredParens, " const"); + + return None; +} + +Optional 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/utils/LexerUtils.h =================================================================== --- clang-tidy/utils/LexerUtils.h +++ 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-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: unittests/clang-tidy/AddConstTest.cpp =================================================================== --- /dev/null +++ unittests/clang-tidy/AddConstTest.cpp @@ -0,0 +1,826 @@ +#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 +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("var"); + using utils::fixit::changeVarDeclToConst; + Optional 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; +using PointeeRTransform = + ConstTransform; + +using ValueLTransform = ConstTransform; +using ValueRTransform = ConstTransform; + +// ---------------------------------------------------------------------------- +// 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(Snippet)); + EXPECT_EQ("const int target = 0;", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int const target = 0;", runCheckOnCode(Snippet)); + EXPECT_EQ("int const target = 0;", + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const MyInt target = 0;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("MyInt const target = 0;"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("MyInt const target = 0;"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const MyInt target = nullptr;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("MyInt const target = nullptr;"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("MyInt const target = nullptr;"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const auto target = f();"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("auto const target = f();"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("auto const target = f();"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const auto target = f();"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("auto const target = f();"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("auto const target = f();"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const auto target = f();"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("auto const target = f();"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("auto const target = f();"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const decltype(f()) target = f();"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("decltype(f()) const target = f();"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("decltype(f()) const target = f();"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const decltype(f()) target = f();"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("decltype(f()) const target = f();"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("decltype(f()) const target = f();"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const decltype(f()) target = f();"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("decltype(f()) const target = f();"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("decltype(f()) const target = f();"), + runCheckOnCode(Cat(S))); +} +TEST(Values, Parens) { + StringRef Snippet = "int ((target)) = 0;"; + + EXPECT_EQ("const int ((target)) = 0;", + runCheckOnCode(Snippet)); + EXPECT_EQ("const int ((target)) = 0;", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int const ((target)) = 0;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int const ((target)) = 0;", + runCheckOnCode(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(Snippet)); + EXPECT_EQ("const int target[][1] = {{1}, {2}, {3}};", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int const target[][1] = {{1}, {2}, {3}};", + runCheckOnCode(Snippet)); + EXPECT_EQ("int const target[][1] = {{1}, {2}, {3}};", + runCheckOnCode(Snippet)); +} +TEST(Arrays, BuiltinParens) { + StringRef Snippet = "int ((target))[][1] = {{1}, {2}, {3}};"; + + EXPECT_EQ("const int ((target))[][1] = {{1}, {2}, {3}};", + runCheckOnCode(Snippet)); + EXPECT_EQ("const int ((target))[][1] = {{1}, {2}, {3}};", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int const ((target))[][1] = {{1}, {2}, {3}};", + runCheckOnCode(Snippet)); + EXPECT_EQ("int const ((target))[][1] = {{1}, {2}, {3}};", + runCheckOnCode(Snippet)); +} +TEST(Arrays, Pointers) { + StringRef Snippet = "int x; int* target[] = {&x, &x, &x};"; + + EXPECT_EQ("int x; const int* target[] = {&x, &x, &x};", + runCheckOnCode(Snippet)); + EXPECT_EQ("int x; int const* target[] = {&x, &x, &x};", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int x; int* const target[] = {&x, &x, &x};", + runCheckOnCode(Snippet)); + EXPECT_EQ("int x; int* const target[] = {&x, &x, &x};", + runCheckOnCode(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(Snippet)); + EXPECT_EQ("int* x = nullptr; int** const target[] = {&x, &x, &x};", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int* x = nullptr; int* const* target[] = {&x, &x, &x};", + runCheckOnCode(Snippet)); + EXPECT_EQ("int* x = nullptr; int** const target[] = {&x, &x, &x};", + runCheckOnCode(Snippet)); +} +TEST(Arrays, PointersParens) { + StringRef Snippet = "int x; int* (target)[] = {&x, &x, &x};"; + + EXPECT_EQ("int x; const int* (target)[] = {&x, &x, &x};", + runCheckOnCode(Snippet)); + EXPECT_EQ("int x; int const* (target)[] = {&x, &x, &x};", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int x; int* const (target)[] = {&x, &x, &x};", + runCheckOnCode(Snippet)); + EXPECT_EQ("int x; int* const (target)[] = {&x, &x, &x};", + runCheckOnCode(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(Snippet)); + EXPECT_EQ("int x = 42; const int& target = x;", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int x = 42; int const& target = x;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int x = 42; int const& target = x;", + runCheckOnCode(Snippet)); +} +TEST(Reference, RValueBuiltin) { + StringRef Snippet = "int&& target = 42;"; + EXPECT_EQ("const int&& target = 42;", + runCheckOnCode(Snippet)); + EXPECT_EQ("const int&& target = 42;", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int const&& target = 42;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int const&& target = 42;", + runCheckOnCode(Snippet)); +} +TEST(Reference, LValueToPointer) { + StringRef Snippet = "int* p; int *& target = p;"; + EXPECT_EQ("int* p; int * const& target = p;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int* p; int * const& target = p;", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int* p; int * const& target = p;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int* p; int * const& target = p;", + runCheckOnCode(Snippet)); +} +TEST(Reference, LValueParens) { + StringRef Snippet = "int x = 42; int ((& target)) = x;"; + + EXPECT_EQ("int x = 42; const int ((& target)) = x;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int x = 42; const int ((& target)) = x;", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int x = 42; int const((& target)) = x;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int x = 42; int const((& target)) = x;", + runCheckOnCode(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(Cat(Snippet))); + EXPECT_EQ(Cat("const int (&target)[4] = a;"), + runCheckOnCode(Cat(Snippet))); + + EXPECT_EQ(Cat("int const(&target)[4] = a;"), + runCheckOnCode(Cat(Snippet))); + EXPECT_EQ(Cat("int const(&target)[4] = a;"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("auto const& target = f();"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("const auto& target = f();"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("auto const& target = f();"), + runCheckOnCode(Cat(S))); +} + +// ---------------------------------------------------------------------------- +// Test pointers types. +// ---------------------------------------------------------------------------- + +TEST(Pointers, SingleBuiltin) { + StringRef Snippet = "int* target = nullptr;"; + + EXPECT_EQ("int* const target = nullptr;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int* const target = nullptr;", + runCheckOnCode(Snippet)); + + EXPECT_EQ("const int* target = nullptr;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int const* target = nullptr;", + runCheckOnCode(Snippet)); +} +TEST(Pointers, MultiBuiltin) { + StringRef Snippet = "int** target = nullptr;"; + + EXPECT_EQ("int** const target = nullptr;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int** const target = nullptr;", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int* const* target = nullptr;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int* const* target = nullptr;", + runCheckOnCode(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(Cat(Snippet))); + EXPECT_EQ(Cat("const int (*target)[4] = &a;"), + runCheckOnCode(Cat(Snippet))); + + EXPECT_EQ(Cat("int (*const target)[4] = &a;"), + runCheckOnCode(Cat(Snippet))); + EXPECT_EQ(Cat("int const(*target)[4] = &a;"), + runCheckOnCode(Cat(Snippet))); +} +TEST(Pointers, Parens) { + StringRef Snippet = "int ((**target)) = nullptr;"; + + EXPECT_EQ("int ((**const target)) = nullptr;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int ((**const target)) = nullptr;", + runCheckOnCode(Snippet)); + + EXPECT_EQ("int ((* const*target)) = nullptr;", + runCheckOnCode(Snippet)); + EXPECT_EQ("int ((* const*target)) = nullptr;", + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("auto* const target = f();"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("const auto* target = f();"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("auto const* target = f();"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("auto (((* const target))) = f();"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("const auto (((* target))) = f();"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("auto const(((* target))) = f();"), + runCheckOnCode(Cat(S))); +} +TEST(Pointers, FunctionPointer) { + StringRef S = "int (*target)(float, int, double) = nullptr;"; + + EXPECT_EQ("int (*const target)(float, int, double) = nullptr;", + runCheckOnCode(S)); + EXPECT_EQ("int (*const target)(float, int, double) = nullptr;", + runCheckOnCode(S)); + + EXPECT_EQ("int (*const target)(float, int, double) = nullptr;", + runCheckOnCode(S)); + EXPECT_EQ("int (*const target)(float, int, double) = nullptr;", + runCheckOnCode(S)); + + S = "int (((*target)))(float, int, double) = nullptr;"; + EXPECT_EQ("int (((*const target)))(float, int, double) = nullptr;", + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("int (A::*const target)() = &A::f;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("int (A::*const target)() = &A::f;"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("int (A::*const target)() = &A::f;"), + runCheckOnCode(Cat(S))); + + S = "int (A::*((target)))() = &A::f;"; + EXPECT_EQ(Cat("int (A::*const ((target)))() = &A::f;"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("int A::*const target = &A::member;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("int A::*const target = &A::member;"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("int A::*const target = &A::member;"), + runCheckOnCode(Cat(S))); + + S = "int A::*((target)) = &A::member;"; + EXPECT_EQ(Cat("int A::*const ((target)) = &A::member;"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const struct Foo target{0};"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("struct Foo const target{0};"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("struct Foo const target{0};"), + runCheckOnCode(Cat(S))); + + S = "Foo target{0};"; + EXPECT_EQ(Cat("const Foo target{0};"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("const Foo target{0};"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("Foo const target{0};"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("Foo const target{0};"), + runCheckOnCode(Cat(S))); + + S = "Foo (target){0};"; + EXPECT_EQ(Cat("const Foo (target){0};"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("const Foo (target){0};"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("Foo const (target){0};"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("Foo const (target){0};"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const class Foo target;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("class Foo const target;"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("class Foo const target;"), + runCheckOnCode(Cat(S))); + + S = "Foo target;"; + EXPECT_EQ(Cat("const Foo target;"), runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("const Foo target;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("Foo const target;"), runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("Foo const target;"), + runCheckOnCode(Cat(S))); + + S = "Foo (target);"; + EXPECT_EQ(Cat("const Foo (target);"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("const Foo (target);"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("Foo const (target);"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("Foo const (target);"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const enum Foo target;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("enum Foo const target;"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("enum Foo const target;"), + runCheckOnCode(Cat(S))); + + S = "Foo target;"; + EXPECT_EQ(Cat("const Foo target;"), runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("const Foo target;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("Foo const target;"), runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("Foo const target;"), + runCheckOnCode(Cat(S))); + + S = "Foo (target);"; + EXPECT_EQ(Cat("const Foo (target);"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("const Foo (target);"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("Foo const (target);"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("Foo const (target);"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("const union Foo target;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("union Foo const target;"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("union Foo const target;"), + runCheckOnCode(Cat(S))); + + S = "Foo target;"; + EXPECT_EQ(Cat("const Foo target;"), runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("const Foo target;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("Foo const target;"), runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("Foo const target;"), + runCheckOnCode(Cat(S))); + + S = "Foo (target);"; + EXPECT_EQ(Cat("const Foo (target);"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("const Foo (target);"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("Foo const (target);"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("Foo const (target);"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("DEFINE_VARIABLE;"), runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("DEFINE_VARIABLE;"), runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("DEFINE_VARIABLE;"), runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("DEFINE_VARIABLE(target);"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("DEFINE_VARIABLE(target);"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("DEFINE_VARIABLE(target);"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("BAD_TYPEDEF target = 42;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("BAD_TYPEDEF const target = 42;"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("BAD_TYPEDEF const target = 42;"), + runCheckOnCode(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(Cat(S))); + EXPECT_EQ(Cat("BAD_TYPEDEF const target = nullptr;"), + runCheckOnCode(Cat(S))); + + // FIXME: Failing even all parts seem to bail-out in for isMacroID() + EXPECT_NE(Cat("BAD_TYPEDEF target = nullptr;"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("BAD_TYPEDEF target = nullptr;"), + runCheckOnCode(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(Cat(S))); + // FIXME: Failing even all parts seem to bail-out in for isMacroID() + EXPECT_NE(Cat("BAD_TYPEDEF target = g;"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("BAD_TYPEDEF target = g;"), + runCheckOnCode(Cat(S))); + // FIXME: Failing even all parts seem to bail-out in for isMacroID() + EXPECT_NE(Cat("BAD_TYPEDEF target = g;"), + runCheckOnCode(Cat(S))); +} + +// ---------------------------------------------------------------------------- +// Test template code. +// ---------------------------------------------------------------------------- + +TEST(Template, FunctionValue) { + StringRef T = "template 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(Cat(S))); + EXPECT_EQ(Cat("{ T const target = v; }"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("{ const T target = v; }"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("{ T const target = v; }"), + runCheckOnCode(Cat(S))); +} +TEST(Template, FunctionPointer) { + StringRef T = "template 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(Cat(S))); + EXPECT_EQ(Cat("{ T* const target = v; }"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("{ const T* target = v; }"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("{ T const* target = v; }"), + runCheckOnCode(Cat(S))); +} +TEST(Template, FunctionReference) { + StringRef T = "template 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(Cat(S))); + EXPECT_EQ(Cat("{ T const& target = v; }"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("{ const T& target = v; }"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("{ T const& target = v; }"), + runCheckOnCode(Cat(S))); +} +TEST(Template, StructValue) { + StringRef T = "template 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(Cat(S))); + EXPECT_EQ(Cat("{ T const target = v; }"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("{ const T target = v; }"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("{ T const target = v; }"), + runCheckOnCode(Cat(S))); +} +TEST(Template, StructPointer) { + StringRef T = "template 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(Cat(S))); + EXPECT_EQ(Cat("{ T* const target = v; }"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("{ const T* target = v; }"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("{ T const* target = v; }"), + runCheckOnCode(Cat(S))); +} +TEST(Template, StructReference) { + StringRef T = "template 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(Cat(S))); + EXPECT_EQ(Cat("{ T const& target = v; }"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("{ const T& target = v; }"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("{ T const& target = v; }"), + runCheckOnCode(Cat(S))); +} +} // namespace test +} // namespace tidy +} // namespace clang 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