[clang-tidy] added AllowedTypes
option to readability-qualified-auto
check (#136571)
Added `AllowedTypes` option to `readability-qualified-auto` check Fixes https://github.com/llvm/llvm-project/issues/63461.
This commit is contained in:
parent
5e0c390160
commit
ac0a880f4e
@ -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))),
|
||||
|
@ -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 @@ public:
|
||||
|
||||
private:
|
||||
const bool AddConstToQualified;
|
||||
const std::vector<StringRef> AllowedTypes;
|
||||
};
|
||||
|
||||
} // namespace clang::tidy::readability
|
||||
|
@ -239,6 +239,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
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user