Index: clang-tidy/performance/ForRangeCopyCheck.cpp =================================================================== --- clang-tidy/performance/ForRangeCopyCheck.cpp +++ 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-tidy/performance/UnnecessaryCopyInitialization.cpp =================================================================== --- clang-tidy/performance/UnnecessaryCopyInitialization.cpp +++ 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-tidy/performance/UnnecessaryValueParamCheck.cpp =================================================================== --- clang-tidy/performance/UnnecessaryValueParamCheck.cpp +++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp @@ -163,7 +163,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::addQualifierToVarDecl(CurrentParam, DeclSpec::TQ::TQ_const); } } Index: clang-tidy/utils/FixItHintUtils.h =================================================================== --- clang-tidy/utils/FixItHintUtils.h +++ 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 { @@ -20,8 +21,34 @@ /// \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 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 +addQualifierToVarDecl(const VarDecl &Var, DeclSpec::TQ Qualifier, + QualifierTarget CT = QualifierTarget::Pointee, + QualifierPolicy CP = QualifierPolicy::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 @@ -26,10 +26,208 @@ 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); +} + +// 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 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 IgnoredParens = + skipLParensBackwards(Var.getLocation(), Context); + + if (IgnoredParens) + return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier)); + return None; + } +} + +static Optional changePointerItself(const VarDecl &Var, + DeclSpec::TQ Qualifier, + const ASTContext &Context) { + if (locDangerous(Var.getLocation())) + return None; + + Optional IgnoredParens = + skipLParensBackwards(Var.getLocation(), Context); + if (IgnoredParens) + return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier)); + return None; +} + +static Optional +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 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 +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 IgnoredParens = + skipLParensBackwards(BeforeRef, Context); + if (IgnoredParens) + return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier, true)); + + return None; +} + +Optional 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); + } + + 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 @@ -38,6 +38,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()) @@ -45,7 +47,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 @@ -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: unittests/clang-tidy/AddConstTest.cpp =================================================================== --- /dev/null +++ unittests/clang-tidy/AddConstTest.cpp @@ -0,0 +1,905 @@ +#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::addQualifierToVarDecl; + Optional 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; +using PointeeRTransform = + ConstTransform; + +using ValueLTransform = + ConstTransform; +using ValueRTransform = + ConstTransform; + +// ---------------------------------------------------------------------------- +// 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(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, MultiInstantiationsFunction) { + StringRef T = "template void f(T v) \n"; + StringRef S = "{ T target = v; }"; + StringRef InstantStart = "void calls() {\n"; + StringRef InstValue = "f(42);\n"; + StringRef InstConstValue = "f(42);\n"; + StringRef InstPointer = "f(nullptr);\n"; + StringRef InstPointerConst = "f(nullptr);\n"; + StringRef InstConstPointer = "f(nullptr);\n"; + StringRef InstConstPointerConst = "f(nullptr);\n"; + StringRef InstRef = "int i = 42;\nf(i);\n"; + StringRef InstConstRef = "f(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(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))); +} +TEST(Template, DependentReturnFunction) { + StringRef TS = "template struct TS { using value_type = T; };"; + StringRef T = "template 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(Cat(S))); + EXPECT_EQ(Cat("{ typename T::value_type const target; }"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("{ const typename T::value_type target; }"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("{ typename T::value_type const target; }"), + runCheckOnCode(Cat(S))); +} +TEST(Template, DependentReturnPointerFunction) { + StringRef TS = "template struct TS { using value_type = T; };"; + StringRef T = "template 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(Cat(S))); + EXPECT_EQ(Cat("{ typename T::value_type *const target; }"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("{ const typename T::value_type *target; }"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("{ typename T::value_type const*target; }"), + runCheckOnCode(Cat(S))); +} +TEST(Template, DependentReturnReferenceFunction) { + StringRef TS = "template struct TS { using value_type = T; };"; + StringRef T = "template 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(Cat(S))); + EXPECT_EQ(Cat("{ typename T::value_type const&target = f; }"), + runCheckOnCode(Cat(S))); + + EXPECT_EQ(Cat("{ const typename T::value_type &target = f; }"), + runCheckOnCode(Cat(S))); + EXPECT_EQ(Cat("{ typename T::value_type const&target = f; }"), + 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