[clang-tools-extra] 36fcbb8 - Added readability-qualified-auto check

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 14 11:06:56 PST 2020


Author: Nathan James
Date: 2020-01-14T14:06:46-05:00
New Revision: 36fcbb838c8f293f46bfed78c6ed8c177f1e3485

URL: https://github.com/llvm/llvm-project/commit/36fcbb838c8f293f46bfed78c6ed8c177f1e3485
DIFF: https://github.com/llvm/llvm-project/commit/36fcbb838c8f293f46bfed78c6ed8c177f1e3485.diff

LOG: Added readability-qualified-auto check

Adds a check that detects any auto variables that are deduced to a pointer or
a const pointer then adds in the const and asterisk according. Will also
check auto L value references that could be written as const. This relates
to the coding standard
https://llvm.org/docs/CodingStandards.html#beware-unnecessary-copies-with-auto

Added: 
    clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
    clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.h
    clang-tools-extra/docs/clang-tidy/checks/llvm-qualified-auto.rst
    clang-tools-extra/docs/clang-tidy/checks/readability-qualified-auto.rst
    clang-tools-extra/test/clang-tidy/checkers/readability-qualified-auto-cxx20.cpp
    clang-tools-extra/test/clang-tidy/checkers/readability-qualified-auto.cpp

Modified: 
    clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
    clang-tools-extra/clang-tidy/readability/CMakeLists.txt
    clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
index 0facb957ed09..5ae927c2cf5a 100644
--- a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
+++ b/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 @@ class LLVMModule : public ClangTidyModule {
         "llvm-prefer-isa-or-dyn-cast-in-conditionals");
     CheckFactories.registerCheck<PreferRegisterOverUnsignedCheck>(
         "llvm-prefer-register-over-unsigned");
+    CheckFactories.registerCheck<readability::QualifiedAutoCheck>(
+        "llvm-qualified-auto");
     CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local");
   }
 };

diff  --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index bac7164508cf..97144af06ca4 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangTidyReadabilityModule
   NamedParameterCheck.cpp
   NamespaceCommentCheck.cpp
   NonConstParameterCheck.cpp
+  QualifiedAutoCheck.cpp
   ReadabilityTidyModule.cpp
   RedundantAccessSpecifiersCheck.cpp
   RedundantControlFlowCheck.cpp

diff  --git a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
new file mode 100644
index 000000000000..969927fedc3d
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
@@ -0,0 +1,294 @@
+//===--- 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 "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+// FIXME move to ASTMatchers
+AST_MATCHER_P(QualType, hasUnqualifiedType,
+              ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
+  return InnerMatcher.matches(Node.getUnqualifiedType(), Finder, Builder);
+}
+
+enum class Qualifier { Const, Volatile, Restrict };
+
+llvm::Optional<Token> 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 ||
+         Qual == Qualifier::Restrict && "Invalid Qualifier");
+
+  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 llvm::None;
+
+  tok::TokenKind Tok =
+      Qual == Qualifier::Const
+          ? tok::kw_const
+          : Qual == Qualifier::Volatile ? tok::kw_volatile : tok::kw_restrict;
+
+  return utils::lexer::getQualifyingToken(Tok, FileRange, *Result.Context,
+                                          *Result.SourceManager);
+}
+
+llvm::Optional<SourceRange>
+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<SourceRange> mergeReplacementRange(SourceRange &TypeSpecifier,
+                                                  const Token &ConstToken) {
+  if (TypeSpecifier.getBegin().getLocWithOffset(-1) == ConstToken.getEndLoc()) {
+    TypeSpecifier.setBegin(ConstToken.getLocation());
+    return llvm::None;
+  }
+  if (TypeSpecifier.getEnd().getLocWithOffset(1) == ConstToken.getLocation()) {
+    TypeSpecifier.setEnd(ConstToken.getEndLoc());
+    return llvm::None;
+  }
+  return SourceRange(ConstToken.getLocation(), ConstToken.getEndLoc());
+}
+
+bool isPointerConst(QualType QType) {
+  QualType Pointee = QType->getPointeeType();
+  assert(!Pointee.isNull() && "can't have a null Pointee");
+  return Pointee.isConstQualified();
+}
+
+bool isAutoPointerConst(QualType QType) {
+  QualType Pointee =
+      cast<AutoType>(QType->getPointeeType().getTypePtr())->desugar();
+  assert(!Pointee.isNull() && "can't have a null Pointee");
+  return Pointee.isConstQualified();
+}
+
+} // namespace
+
+void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return; // Auto deduction not used in 'C or C++03 and earlier', so don't
+            // register Matchers.
+  auto ExplicitSingleVarDecl =
+      [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
+         llvm::StringRef ID) {
+        return declStmt(
+            unless(isInTemplateInstantiation()),
+            hasSingleDecl(
+                varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
+      };
+  auto ExplicitSingleVarDeclInTemplate =
+      [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
+         llvm::StringRef ID) {
+        return declStmt(
+            isInTemplateInstantiation(),
+            hasSingleDecl(
+                varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
+      };
+
+  auto IsBoundToType = refersToType(equalsBoundNode("type"));
+
+  Finder->addMatcher(
+      ExplicitSingleVarDecl(hasType(autoType(hasDeducedType(
+                                pointerType(pointee(unless(functionType())))))),
+                            "auto"),
+      this);
+
+  Finder->addMatcher(
+      ExplicitSingleVarDeclInTemplate(
+          allOf(hasType(autoType(hasDeducedType(pointerType(
+                    pointee(hasUnqualifiedType(qualType().bind("type")),
+                            unless(functionType())))))),
+                anyOf(hasAncestor(
+                          functionDecl(hasAnyTemplateArgument(IsBoundToType))),
+                      hasAncestor(classTemplateSpecializationDecl(
+                          hasAnyTemplateArgument(IsBoundToType))))),
+          "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 (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto")) {
+    SourceRange TypeSpecifier;
+    if (llvm::Optional<SourceRange> TypeSpec =
+            getTypeSpecifierLocation(Var, Result)) {
+      TypeSpecifier = *TypeSpec;
+    } else
+      return;
+
+    llvm::SmallVector<SourceRange, 4> RemoveQualifiersRange;
+    auto CheckQualifier = [&](bool IsPresent, Qualifier Qual) {
+      if (IsPresent) {
+        llvm::Optional<Token> Token = findQualToken(Var, Qual, Result);
+        if (!Token || Token->getLocation().isMacroID())
+          return true; // Disregard this VarDecl.
+        if (llvm::Optional<SourceRange> Result =
+                mergeReplacementRange(TypeSpecifier, *Token))
+          RemoveQualifiersRange.push_back(*Result);
+      }
+      return false;
+    };
+
+    bool IsLocalConst = Var->getType().isLocalConstQualified();
+    bool IsLocalVolatile = Var->getType().isLocalVolatileQualified();
+    bool IsLocalRestrict = Var->getType().isLocalRestrictQualified();
+
+    if (CheckQualifier(IsLocalConst, Qualifier::Const))
+      return;
+    if (CheckQualifier(IsLocalVolatile, Qualifier::Volatile))
+      return;
+    if (CheckQualifier(IsLocalRestrict, Qualifier::Restrict))
+      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 = isPointerConst(Var->getType()) ? "const " : "";
+      llvm::StringRef LocalConst = IsLocalConst ? "const " : "";
+      llvm::StringRef LocalVol = IsLocalVolatile ? "volatile " : "";
+      llvm::StringRef LocalRestrict = IsLocalRestrict ? "__restrict " : "";
+      return (PtrConst + "auto *" + LocalConst + LocalVol + LocalRestrict)
+          .str();
+    }();
+
+    DiagnosticBuilder Diag =
+        diag(FixitLoc, "'%0%1%2auto %3' can be declared as '%4%3'")
+        << (IsLocalConst ? "const " : "")
+        << (IsLocalVolatile ? "volatile " : "")
+        << (IsLocalRestrict ? "__restrict " : "") << Var->getName() << ReplStr;
+
+    for (SourceRange &Range : RemoveQualifiersRange) {
+      Diag << FixItHint::CreateRemoval(
+          CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd()));
+    }
+
+    Diag << FixItHint::CreateReplacement(FixItRange, ReplStr);
+    return;
+  }
+  if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto_ptr")) {
+    if (!isPointerConst(Var->getType()))
+      return; // Pointer isn't const, no need to add const qualifier.
+    if (!isAutoPointerConst(Var->getType()))
+      return; // Const isnt wrapped in the auto type, so must be declared
+              // explicitly.
+
+    if (Var->getType().isLocalConstQualified()) {
+      llvm::Optional<Token> Token =
+          findQualToken(Var, Qualifier::Const, Result);
+      if (!Token || Token->getLocation().isMacroID())
+        return;
+    }
+    if (Var->getType().isLocalVolatileQualified()) {
+      llvm::Optional<Token> Token =
+          findQualToken(Var, Qualifier::Volatile, Result);
+      if (!Token || Token->getLocation().isMacroID())
+        return;
+    }
+    if (Var->getType().isLocalRestrictQualified()) {
+      llvm::Optional<Token> Token =
+          findQualToken(Var, Qualifier::Restrict, Result);
+      if (!Token || Token->getLocation().isMacroID())
+        return;
+    }
+
+    CharSourceRange FixItRange;
+    if (llvm::Optional<SourceRange> 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 (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("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<SourceRange> 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

diff  --git a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.h b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.h
new file mode 100644
index 000000000000..799342cce2eb
--- /dev/null
+++ b/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

diff  --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index d8b1c25f3264..5ece15ed2912 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/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 @@ class ReadabilityModule : public ClangTidyModule {
         "readability-misleading-indentation");
     CheckFactories.registerCheck<MisplacedArrayIndexCheck>(
         "readability-misplaced-array-index");
+    CheckFactories.registerCheck<QualifiedAutoCheck>(
+        "readability-qualified-auto");
     CheckFactories.registerCheck<RedundantAccessSpecifiersCheck>(
         "readability-redundant-access-specifiers");
     CheckFactories.registerCheck<RedundantFunctionPtrDereferenceCheck>(

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 65938a357ee9..52e98cb23f50 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -165,6 +165,12 @@ New checks
   Finds non-static member functions that can be made ``const``
   because the functions don't use ``this`` in a non-const way.
 
+- New :doc:`readability-qualified-auto
+  <clang-tidy/checks/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
   <clang-tidy/checks/readability-redundant-access-specifiers>` check.
 
@@ -179,6 +185,11 @@ New aliases
   :doc:`bugprone-bad-signal-to-kill-thread
   <clang-tidy/checks/bugprone-bad-signal-to-kill-thread>` was added.
 
+- New alias :doc:`llvm-qualified-auto
+  <clang-tidy/checks/llvm-qualified-auto>` to
+  :doc:`readability-qualified-auto
+  <clang-tidy/checks/readability-qualified-auto>` was added.
+
 Changes in existing checks
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index c89ec0c10917..5787ffcac5e1 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -270,6 +270,7 @@ Clang-Tidy Checks
    `readability-misplaced-array-index <readability-misplaced-array-index.html>`_, "Yes"
    `readability-named-parameter <readability-named-parameter.html>`_, "Yes"
    `readability-non-const-parameter <readability-non-const-parameter.html>`_, "Yes"
+   `readability-qualified-auto <readability-qualified-auto.html>`_, "Yes"
    `readability-redundant-access-specifiers <readability-redundant-access-specifiers.html>`_, "Yes"
    `readability-redundant-control-flow <readability-redundant-control-flow.html>`_, "Yes"
    `readability-redundant-declaration <readability-redundant-declaration.html>`_, "Yes"
@@ -402,3 +403,4 @@ Clang-Tidy Checks
    `hicpp-use-nullptr <hicpp-use-nullptr.html>`_, `modernize-use-nullptr <modernize-use-nullptr.html>`_, "Yes"
    `hicpp-use-override <hicpp-use-override.html>`_, `modernize-use-override <modernize-use-override.html>`_, "Yes"
    `hicpp-vararg <hicpp-vararg.html>`_, `cppcoreguidelines-pro-type-vararg <cppcoreguidelines-pro-type-vararg.html>`_,
+   `llvm-qualified-auto <llvm-qualified-auto.html>`_, `readability-qualified-auto <readability-qualified-auto.html>`_, "Yes"

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/llvm-qualified-auto.rst b/clang-tools-extra/docs/clang-tidy/checks/llvm-qualified-auto.rst
new file mode 100644
index 000000000000..9f4b9b08a79b
--- /dev/null
+++ b/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 <readability-qualified-auto.html>`_
+for more information.

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/readability-qualified-auto.rst b/clang-tools-extra/docs/clang-tidy/checks/readability-qualified-auto.rst
new file mode 100644
index 000000000000..5f28c04ad818
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability-qualified-auto.rst
@@ -0,0 +1,64 @@
+.. 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 <https://llvm.org/docs/CodingStandards.html>`_ 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<int *>(Baz1);
+  const auto Bar = cast<const int *>(Baz2);
+  volatile auto FooBar = cast<int*>(Baz3);
+
+Would be transformed into:
+
+.. code-block:: c++
+
+  auto *const Foo = cast<int *>(Baz1);
+  const auto *const Bar = cast<const int *>(Baz2);
+  auto *volatile FooBar = cast<int*>(Baz3);
+
+This check helps to enforce this `LLVM Coding Standards recommendation
+<https://llvm.org/docs/CodingStandards.html#beware-unnecessary-copies-with-auto>`_.

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/readability-qualified-auto-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability-qualified-auto-cxx20.cpp
new file mode 100644
index 000000000000..9982d8fab659
--- /dev/null
+++ b/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 <typename T>
+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<int> *getVec();
+const std::vector<int> *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) {
+  }
+}

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/readability-qualified-auto.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability-qualified-auto.cpp
new file mode 100644
index 000000000000..6cc794882859
--- /dev/null
+++ b/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 <typename T>
+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<int> &Mutate, const std::vector<int> &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<int *> &Mutate, const std::vector<const int *> &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 <typename T>
+void tempLoopPtr(std::vector<T *> &MutateTemplate, std::vector<const T *> &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: :[[@LINE-1]]:8: warning: 'auto Data' can be declared as 'const auto *Data'
+    // CHECK-FIXES: {{^}}  for (const auto *Data : ConstantTemplate) {
+    observe(*Data);
+  }
+}
+
+template <typename T>
+class TemplateLoopPtr {
+public:
+  void operator()(const std::vector<T *> &MClassTemplate, const std::vector<const T *> &CClassTemplate) {
+    for (auto Data : MClassTemplate) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: 'auto Data' can be declared as 'auto *Data'
+      // CHECK-FIXES: {{^}}    for (auto *Data : MClassTemplate) {
+      change(*Data);
+    }
+    //FixMe
+    for (auto Data : CClassTemplate) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: 'auto Data' can be declared as 'const auto *Data'
+      // CHECK-FIXES: {{^}}    for (const auto *Data : CClassTemplate) {
+      observe(*Data);
+    }
+  }
+};
+
+void bar() {
+  std::vector<int> Vec;
+  std::vector<int *> PtrVec;
+  std::vector<const int *> CPtrVec;
+  loopRef(Vec, Vec);
+  loopPtr(PtrVec, CPtrVec);
+  tempLoopPtr(PtrVec, CPtrVec);
+  TemplateLoopPtr<int>()(PtrVec, CPtrVec);
+}
+
+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();
+}


        


More information about the cfe-commits mailing list