Skip to content

[clang-tidy] 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
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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) {
Expand All @@ -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))),
Expand Down
5 changes: 2 additions & 3 deletions clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -33,6 +31,7 @@ class QualifiedAutoCheck : public ClangTidyCheck {

private:
const bool AddConstToQualified;
const std::vector<StringRef> AllowedTypes;
};

} // namespace clang::tidy::readability
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^^^^^^^

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
}
}
Loading