Skip to content

[clang-tide] added AllowedTypes option to readability-qualified-auto check #136571

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

vbvictor
Copy link
Contributor

Added AllowedTypes option to readability-qualified-auto check

Fixes #63461.

@llvmbot
Copy link
Member

llvmbot commented Apr 21, 2025

@llvm/pr-subscribers-clang-tidy

@llvm/pr-subscribers-clang-tools-extra

Author: Baranov Victor (vbvictor)

Changes

Added AllowedTypes option to readability-qualified-auto check

Fixes #63461.


Full diff: https://github.com/llvm/llvm-project/pull/136571.diff

5 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp (+22-5)
  • (modified) clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.h (+2-3)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+4)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/readability/qualified-auto.rst (+14)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/readability/qualified-auto.cpp (+146-1)
diff --git a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
index e843c593a92cc..00999ee8310c1 100644
--- a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
@@ -8,6 +8,8 @@
 
 #include "QualifiedAutoCheck.h"
 #include "../utils/LexerUtils.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "llvm/ADT/SmallVector.h"
 #include <optional>
@@ -100,8 +102,17 @@ bool isAutoPointerConst(QualType QType) {
 
 } // namespace
 
+QualifiedAutoCheck::QualifiedAutoCheck(StringRef Name,
+                                       ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      AddConstToQualified(Options.get("AddConstToQualified", true)),
+      AllowedTypes(
+          utils::options::parseStringList(Options.get("AllowedTypes", ""))) {}
+
 void QualifiedAutoCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
   Options.store(Opts, "AddConstToQualified", AddConstToQualified);
+  Options.store(Opts, "AllowedTypes",
+                utils::options::serializeStringList(AllowedTypes));
 }
 
 void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) {
@@ -124,20 +135,26 @@ void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) {
 
   auto IsBoundToType = refersToType(equalsBoundNode("type"));
   auto UnlessFunctionType = unless(hasUnqualifiedDesugaredType(functionType()));
-  auto IsAutoDeducedToPointer = [](const auto &...InnerMatchers) {
+  auto IsAutoDeducedToPointer = [](const std::vector<StringRef> &AllowedTypes,
+                                   const auto &...InnerMatchers) {
     return autoType(hasDeducedType(
-        hasUnqualifiedDesugaredType(pointerType(pointee(InnerMatchers...)))));
+        hasUnqualifiedDesugaredType(pointerType(pointee(InnerMatchers...))),
+        unless(hasUnqualifiedType(
+            matchers::matchesAnyListedTypeName(AllowedTypes, false))),
+        unless(pointerType(pointee(hasUnqualifiedType(
+            matchers::matchesAnyListedTypeName(AllowedTypes, false)))))));
   };
 
   Finder->addMatcher(
-      ExplicitSingleVarDecl(hasType(IsAutoDeducedToPointer(UnlessFunctionType)),
-                            "auto"),
+      ExplicitSingleVarDecl(
+          hasType(IsAutoDeducedToPointer(AllowedTypes, UnlessFunctionType)),
+          "auto"),
       this);
 
   Finder->addMatcher(
       ExplicitSingleVarDeclInTemplate(
           allOf(hasType(IsAutoDeducedToPointer(
-                    hasUnqualifiedType(qualType().bind("type")),
+                    AllowedTypes, hasUnqualifiedType(qualType().bind("type")),
                     UnlessFunctionType)),
                 anyOf(hasAncestor(
                           functionDecl(hasAnyTemplateArgument(IsBoundToType))),
diff --git a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.h b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.h
index dae9add48e3d7..187a4cd6ee614 100644
--- a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.h
@@ -21,9 +21,7 @@ namespace clang::tidy::readability {
 /// 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),
-        AddConstToQualified(Options.get("AddConstToQualified", true)) {}
+  QualifiedAutoCheck(StringRef Name, ClangTidyContext *Context);
   bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
     return LangOpts.CPlusPlus11;
   }
@@ -33,6 +31,7 @@ class QualifiedAutoCheck : public ClangTidyCheck {
 
 private:
   const bool AddConstToQualified;
+  const std::vector<StringRef> AllowedTypes;
 };
 
 } // namespace clang::tidy::readability
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 761c1d3a80359..d3e4530535638 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -204,6 +204,10 @@ Changes in existing checks
   tolerating fix-it breaking compilation when functions is used as pointers
   to avoid matching usage of functions within the current compilation unit.
 
+- Improved :doc:`readability-qualified-auto
+  <clang-tidy/checks/readability/qualified-auto>` check by adding the option
+  `AllowedTypes`, that excludes specified types from adding qualifiers.
+
 Removed checks
 ^^^^^^^^^^^^^^
 
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
index 9bc2c1295d3ec..efa085719643c 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/qualified-auto.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/qualified-auto.rst
@@ -82,3 +82,17 @@ Otherwise it will be transformed into:
    const auto &Foo3 = cast<const int &>(Bar3);
 
 Note in the LLVM alias, the default value is `false`.
+
+.. option:: AllowedTypes
+
+  A semicolon-separated list of names of types to ignore when ``auto`` is
+  deduced to that type or a pointer to that type. Note that this distinguishes
+  type aliases from the original type, so specifying e.g. ``my_int`` will not
+  suppress reports about ``int`` even if it is defined as a ``typedef`` alias
+  for ``int``. Regular expressions are accepted, e.g. ``[Rr]ef(erence)?$``
+  matches every type with suffix ``Ref``, ``ref``, ``Reference`` and
+  ``reference``. If a name in the list contains the sequence `::` it is matched
+  against the qualified type name (i.e. ``namespace::Type``), otherwise it is
+  matched against only the type name (i.e. ``Type``). E.g. to suppress reports
+  for ``std::array`` iterators use `std::array<.*>::(const_)?iterator` string.
+  The default is an empty string.
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
index 6cc794882859f..7c25b853bfa0c 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/qualified-auto.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/qualified-auto.cpp
@@ -1,4 +1,7 @@
-// RUN: %check_clang_tidy %s readability-qualified-auto %t
+// RUN: %check_clang_tidy %s readability-qualified-auto %t \
+// RUN: -config='{CheckOptions: { \
+// RUN:   readability-qualified-auto.AllowedTypes: "[iI]terator$;my::ns::Ignored1;std::array<.*>::Ignored2;MyIgnoredPtr" \
+// RUN: }}'
 
 namespace typedefs {
 typedef int *MyPtr;
@@ -238,3 +241,145 @@ void baz() {
 
   auto &MyFunctionRef2 = *getPtrFunction();
 }
+
+namespace std {
+
+template<typename T, int N>
+struct array {
+  typedef T value_type;
+
+  typedef value_type* iterator;
+  typedef value_type* Iterator;
+  using using_iterator = T*;
+  typedef const value_type* const_iterator;
+  typedef const value_type* constIterator;
+
+  struct Ignored2 {};
+  using NotIgnored2 = Ignored2;
+
+  iterator begin() { return nullptr; }
+  const_iterator begin() const { return nullptr; }
+  iterator end() { return nullptr; }
+  const_iterator end() const { return nullptr; }
+};
+
+struct Iterator {};
+
+struct Ignored2 {}; // should not be ignored
+
+} // namespace std
+
+typedef std::Iterator iterator;
+
+namespace my {
+namespace ns {
+
+struct Ignored1 {};
+
+using NotIgnored1 = Ignored1;
+typedef Ignored1 NotIgnored2;
+
+} // namespace ns
+
+struct Ignored1 {}; // should not be ignored
+
+} // namespace my
+
+typedef int *MyIgnoredPtr;
+MyIgnoredPtr getIgnoredPtr();
+
+void ignored_types() {
+  auto ignored_ptr = getIgnoredPtr();
+  // CHECK-MESSAGES-NOT: warning: 'auto ignored_ptr' can be declared as 'auto *ignored_ptr'
+  // CHECK-FIXES-NOT: auto *ignored_ptr = getIgnoredPtr();
+
+  std::array<int, 4> arr;
+  std::array<int, 4> carr;
+
+  auto it1 = arr.begin();
+  // CHECK-MESSAGES-NOT: warning: 'auto it' can be declared as 'auto *it'
+  // CHECK-FIXES-NOT: auto *it = vec.it_begin();
+  
+  auto it2 = carr.begin();
+  // CHECK-MESSAGES-NOT: warning: 'auto it2' can be declared as 'auto *it2'
+  // CHECK-FIXES-NOT: auto *it2 = carr.begin();
+
+  auto it3 = std::array<int, 4>::iterator{};
+  // CHECK-MESSAGES-NOT: warning: 'auto it3' can be declared as 'auto *it3'
+  // CHECK-FIXES-NOT: auto *it3 = std::array<int, 4>::iterator{};
+
+  auto it4 = std::array<int, 4>::Iterator{};
+  // CHECK-MESSAGES-NOT: warning: 'auto it4' can be declared as 'auto *it4'
+  // CHECK-FIXES-NOT: auto *it4 = std::array<int, 4>::Iterator{};
+
+  auto it5 = std::array<int, 4>::using_iterator{};
+  // CHECK-MESSAGES-NOT: warning: 'auto it5' can be declared as 'auto *it5'
+  // CHECK-FIXES-NOT: auto *it5 = std::array<int, 4>::using_iterator{};
+
+  auto it6 = std::array<int, 4>::const_iterator{};
+  // CHECK-MESSAGES-NOT: warning: 'auto it6' can be declared as 'auto *it6'
+  // CHECK-FIXES-NOT: auto *it6 = std::array<int, 4>::const_iterator{};
+
+  auto it7 = std::array<int, 4>::constIterator{};
+  // CHECK-MESSAGES-NOT: warning: 'auto it7' can be declared as 'auto *it7'
+  // CHECK-FIXES-NOT: auto *it7 = std::array<int, 4>::constIterator{};
+
+  auto it8 = new std::Iterator();
+  // CHECK-MESSAGES-NOT: warning: 'auto it8' can be declared as 'auto *it8'
+  // CHECK-FIXES-NOT: auto *it8 = new std::Iterator();
+
+  auto it9 = new iterator();
+  // CHECK-MESSAGES-NOT: warning: 'auto it9' can be declared as 'auto *it9'
+  // CHECK-FIXES-NOT: auto *it9 = new iterator();
+
+  auto arr_ignored2 = new std::array<int, 4>::Ignored2();
+  // CHECK-MESSAGES-NOT: warning: 'auto arr_ignored2' can be declared as 'auto *arr_ignored2'
+  // CHECK-FIXES-NOT: auto *arr_ignored2 = new std::array<int, 4>::Ignored2();
+
+  auto arr_not_ignored2 = new std::array<int, 4>::NotIgnored2();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto arr_not_ignored2' can be declared as 'auto *arr_not_ignored2'
+  // CHECK-FIXES: auto *arr_not_ignored2 = new std::array<int, 4>::NotIgnored2();
+
+  auto not_ignored2 = new std::Ignored2();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto not_ignored2' can be declared as 'auto *not_ignored2'
+  // CHECK-FIXES: auto *not_ignored2 = new std::Ignored2();
+
+  auto ignored1 = new my::ns::Ignored1();
+  // CHECK-MESSAGES-NOT: warning: 'auto ignored1' can be declared as 'auto *ignored1'
+  // CHECK-FIXES-NOT: auto *ignored1 = new my::ns::Ignored1();
+
+  auto not_ignored1 = new my::ns::NotIgnored1();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto not_ignored1' can be declared as 'auto *not_ignored1'
+  // CHECK-FIXES: auto *not_ignored1 = new my::ns::NotIgnored1();
+
+  auto not2_ignored1 = new my::ns::NotIgnored2();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto not2_ignored1' can be declared as 'auto *not2_ignored1'
+  // CHECK-FIXES: auto *not2_ignored1 = new my::ns::NotIgnored2();
+
+  auto not3_ignored1 = new my::Ignored1();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'auto not3_ignored1' can be declared as 'auto *not3_ignored1'
+  // CHECK-FIXES: auto *not3_ignored1 = new my::Ignored1();
+}
+
+template <typename T>
+void ignored_types_template(std::array<T, 4> arr, const std::array<T, 4>& carr) {
+  auto it1 = arr.begin();
+  // CHECK-MESSAGES-NOT: warning: 'auto it' can be declared as 'auto *it'
+  // CHECK-FIXES-NOT: auto *it = arr.it_begin();
+  
+  auto it2 = carr.begin();
+  // CHECK-MESSAGES-NOT: warning: 'auto it2' can be declared as 'auto *it2'
+  // CHECK-FIXES-NOT: auto *it2 = carr.begin();
+
+  for (auto Data : arr) {
+    // CHECK-MESSAGES-NOT: warning: 'auto Data' can be declared as 'auto *Data'
+    // CHECK-FIXES-NOT: {{^}}    for (auto *Data : MClassTemplate) {
+    change(*Data);
+  }
+
+  for (auto Data : carr) {
+    // CHECK-MESSAGES-NOT: warning: 'auto Data' can be declared as 'const auto *Data'
+    // CHECK-FIXES-NOT: {{^}}    for (const auto *Data : MClassTemplate) {
+    change(*Data);
+  }
+}
\ No newline at end of file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[clang-tidy] readability-qualified-auto and std::array iterators
3 participants