Index: clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp +++ clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "../readability/NamespaceCommentCheck.h" +#include "../readability/QualifiedAutoCheck.h" #include "HeaderGuardCheck.h" #include "IncludeOrderCheck.h" #include "PreferIsaOrDynCastInConditionalsCheck.h" @@ -31,6 +32,8 @@ "llvm-prefer-isa-or-dyn-cast-in-conditionals"); CheckFactories.registerCheck( "llvm-prefer-register-over-unsigned"); + CheckFactories.registerCheck( + "llvm-qualified-auto"); CheckFactories.registerCheck("llvm-twine-local"); } }; Index: clang-tools-extra/clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -21,6 +21,7 @@ NamedParameterCheck.cpp NamespaceCommentCheck.cpp NonConstParameterCheck.cpp + QualifiedAutoCheck.cpp ReadabilityTidyModule.cpp RedundantAccessSpecifiersCheck.cpp RedundantControlFlowCheck.cpp Index: clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.h @@ -0,0 +1,36 @@ +//===--- QualifiedAutoCheck.h - clang-tidy ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_QUALIFIEDAUTOCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_QUALIFIEDAUTOCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// Finds variables declared as auto that could be declared as: +/// 'auto*' or 'const auto *' and reference variables declared as: +/// 'const auto &'. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-qualified-auto.html +class QualifiedAutoCheck : public ClangTidyCheck { +public: + QualifiedAutoCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_QUALIFIEDAUTOCHECK_H Index: clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp @@ -0,0 +1,289 @@ +//===--- QualifiedAutoCheck.cpp - clang-tidy ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "QualifiedAutoCheck.h" +#include "../utils/LexerUtils.h" +#include "llvm/ADT/Optional.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace readability { + +namespace { +enum class Qualifier { + Const, + Volatile // Ignore restrict as not c++ keyword. +}; + +llvm::Optional findQualToken(const VarDecl *Decl, Qualifier Qual, + const MatchFinder::MatchResult &Result) { + // Since either of the locs can be in a macro, use `makeFileCharRange` to be + // sure that we have a consistent `CharSourceRange`, located entirely in the + // source file. + + assert(Qual == Qualifier::Const || + Qual == Qualifier::Volatile && "Invalid Qualifier"); + + tok::TokenKind Tok = + Qual == Qualifier::Const ? tok::kw_const : tok::kw_volatile; + + SourceLocation BeginLoc = Decl->getQualifierLoc().getBeginLoc(); + if (BeginLoc.isInvalid()) + BeginLoc = Decl->getBeginLoc(); + + SourceLocation EndLoc = Decl->getLocation(); + + CharSourceRange FileRange = Lexer::makeFileCharRange( + CharSourceRange::getCharRange(BeginLoc, EndLoc), *Result.SourceManager, + Result.Context->getLangOpts()); + + if (FileRange.isInvalid()) + return None; + + return utils::lexer::getQualifyingToken(Tok, FileRange, *Result.Context, + *Result.SourceManager); +} + +llvm::Optional +getTypeSpecifierLocation(const VarDecl *Var, + const MatchFinder::MatchResult &Result) { + SourceRange TypeSpecifier( + Var->getTypeSpecStartLoc(), + Var->getTypeSpecEndLoc().getLocWithOffset(Lexer::MeasureTokenLength( + Var->getTypeSpecEndLoc(), *Result.SourceManager, + Result.Context->getLangOpts()))); + + if (TypeSpecifier.getBegin().isMacroID() || + TypeSpecifier.getEnd().isMacroID()) { + return llvm::None; + } + return TypeSpecifier; +} + +llvm::Optional mergeReplacementRange(SourceRange &TypeSpecifier, + const Token &ConstToken) { + if (TypeSpecifier.getBegin().getLocWithOffset(-1) == ConstToken.getEndLoc()) { + TypeSpecifier.setBegin(ConstToken.getLocation()); + return llvm::None; + } else if (TypeSpecifier.getEnd().getLocWithOffset(1) == + ConstToken.getLocation()) { + TypeSpecifier.setEnd(ConstToken.getEndLoc()); + return llvm::None; + } + return SourceRange(ConstToken.getLocation(), ConstToken.getEndLoc()); +} + +bool isPointerConst(QualType QType) { + auto Pointee = QType.getTypePtr()->getPointeeType(); + if (Pointee.isNull()) + return Pointee.isLocalConstQualified(); + return Pointee.isConstQualified(); +} + +bool isAutoPointerConst(QualType QType) { + auto Pointee = + dyn_cast(QType.getTypePtr()->getPointeeType().getTypePtr()) + ->getDeducedType(); + if (Pointee.isNull()) + return Pointee.isLocalConstQualified(); + return Pointee.isConstQualified(); +} + +} // namespace + +void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; // Auto deduction not used in 'C', so don't register Matchers. + auto ExplicitSingleVarDecl = + [](const ast_matchers::internal::Matcher &InnerMatcher, + llvm::StringRef ID) { + return declStmt( + unless(isInTemplateInstantiation()), + hasSingleDecl( + varDecl(unless(isImplicit()), InnerMatcher).bind(ID))); + }; + auto ExplicitSingleVarDeclInTemplate = + [](const ast_matchers::internal::Matcher &InnerMatcher, + llvm::StringRef ID) { + return declStmt( + isInTemplateInstantiation(), + hasSingleDecl( + varDecl(unless(isImplicit()), InnerMatcher).bind(ID))); + }; + Finder->addMatcher( + ExplicitSingleVarDecl(hasType(autoType(hasDeducedType( + pointerType(pointee(unless(functionType())))))), + "auto"), + this); + + auto TemplateMatcher = [](auto &Matcher) { + return hasAncestor( + // FIXME add matcher that gets CXXMethodDecls and can return template + // arguments from the Method Decl and Class Decl + Matcher(hasAnyTemplateArgument(refersToType(equalsBoundNode("type"))))); + }; + + Finder->addMatcher( + ExplicitSingleVarDeclInTemplate( + allOf(hasType(autoType(hasDeducedType(pointerType(pointee( + // FIXME add matcher hasUnqualifiedType (or similar) that + // will check a type with no qualifiers + qualType().bind("type"), unless(functionType())))))), + TemplateMatcher(functionDecl)), + "auto"), + this); + Finder->addMatcher(ExplicitSingleVarDecl( + hasType(pointerType(pointee(autoType()))), "auto_ptr"), + this); + Finder->addMatcher( + ExplicitSingleVarDecl(hasType(lValueReferenceType(pointee(autoType()))), + "auto_ref"), + this); +} + +void QualifiedAutoCheck::check(const MatchFinder::MatchResult &Result) { + if (auto Var = Result.Nodes.getNodeAs("auto")) { + bool IsPtrConst = isPointerConst(Var->getType()); + SourceRange TypeSpecifier; + if (llvm::Optional TypeSpec = + getTypeSpecifierLocation(Var, Result)) { + TypeSpecifier = *TypeSpec; + } else { + return; + } + + llvm::SmallVector RemoveQualifiersRange; + auto CheckQualifier = [&](bool IsPresent, Qualifier Qual) { + if (IsPresent) { + llvm::Optional Token = findQualToken(Var, Qual, Result); + if (!Token || Token->getLocation().isMacroID()) { + return true; // Disregard this VarDecl. + } + if (llvm::Optional Result = + mergeReplacementRange(TypeSpecifier, *Token)) { + RemoveQualifiersRange.push_back(*Result); + } + } + return false; + }; + + bool IsLocalConst = Var->getType().isLocalConstQualified(); + bool IsLocalVolatile = Var->getType().isLocalVolatileQualified(); + + if (CheckQualifier(IsLocalConst, Qualifier::Const)) + return; + if (CheckQualifier(IsLocalVolatile, Qualifier::Volatile)) + return; + + // Check for bridging the gap between the asterisk and name. + if (Var->getLocation() == TypeSpecifier.getEnd().getLocWithOffset(1)) + TypeSpecifier.setEnd(TypeSpecifier.getEnd().getLocWithOffset(1)); + + CharSourceRange FixItRange = CharSourceRange::getCharRange(TypeSpecifier); + if (FixItRange.isInvalid()) + return; + + SourceLocation FixitLoc = FixItRange.getBegin(); + for (SourceRange &Range : RemoveQualifiersRange) { + if (Range.getBegin() < FixitLoc) { + FixitLoc = Range.getBegin(); + } + } + + std::string ReplStr = [&] { + llvm::StringRef PtrConst = IsPtrConst ? "const " : ""; + llvm::StringRef LocalConst = IsLocalConst ? "const " : ""; + llvm::StringRef LocalVol = IsLocalVolatile ? "volatile " : ""; + return (PtrConst + "auto *" + LocalConst + LocalVol).str(); + }(); + + DiagnosticBuilder Diag = + diag(FixitLoc, "'%0%1auto %2' can be declared as '%3%2'") + << (IsLocalConst ? "const " : "") + << (IsLocalVolatile ? "volatile " : "") << Var->getName() << ReplStr; + + for (SourceRange &Range : RemoveQualifiersRange) { + Diag << FixItHint::CreateRemoval( + CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd())); + } + + Diag << FixItHint::CreateReplacement(FixItRange, ReplStr); + return; + } + if (auto Var = Result.Nodes.getNodeAs("auto_ptr")) { + if (!isPointerConst(Var->getType())) + return; // Pointer isn't const, no need to add const qualifier. + if (!isAutoPointerConst(Var->getType())) + // Const isnt wrapped in the auto type, so must be declared explicitly. + return; + + if (Var->getType().isLocalConstQualified()) { + llvm::Optional Token = + findQualToken(Var, Qualifier::Const, Result); + if (!Token || Token->getLocation().isMacroID()) { + return; + } + } + if (Var->getType().isLocalVolatileQualified()) { + llvm::Optional Token = + findQualToken(Var, Qualifier::Volatile, Result); + if (!Token || Token->getLocation().isMacroID()) { + return; + } + } + + CharSourceRange FixItRange; + if (llvm::Optional TypeSpec = + getTypeSpecifierLocation(Var, Result)) { + FixItRange = CharSourceRange::getCharRange(*TypeSpec); + if (FixItRange.isInvalid()) + return; + } else { + return; + } + + DiagnosticBuilder Diag = + diag(FixItRange.getBegin(), + "'auto *%0%1%2' can be declared as 'const auto *%0%1%2'") + << (Var->getType().isLocalConstQualified() ? "const " : "") + << (Var->getType().isLocalVolatileQualified() ? "volatile " : "") + << Var->getName(); + Diag << FixItHint::CreateReplacement(FixItRange, "const auto *"); + return; + } + if (auto Var = Result.Nodes.getNodeAs("auto_ref")) { + if (!isPointerConst(Var->getType())) + return; // Pointer isn't const, no need to add const qualifier. + if (!isAutoPointerConst(Var->getType())) + // Const isnt wrapped in the auto type, so must be declared explicitly. + return; + + CharSourceRange FixItRange; + if (llvm::Optional TypeSpec = + getTypeSpecifierLocation(Var, Result)) { + FixItRange = CharSourceRange::getCharRange(*TypeSpec); + if (FixItRange.isInvalid()) + return; + } else { + return; + } + + DiagnosticBuilder Diag = + diag(FixItRange.getBegin(), + "'auto &%0' can be declared as 'const auto &%0'") + << Var->getName(); + Diag << FixItHint::CreateReplacement(FixItRange, "const auto &"); + return; + } +} + +} // namespace readability +} // namespace tidy +} // namespace clang Index: clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -28,6 +28,7 @@ #include "MisplacedArrayIndexCheck.h" #include "NamedParameterCheck.h" #include "NonConstParameterCheck.h" +#include "QualifiedAutoCheck.h" #include "RedundantAccessSpecifiersCheck.h" #include "RedundantControlFlowCheck.h" #include "RedundantDeclarationCheck.h" @@ -86,6 +87,8 @@ "readability-misleading-indentation"); CheckFactories.registerCheck( "readability-misplaced-array-index"); + CheckFactories.registerCheck( + "readability-qualified-auto"); CheckFactories.registerCheck( "readability-redundant-access-specifiers"); CheckFactories.registerCheck( Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -105,6 +105,11 @@ :doc:`bugprone-bad-signal-to-kill-thread ` was added. +- New alias :doc:`llvm-qualified-auto + ` to + :doc:`readability-qualified-auto + ` was added. + - New :doc:`cert-oop58-cpp ` check. @@ -173,6 +178,12 @@ ` fix no longer adds semicolons where they would be redundant. +- New :doc:`readability-qualified-auto + ` check. + + Adds pointer and const qualifications to auto typed variables + that are deduced to pointers and const pointers. + - New :doc:`readability-redundant-access-specifiers ` check. Index: clang-tools-extra/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -264,6 +264,7 @@ `readability-misplaced-array-index `_, "Yes" `readability-named-parameter `_, "Yes" `readability-non-const-parameter `_, "Yes" + `readability-qualified-auto `_, "Yes" `readability-redundant-access-specifiers `_, "Yes" `readability-redundant-control-flow `_, "Yes" `readability-redundant-declaration `_, "Yes" @@ -398,6 +399,7 @@ `hicpp-use-nullptr `_, `modernize-use-nullptr `_, "Yes" `hicpp-use-override `_, `modernize-use-override `_, "Yes" `hicpp-vararg `_, `cppcoreguidelines-pro-type-vararg `_, + `llvm-qualified-auto `_, `readability-qualified-auto `_, "Yes" .. toctree:: :glob: Index: clang-tools-extra/docs/clang-tidy/checks/llvm-qualified-auto.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/llvm-qualified-auto.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - llvm-qualified-auto +.. meta:: + :http-equiv=refresh: 5;URL=readability-qualified-auto.html + +llvm-qualified-auto +=================== + +The llvm-qualified-auto check is an alias, please see +`readability-qualified-auto `_ +for more information. Index: clang-tools-extra/docs/clang-tidy/checks/readability-qualified-auto.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/readability-qualified-auto.rst @@ -0,0 +1,63 @@ +.. title:: clang-tidy - readability-qualified-auto + +readability-qualified-auto +========================== + +Adds pointer and const qualifications to auto typed variables that are deduced +to pointers and const pointers. +`LLVM Coding Standards `_ advises to +make it obvious if a auto typed variable is a pointer, constant pointer or +constant reference. This check will transform ``auto`` to ``auto *`` when the +type is deduced to be a pointer, as well as adding ``const`` when applicable to +auto pointers or references + +.. code-block:: c++ + + for (auto &Data : MutatableContainer) { + change(Data); + } + for (auto &Data : ConstantContainer) { + observe(Data); + } + for (auto Data : MutatablePtrContainer) { + change(*Data); + } + for (auto Data : ConstantPtrContainer) { + observe(*Data); + } + +Would be transformed into: + +.. code-block:: c++ + + for (auto &Data : MutatableContainer) { + change(Data); + } + for (const auto &Data : ConstantContainer) { + observe(Data); + } + for (auto *Data : MutatablePtrContainer) { + change(*Data); + } + for (const auto *Data : ConstantPtrContainer) { + observe(*Data); + } + +Note const volatile qualified types will retain their const and volatile qualifiers. + +.. code-block:: c++ + + const auto Foo = cast(Baz1); + const auto Bar = cast(Baz2); + volatile auto FooBar = cast(Baz3); + +Would be transformed into: + +.. code-block:: c++ + + auto *const Foo = cast(Baz1); + const auto *const Bar = cast(Baz2); + auto *volatile FooBar = cast(Baz3); + +This check helps to enforce this `LLVM Coding Standards recommendation +`_. Index: clang-tools-extra/test/clang-tidy/checkers/readability-qualified-auto-cxx20.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/readability-qualified-auto-cxx20.cpp @@ -0,0 +1,49 @@ +// RUN: %check_clang_tidy %s readability-qualified-auto %t -- -- -std=c++20 +namespace std { +template +class vector { // dummy impl + T _data[1]; + +public: + T *begin() { return _data; } + const T *begin() const { return _data; } + T *end() { return &_data[1]; } + const T *end() const { return &_data[1]; } + unsigned size() const { return 0; } +}; +} // namespace std + +std::vector *getVec(); +const std::vector *getCVec(); +void foo() { + if (auto X = getVec(); X->size() > 0) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'auto X' can be declared as 'auto *X' + // CHECK-FIXES: {{^}} if (auto *X = getVec(); X->size() > 0) { + } + switch (auto X = getVec(); X->size()) { + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 'auto X' can be declared as 'auto *X' + // CHECK-FIXES: {{^}} switch (auto *X = getVec(); X->size()) { + default: + break; + } + for (auto X = getVec(); auto Xi : *X) { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: 'auto X' can be declared as 'auto *X' + // CHECK-FIXES: {{^}} for (auto *X = getVec(); auto Xi : *X) { + } +} +void bar() { + if (auto X = getCVec(); X->size() > 0) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'auto X' can be declared as 'const auto *X' + // CHECK-FIXES: {{^}} if (const auto *X = getCVec(); X->size() > 0) { + } + switch (auto X = getCVec(); X->size()) { + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 'auto X' can be declared as 'const auto *X' + // CHECK-FIXES: {{^}} switch (const auto *X = getCVec(); X->size()) { + default: + break; + } + for (auto X = getCVec(); auto Xi : *X) { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: 'auto X' can be declared as 'const auto *X' + // CHECK-FIXES: {{^}} for (const auto *X = getCVec(); auto Xi : *X) { + } +} Index: clang-tools-extra/test/clang-tidy/checkers/readability-qualified-auto.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/readability-qualified-auto.cpp @@ -0,0 +1,240 @@ +// RUN: %check_clang_tidy %s readability-qualified-auto %t + +namespace typedefs { +typedef int *MyPtr; +typedef int &MyRef; +typedef const int *CMyPtr; +typedef const int &CMyRef; + +MyPtr getPtr(); +MyRef getRef(); +CMyPtr getCPtr(); +CMyRef getCRef(); + +void foo() { + auto TdNakedPtr = getPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto TdNakedPtr' can be declared as 'auto *TdNakedPtr' + // CHECK-FIXES: {{^}} auto *TdNakedPtr = getPtr(); + auto &TdNakedRef = getRef(); + auto TdNakedRefDeref = getRef(); + auto TdNakedCPtr = getCPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto TdNakedCPtr' can be declared as 'const auto *TdNakedCPtr' + // CHECK-FIXES: {{^}} const auto *TdNakedCPtr = getCPtr(); + auto &TdNakedCRef = getCRef(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto &TdNakedCRef' can be declared as 'const auto &TdNakedCRef' + // CHECK-FIXES: {{^}} const auto &TdNakedCRef = getCRef(); + auto TdNakedCRefDeref = getCRef(); +} + +}; // namespace typedefs + +namespace usings { +using MyPtr = int *; +using MyRef = int &; +using CMyPtr = const int *; +using CMyRef = const int &; + +MyPtr getPtr(); +MyRef getRef(); +CMyPtr getCPtr(); +CMyRef getCRef(); + +void foo() { + auto UNakedPtr = getPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto UNakedPtr' can be declared as 'auto *UNakedPtr' + // CHECK-FIXES: {{^}} auto *UNakedPtr = getPtr(); + auto &UNakedRef = getRef(); + auto UNakedRefDeref = getRef(); + auto UNakedCPtr = getCPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto UNakedCPtr' can be declared as 'const auto *UNakedCPtr' + // CHECK-FIXES: {{^}} const auto *UNakedCPtr = getCPtr(); + auto &UNakedCRef = getCRef(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto &UNakedCRef' can be declared as 'const auto &UNakedCRef' + // CHECK-FIXES: {{^}} const auto &UNakedCRef = getCRef(); + auto UNakedCRefDeref = getCRef(); +} + +}; // namespace usings + +int getInt(); +int *getIntPtr(); +const int *getCIntPtr(); + +void foo() { + // make sure check disregards named types + int TypedInt = getInt(); + int *TypedPtr = getIntPtr(); + const int *TypedConstPtr = getCIntPtr(); + int &TypedRef = *getIntPtr(); + const int &TypedConstRef = *getCIntPtr(); + + // make sure check disregards auto types that aren't pointers or references + auto AutoInt = getInt(); + + auto NakedPtr = getIntPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto NakedPtr' can be declared as 'auto *NakedPtr' + // CHECK-FIXES: {{^}} auto *NakedPtr = getIntPtr(); + auto NakedCPtr = getCIntPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto NakedCPtr' can be declared as 'const auto *NakedCPtr' + // CHECK-FIXES: {{^}} const auto *NakedCPtr = getCIntPtr(); + + const auto ConstPtr = getIntPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'const auto ConstPtr' can be declared as 'auto *const ConstPtr' + // CHECK-FIXES: {{^}} auto *const ConstPtr = getIntPtr(); + const auto ConstCPtr = getCIntPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'const auto ConstCPtr' can be declared as 'const auto *const ConstCPtr' + // CHECK-FIXES: {{^}} const auto *const ConstCPtr = getCIntPtr(); + + volatile auto VolatilePtr = getIntPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'volatile auto VolatilePtr' can be declared as 'auto *volatile VolatilePtr' + // CHECK-FIXES: {{^}} auto *volatile VolatilePtr = getIntPtr(); + volatile auto VolatileCPtr = getCIntPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'volatile auto VolatileCPtr' can be declared as 'const auto *volatile VolatileCPtr' + // CHECK-FIXES: {{^}} const auto *volatile VolatileCPtr = getCIntPtr(); + + auto *QualPtr = getIntPtr(); + auto *QualCPtr = getCIntPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto *QualCPtr' can be declared as 'const auto *QualCPtr' + // CHECK-FIXES: {{^}} const auto *QualCPtr = getCIntPtr(); + auto *const ConstantQualCPtr = getCIntPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto *const ConstantQualCPtr' can be declared as 'const auto *const ConstantQualCPtr' + // CHECK-FIXES: {{^}} const auto *const ConstantQualCPtr = getCIntPtr(); + auto *volatile VolatileQualCPtr = getCIntPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto *volatile VolatileQualCPtr' can be declared as 'const auto *volatile VolatileQualCPtr' + // CHECK-FIXES: {{^}} const auto *volatile VolatileQualCPtr = getCIntPtr(); + const auto *ConstQualCPtr = getCIntPtr(); + + auto &Ref = *getIntPtr(); + auto &CRef = *getCIntPtr(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto &CRef' can be declared as 'const auto &CRef' + // CHECK-FIXES: {{^}} const auto &CRef = *getCIntPtr(); + const auto &ConstCRef = *getCIntPtr(); + + if (auto X = getCIntPtr()) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'auto X' can be declared as 'const auto *X' + // CHECK-FIXES: {{^}} if (const auto *X = getCIntPtr()) { + } +} + +void macroTest() { +#define _AUTO auto +#define _CONST const + _AUTO AutoMACROPtr = getIntPtr(); + const _AUTO ConstAutoMacroPtr = getIntPtr(); + _CONST _AUTO ConstMacroAutoMacroPtr = getIntPtr(); + _CONST auto ConstMacroAutoPtr = getIntPtr(); +#undef _AUTO +#undef _CONST +} + +namespace std { +template +class vector { // dummy impl + T _data[1]; + +public: + T *begin() { return _data; } + const T *begin() const { return _data; } + T *end() { return &_data[1]; } + const T *end() const { return &_data[1]; } +}; +} // namespace std + +void change(int &); +void observe(const int &); + +void loopRef(std::vector &Mutate, const std::vector &Constant) { + for (auto &Data : Mutate) { + change(Data); + } + for (auto &Data : Constant) { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: 'auto &Data' can be declared as 'const auto &Data' + // CHECK-FIXES: {{^}} for (const auto &Data : Constant) { + observe(Data); + } +} + +void loopPtr(const std::vector &Mutate, const std::vector &Constant) { + for (auto Data : Mutate) { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: 'auto Data' can be declared as 'auto *Data' + // CHECK-FIXES: {{^}} for (auto *Data : Mutate) { + change(*Data); + } + for (auto Data : Constant) { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: 'auto Data' can be declared as 'const auto *Data' + // CHECK-FIXES: {{^}} for (const auto *Data : Constant) { + observe(*Data); + } +} + +template +void tempLoopPtr(std::vector &MutateTemplate, std::vector &ConstantTemplate) { + for (auto Data : MutateTemplate) { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: 'auto Data' can be declared as 'auto *Data' + // CHECK-FIXES: {{^}} for (auto *Data : MutateTemplate) { + change(*Data); + } + //FixMe + for (auto Data : ConstantTemplate) { + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:8: warning: 'auto Data' can be declared as 'const auto *Data' + // CHECK-FIXES-NOT: {{^}} for (const auto *Data : ConstantTemplate) { + observe(*Data); + } +} + +template +class TemplateLoopPtr { +public: + void operator()(std::vector &MClassTemplate, std::vector &CClassTemplate) { + for (auto Data : MClassTemplate) { + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:10: warning: 'auto Data' can be declared as 'auto *Data' + // CHECK-FIXES-NOT: {{^}} for (auto *Data : MClassTemplate) { + change(*Data); + } + //FixMe + for (auto Data : CClassTemplate) { + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:10: warning: 'auto Data' can be declared as 'const auto *Data' + // CHECK-FIXES-NOT: {{^}} for (const auto *Data : CClassTemplate) { + observe(*Data); + } + } +}; + +void bar() { + std::vector Vec; + std::vector PtrVec; + std::vector CPtrVec; + loopRef(Vec, Vec); + loopPtr(PtrVec, CPtrVec); + tempLoopPtr(PtrVec, CPtrVec); + TemplateLoopPtr(); +} + +typedef int *(*functionRetPtr)(); +typedef int (*functionRetVal)(); + +functionRetPtr getPtrFunction(); +functionRetVal getValFunction(); + +void baz() { + auto MyFunctionPtr = getPtrFunction(); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: 'auto MyFunctionPtr' can be declared as 'auto *MyFunctionPtr' + // CHECK-FIXES-NOT: {{^}} auto *MyFunctionPtr = getPtrFunction(); + auto MyFunctionVal = getValFunction(); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: 'auto MyFunctionVal' can be declared as 'auto *MyFunctionVal' + // CHECK-FIXES-NOT: {{^}} auto *MyFunctionVal = getValFunction(); + + auto LambdaTest = [] { return 0; }; + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: 'auto LambdaTest' can be declared as 'auto *LambdaTest' + // CHECK-FIXES-NOT: {{^}} auto *LambdaTest = [] { return 0; }; + + auto LambdaTest2 = +[] { return 0; }; + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: 'auto LambdaTest2' can be declared as 'auto *LambdaTest2' + // CHECK-FIXES-NOT: {{^}} auto *LambdaTest2 = +[] { return 0; }; + + auto MyFunctionRef = *getPtrFunction(); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: 'auto MyFunctionRef' can be declared as 'auto *MyFunctionRef' + // CHECK-FIXES-NOT: {{^}} auto *MyFunctionRef = *getPtrFunction(); + + auto &MyFunctionRef2 = *getPtrFunction(); +}