nick updated this revision to Diff 225133.
nick added a comment.
Addressed comments.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D69000/new/
https://reviews.llvm.org/D69000
Files:
clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
clang-tools-extra/clang-tidy/modernize/DeprecatedIteratorBaseCheck.cpp
clang-tools-extra/clang-tidy/modernize/DeprecatedIteratorBaseCheck.h
clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
clang-tools-extra/docs/clang-tidy/checks/modernize-deprecated-iterator-base.rst
clang-tools-extra/test/clang-tidy/checkers/modernize-deprecated-iterator-base.cpp
Index: clang-tools-extra/test/clang-tidy/checkers/modernize-deprecated-iterator-base.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/modernize-deprecated-iterator-base.cpp
@@ -0,0 +1,321 @@
+// RUN: %check_clang_tidy %s modernize-deprecated-iterator-base %t
+
+namespace std {
+using ptrdiff_t = int;
+struct input_iterator_tag;
+template <class C, class T, class D = ptrdiff_t, class P = T*, class R = T&>
+struct iterator {
+ using iterator_category = C;
+ using value_type = T;
+ using difference_type = D;
+ using pointer = P;
+ using reference = R;
+};
+}
+
+
+using iterator_alias = std::iterator<std::input_iterator_tag, int>;
+typedef std::iterator<std::input_iterator_tag, long> iterator_typedef;
+
+
+// Sugar
+
+// CHECK-FIXES: struct from_alias {
+// CHECK-MESSAGES: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct from_alias: iterator_alias {};
+
+// CHECK-FIXES: struct from_typedef {
+// CHECK-MESSAGES: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct from_typedef: iterator_typedef {};
+
+
+// False-positive
+
+// CHECK-FIXES: struct indirect_base: from_alias {};
+// CHECK-MESSAGES-NOT: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct indirect_base: from_alias {};
+
+
+// Unsupported
+
+// CHECK-FIXES: class skipif_non_public_inheritance: iterator_alias {};
+// CHECK-MESSAGES: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+class skipif_non_public_inheritance: iterator_alias {};
+
+
+// Base removal
+
+struct A {};
+struct B {};
+
+struct collection {
+ template <class...>
+ struct iterator;
+};
+
+// CHECK-FIXES: template <> struct collection::iterator<> {
+// CHECK-MESSAGES: :[[@LINE+1]]:45: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+template <> struct collection::iterator<> : iterator_alias {};
+// CHECK-FIXES: template <> struct collection::iterator<A> : A {
+// CHECK-MESSAGES: :[[@LINE+1]]:49: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+template <> struct collection::iterator<A> : A, iterator_alias {};
+// CHECK-FIXES: template <> struct collection::iterator<B> : B {
+// CHECK-MESSAGES: :[[@LINE+1]]:46: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+template <> struct collection::iterator<B> : iterator_alias, B {};
+// CHECK-FIXES: template <> struct collection::iterator<A, B> : A, B {
+// CHECK-MESSAGES: :[[@LINE+1]]:52: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+template <> struct collection::iterator<A, B> : A, iterator_alias, B {};
+
+// CHECK-FIXES: struct do_not_strip_final final {
+// CHECK-MESSAGES: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct do_not_strip_final final : iterator_alias {};
+
+// CHECK-FIXES: struct iteratorZ // iterator_alias
+// CHECK-FIXES: {
+// CHECK-MESSAGES: :[[@LINE+2]]:5: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iteratorZ // iteratorZ
+ : iterator_alias // iterator_alias
+{};
+// CHECK-FIXES: struct iteratorA // iteratorA
+// CHECK-FIXES: : A // iterator_alias
+// CHECK-FIXES: {
+// CHECK-MESSAGES: :[[@LINE+3]]:5: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iteratorA // iteratorA
+ : A // A
+ , iterator_alias // iterator_alias
+{};
+// CHECK-FIXES: struct iteratorB // iteratorB
+// CHECK-FIXES: : B // B
+// CHECK-FIXES: {
+// CHECK-MESSAGES: :[[@LINE+2]]:5: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iteratorB // iteratorB
+ : iterator_alias // iterator_alias
+ , B // B
+{};
+// CHECK-FIXES: struct iteratorAB // iteratorAB
+// CHECK-FIXES: : A // A
+// CHECK-FIXES: , B // B
+// CHECK-FIXES: {
+// CHECK-MESSAGES: :[[@LINE+3]]:5: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iteratorAB // iteratorAB
+ : A // A
+ , iterator_alias // iterator_alias
+ , B // B
+{};
+// CHECK-FIXES: struct iterator0Z // iterator_alias
+// CHECK-FIXES: {
+// CHECK-MESSAGES: :[[@LINE+2]]:5: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iterator0Z : // iterator0Z
+ iterator_alias // iterator_alias
+{};
+// CHECK-FIXES: struct iterator0A : // iterator0A
+// CHECK-FIXES: A // iterator_alias
+// CHECK-FIXES: {
+// CHECK-MESSAGES: :[[@LINE+3]]:5: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iterator0A : // iterator0A
+ A, // A
+ iterator_alias // iterator_alias
+{};
+// CHECK-FIXES: struct iterator0B : // iterator0B
+// CHECK-FIXES: B // B
+// CHECK-FIXES: {
+// CHECK-MESSAGES: :[[@LINE+2]]:5: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iterator0B : // iterator0B
+ iterator_alias, // iterator_alias
+ B // B
+{};
+// CHECK-FIXES: struct iterator0AB : // iterator0AB
+// CHECK-FIXES: A, // A
+// CHECK-FIXES: B // B
+// CHECK-FIXES: {
+// CHECK-MESSAGES: :[[@LINE+3]]:5: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iterator0AB : // iterator0AB
+ A, // A
+ iterator_alias, // iterator_alias
+ B // B
+{};
+
+
+// Opening/closing placement
+
+// CHECK-FIXES: class iterator00 {
+// CHECK-FIXES-NEXT: public:
+// CHECK-FIXES: private:
+// CHECK-MESSAGES: :[[@LINE+1]]:27: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+class iterator00 : public iterator_alias {
+ int dummy;
+};
+// CHECK-FIXES: class iterator01 {
+// CHECK-FIXES-NEXT: public:
+// CHECK-FIXES-NOT: private:
+// CHECK-MESSAGES: :[[@LINE+1]]:27: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+class iterator01 : public iterator_alias {
+protected:
+};
+// CHECK-FIXES: class iterator02 {
+// CHECK-FIXES-NEXT: public:
+// CHECK-FIXES-NOT: private:
+// CHECK-MESSAGES: :[[@LINE+1]]:27: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+class iterator02 : public iterator_alias {
+public:
+protected:
+};
+
+// CHECK-FIXES: struct iterator10 {
+// CHECK-FIXES-NEXT: public:
+// CHECK-FIXES-NOT: private:
+// CHECK-MESSAGES: :[[@LINE+1]]:28: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iterator10 : public iterator_alias {
+ int dummy;
+};
+// CHECK-FIXES: struct iterator11 {
+// CHECK-FIXES-NEXT: public:
+// CHECK-FIXES-NOT: private:
+// CHECK-MESSAGES: :[[@LINE+1]]:28: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iterator11 : public iterator_alias {
+protected:
+};
+// CHECK-FIXES: struct iterator12 {
+// CHECK-FIXES-NEXT: public:
+// CHECK-FIXES-NOT: private:
+// CHECK-MESSAGES: :[[@LINE+1]]:28: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iterator12 : public iterator_alias {
+public:
+protected:
+};
+
+// CHECK-FIXES: struct iterator20 {
+// CHECK-FIXES-NEXT: using iterator_category = std::input_iterator_tag;
+// CHECK-FIXES-NOT: private:
+// CHECK-MESSAGES: :[[@LINE+1]]:21: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iterator20 : iterator_alias {
+ int dummy;
+};
+// CHECK-FIXES: struct iterator21 {
+// CHECK-FIXES-NEXT: using iterator_category = std::input_iterator_tag;
+// CHECK-FIXES-NOT: private:
+// CHECK-MESSAGES: :[[@LINE+1]]:21: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iterator21 : iterator_alias {
+protected:
+};
+// CHECK-FIXES: struct iterator22 {
+// CHECK-FIXES-NEXT: public:
+// CHECK-FIXES-NOT: private:
+// CHECK-MESSAGES: :[[@LINE+1]]:21: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct iterator22 : iterator_alias {
+public:
+protected:
+};
+
+
+// Typedefs
+
+// CHECK-FIXES: struct basic {
+// CHECK-FIXES-NEXT: using iterator_category = std::input_iterator_tag;
+// CHECK-FIXES-NEXT: using value_type = int;
+// CHECK-FIXES-NEXT: using difference_type = std::ptrdiff_t;
+// CHECK-FIXES-NEXT: using pointer = int {{\*}};
+// CHECK-FIXES-NEXT: using reference = int &;
+// CHECK-FIXES-NEXT-EMPTY:
+// CHECK-MESSAGES: :[[@LINE+1]]:16: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct basic : std::iterator<std::input_iterator_tag, int> {};
+
+// CHECK-FIXES: class nontempl
+// CHECK-FIXES-NEXT: {
+// CHECK-FIXES-NEXT: public:
+// CHECK-FIXES-NEXT: using iterator_category = std::input_iterator_tag;
+// CHECK-FIXES-NEXT: using value_type = int;
+// CHECK-FIXES-NEXT: using difference_type = long;
+// CHECK-FIXES-NEXT: using pointer = int const{{\*}};
+// CHECK-FIXES-NEXT: using reference = int const&;
+// CHECK-FIXES-NEXT-EMPTY:
+// CHECK-MESSAGES: :[[@LINE+2]]:12: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+class nontempl
+ : public std::iterator
+ < std::input_iterator_tag // iterator_category
+ , int // value_type
+ , long // difference_type
+ , int const* // pointer
+ , int const& // reference
+ >
+{
+ private:
+ int dummy;
+};
+
+// CHECK-FIXES: class templ
+// CHECK-FIXES-NEXT: {
+// CHECK-FIXES-NEXT: public:
+// CHECK-FIXES-NEXT: using iterator_category = typename T::C;
+// CHECK-FIXES-NEXT: using value_type = typename T::V;
+// CHECK-FIXES-NEXT: using difference_type = typename T::D;
+// CHECK-FIXES-NEXT: using pointer = typename T::P;
+// CHECK-FIXES-NEXT: using reference = typename T::R;
+// CHECK-FIXES-NEXT-EMPTY:
+// CHECK-MESSAGES: :[[@LINE+3]]:12: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+template <class T>
+class templ
+ : public std::iterator
+ < typename T::C // iterator_category
+ , typename T::V // value_type
+ , typename T::D // difference_type
+ , typename T::P // pointer
+ , typename T::R // reference
+ >
+{
+ protected:
+ int dummy;
+};
+
+// CHECK-FIXES: struct redeclared
+// CHECK-FIXES-NEXT: {
+// CHECK-FIXES-NEXT: using iterator_category = std::input_iterator_tag;
+// CHECK-FIXES-NEXT: using value_type = void ;
+// CHECK-FIXES-NEXT: using difference_type = long;
+// CHECK-FIXES-NEXT: struct pointer {} ;
+// CHECK-FIXES-NEXT: using reference = int&;
+// CHECK-FIXES-NEXT: };
+// CHECK-MESSAGES: :[[@LINE+2]]:12: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+struct redeclared
+ : public std::iterator<
+ std::input_iterator_tag, // iterator_category
+ int, // value_type
+ long, // difference_type
+ int*, // pointer
+ int&> // reference
+{
+ using value_type = void ;
+ struct pointer {} ;
+};
+
+
+// Indentation
+
+// CHECK-FIXES: {{^ class indent_use_rec {}}
+// CHECK-FIXES-NEXT: {{^ public:}}
+// CHECK-FIXES-NEXT: {{^ using}}
+// CHECK-MESSAGES: :[[@LINE+1]]:33: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+ class indent_use_rec : public iterator_alias {
+ };
+// CHECK-FIXES: {{^ class indent_use_acc {}}
+// CHECK-FIXES-NEXT: {{^ public:}}
+// CHECK-FIXES-NEXT: {{^ using}}
+// CHECK-MESSAGES: :[[@LINE+1]]:33: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+ class indent_use_acc : public iterator_alias {
+ public:
+ };
+// CHECK-FIXES: {{^ class indent_use_memb {}}
+// CHECK-FIXES-NEXT: {{^ public:}}
+// CHECK-FIXES-NEXT: {{^ using}}
+// CHECK-MESSAGES: :[[@LINE+1]]:34: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+ class indent_use_memb : public iterator_alias {
+ int dummy;
+ };
+// CHECK-FIXES: {{^ class indent_use_both {}}
+// CHECK-FIXES-NEXT: {{^ public:}}
+// CHECK-FIXES-NEXT: {{^ using}}
+// CHECK-MESSAGES: :[[@LINE+1]]:34: warning: inheriting from 'std::iterator' is deprecated [modernize-deprecated-iterator-base]
+ class indent_use_both : public iterator_alias {
+ protected:
+ int dummy;
+ };
Index: clang-tools-extra/docs/clang-tidy/checks/modernize-deprecated-iterator-base.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/modernize-deprecated-iterator-base.rst
@@ -0,0 +1,37 @@
+.. title:: clang-tidy - modernize-deprecated-iterator-base
+
+modernize-deprecated-iterator-base
+==================================
+
+Finds deprecated in C++17 inheritance from ``std::iterator`` and replaces it
+with type aliases.
+
+Example
+-------
+
+.. code-block:: c++
+
+ struct my_iterator : std::iterator<std::random_access_iterator_tag, int> {
+ ...
+ };
+
+transforms to:
+
+.. code-block:: c++
+
+ struct my_iterator {
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = int;
+ using difference_type = std::ptrdiff_t;
+ using pointer = int *;
+ using reference = int &;
+
+ ...
+ };
+
+Known Limitations
+-----------------
+
+* Base class symbol ambiguities resolved with ``std::iterator`` values.
+
+* Will not remove ``<iterator>`` include even if it is no longer needed.
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
@@ -300,6 +300,7 @@
modernize-concat-nested-namespaces
modernize-deprecated-headers
modernize-deprecated-ios-base-aliases
+ modernize-deprecated-iterator-base
modernize-loop-convert
modernize-make-shared
modernize-make-unique
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -115,6 +115,12 @@
Finds historical use of ``unsigned`` to hold vregs and physregs and rewrites
them to use ``Register``
+- New :doc:`modernize-deprecated-iterator-base
+ <clang-tidy/checks/modernize-deprecated-iterator-base>` check.
+
+ Finds deprecated in C++17 inheritance from ``std::iterator`` and replaces it
+ with type aliases.
+
- New :doc:`objc-missing-hash
<clang-tidy/checks/objc-missing-hash>` check.
Index: clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -14,6 +14,7 @@
#include "ConcatNestedNamespacesCheck.h"
#include "DeprecatedHeadersCheck.h"
#include "DeprecatedIosBaseAliasesCheck.h"
+#include "DeprecatedIteratorBaseCheck.h"
#include "LoopConvertCheck.h"
#include "MakeSharedCheck.h"
#include "MakeUniqueCheck.h"
@@ -57,6 +58,8 @@
"modernize-deprecated-headers");
CheckFactories.registerCheck<DeprecatedIosBaseAliasesCheck>(
"modernize-deprecated-ios-base-aliases");
+ CheckFactories.registerCheck<DeprecatedIteratorBaseCheck>(
+ "modernize-deprecated-iterator-base");
CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert");
CheckFactories.registerCheck<MakeSharedCheck>("modernize-make-shared");
CheckFactories.registerCheck<MakeUniqueCheck>("modernize-make-unique");
Index: clang-tools-extra/clang-tidy/modernize/DeprecatedIteratorBaseCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/modernize/DeprecatedIteratorBaseCheck.h
@@ -0,0 +1,35 @@
+//===--- DeprecatedIteratorBaseCheck.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_MODERNIZE_DEPRECATEDITERATORBASECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_DEPRECATEDITERATORBASECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Finds deprecated in C++17 inheritance from `std::iterator` and replaces it
+/// with type aliases.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-deprecated-iterator-base.html
+class DeprecatedIteratorBaseCheck : public ClangTidyCheck {
+public:
+ DeprecatedIteratorBaseCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_DEPRECATEDITERATORBASECHECK_H
Index: clang-tools-extra/clang-tidy/modernize/DeprecatedIteratorBaseCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/modernize/DeprecatedIteratorBaseCheck.cpp
@@ -0,0 +1,355 @@
+//===--- DeprecatedIteratorBaseCheck.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 "DeprecatedIteratorBaseCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/Sequence.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::utils::lexer;
+
+// FIXME: Clang really should provide this out-of-the-box
+namespace clang {
+namespace ast_type_traits {
+
+template <> struct ASTNodeKind::KindToKindId<CXXBaseSpecifier> {
+ static const NodeKindId Id = NKI_NumberOfKinds;
+};
+
+template <>
+struct DynTypedNode::BaseConverter<CXXBaseSpecifier, void>
+ : public PtrConverter<CXXBaseSpecifier> {};
+
+} // end namespace ast_type_traits
+} // end namespace clang
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Returns the first child of the CXXRecordDecl
+static const Decl *getTopDecl(const CXXRecordDecl &RD) {
+ auto It = RD.decls_begin();
+ // The first decl of CXXRecordDecl is self-reference, skip it
+ assert(It->getKind() == Decl::CXXRecord && "expecting self-reference");
+ ++It;
+ return It != RD.decls_end() ? *It : nullptr;
+}
+
+/// Returns an AccessSpecDecl if it is the first child of the CXXRecordDecl
+static const AccessSpecDecl *getTopAccSpecDecl(const CXXRecordDecl &RD) {
+ return dyn_cast_or_null<AccessSpecDecl>(getTopDecl(RD));
+}
+
+/// Returns an AccessSpecDecl child of the DeclContext, if any
+static const AccessSpecDecl *getAnyAccSpecDecl(const DeclContext &DC) {
+ for (const Decl *D : DC.decls())
+ if (auto const *ASD = dyn_cast<AccessSpecDecl>(D))
+ return ASD;
+ return nullptr;
+}
+
+/// Returns a non-AccessSpecDecl child of the CXXRecordDecl, if any
+static const Decl *getAnyNonAccSpecDecl(const CXXRecordDecl &RD) {
+ auto It = RD.decls_begin();
+ // The first decl of CXXRecordDecl is self-reference, skip it
+ assert(It->getKind() == Decl::CXXRecord && "expecting self-reference");
+ ++It;
+ for (; It != RD.decls_end(); ++It)
+ if (It->getKind() != Decl::AccessSpec)
+ return *It;
+ return nullptr;
+}
+
+/// Returns default visibility for the TagDecl children
+static AccessSpecifier defaultAccessSpecifierFor(const TagDecl &TD) {
+ switch (TD.getTagKind()) {
+ case TTK_Struct:
+ return AS_public;
+ case TTK_Class:
+ return AS_private;
+ default:
+ llvm_unreachable("unexpected tag kind");
+ }
+}
+
+/// Returns a base removal fixit
+static FixItHint createBaseRemoval(const CXXRecordDecl &RD,
+ const CXXBaseSpecifier &Base,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ SourceRange R = Base.getSourceRange();
+ if (RD.getNumBases() == 1) {
+ // class RD : public Base {
+ // ^^^^^^^^^^^^^^
+ // TODO: Is there a way to get the location without lexer gymnastics?
+ SourceLocation Colon =
+ findPreviousTokenKind(R.getBegin(), SM, LangOpts, tok::colon);
+ Token Tok = getPreviousToken(Colon, SM, LangOpts, /*SkipComments=*/true);
+ assert(!Tok.is(tok::unknown));
+ R.setBegin(Lexer::getLocForEndOfToken(Tok.getLocation(), 0, SM, LangOpts));
+ } else if (RD.bases_end() - 1 == &Base) {
+ // class RD : ... , public Base {
+ // ^^^^^^^^^^^^^^
+ SourceLocation Loc = (RD.bases_end() - 2)->getEndLoc();
+ R.setBegin(Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts));
+ } else {
+ // class RD : ... , public Base, ... {
+ // ^^^^^^^^^^^^^
+ auto It =
+ llvm::find_if(RD.bases(), [&Base](const CXXBaseSpecifier &Candidate) {
+ return &Candidate == &Base;
+ });
+ R.setEnd(std::next(It)->getBeginLoc().getLocWithOffset(-1));
+ }
+ return FixItHint::CreateRemoval(R);
+}
+
+/// Returns an aliases insertion location for the CXXRecordDecl
+static SourceLocation getInsertLoc(const CXXRecordDecl &RD) {
+ const AccessSpecDecl *TopAS = getTopAccSpecDecl(RD);
+ if (TopAS && TopAS->getAccessUnsafe() == AS_public) {
+ // class/struct {
+ // public:
+ // ^
+ return TopAS->getColonLoc().getLocWithOffset(1);
+ } else {
+ // class/struct {
+ // ^
+ return RD.getBraceRange().getBegin().getLocWithOffset(1);
+ }
+}
+
+static bool isOpeningRequired(const CXXRecordDecl &RD) {
+ const AccessSpecDecl *TopAS = getTopAccSpecDecl(RD);
+ if (TopAS && TopAS->getAccessUnsafe() == AS_public) {
+ // class/struct {
+ // public:
+ // ^^^^^^^
+ // Public access specifier is already at the top
+ return false;
+ }
+ if (RD.isStruct()) {
+ // struct : public ... {
+ // public: vvvvvv
+ // ^^^^^^^<<<<<<<<
+ // When a user types public access specifier for a struct base
+ // it is highly likely to also find it at the struct body top
+ for (const CXXBaseSpecifier &Base : RD.bases())
+ switch (Base.getAccessSpecifierAsWritten()) {
+ case AS_public:
+ return true;
+ case AS_none:
+ return false;
+ default:
+ break;
+ }
+ }
+ return defaultAccessSpecifierFor(RD) != AS_public;
+}
+
+static bool isClosingRequired(const CXXRecordDecl &RD) {
+ if (getTopAccSpecDecl(RD)) {
+ // class/struct {
+ // public/private/protected:
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^
+ // No need to emit a closing access specifier because there is already one
+ return false;
+ } else {
+ return defaultAccessSpecifierFor(RD) != AS_public;
+ }
+}
+
+static TemplateSpecializationTypeLoc getTSTLoc(TypeLoc TL) {
+ if (auto TSTL = TL.getAsAdjusted<TemplateSpecializationTypeLoc>())
+ return TSTL;
+ if (auto TDTL = TL.getAs<TypedefTypeLoc>())
+ return getTSTLoc(
+ TDTL.getTypedefNameDecl()->getTypeSourceInfo()->getTypeLoc());
+ llvm_unreachable("failed to desugar TemplateSpecializationTypeLoc");
+}
+
+namespace {
+
+const ast_matchers::internal::VariadicAllOfMatcher<CXXBaseSpecifier>
+ cxxBaseSpecifier;
+
+AST_MATCHER_P(CXXRecordDecl, hasBase,
+ ast_matchers::internal::Matcher<CXXBaseSpecifier>, InnerMatcher) {
+ if (!Node.hasDefinition())
+ return false;
+ for (const CXXBaseSpecifier &Base : Node.bases())
+ if (InnerMatcher.matches(Base, Finder, Builder))
+ return true;
+ return false;
+}
+
+AST_MATCHER_P(CXXBaseSpecifier, ofType,
+ ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
+ return InnerMatcher.matches(Node.getType(), Finder, Builder);
+}
+
+AST_MATCHER_P(Type, asTST,
+ ast_matchers::internal::Matcher<TemplateSpecializationType>,
+ InnerMatcher) {
+ if (const auto *TST = Node.getAs<TemplateSpecializationType>())
+ return InnerMatcher.matches(*TST, Finder, Builder);
+ return false;
+}
+
+enum StdIteratorArg {
+ ARG_iterator_category = 0,
+ ARG_value_type,
+ ARG_difference_type,
+ ARG_pointer,
+ ARG_reference,
+ ARG_num,
+};
+
+} // namespace
+
+void DeprecatedIteratorBaseCheck::registerMatchers(MatchFinder *Finder) {
+ // Requires C++.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Match record declarations which have std::iterator base.
+ Finder->addMatcher(
+ cxxRecordDecl(
+ anyOf(isClass(), isStruct()), isDefinition(),
+ unless(ast_matchers::isTemplateInstantiation()),
+ hasBase(cxxBaseSpecifier(
+ ofType(asTST(templateSpecializationType(
+ hasDeclaration(namedDecl(
+ hasName("::std::iterator"))))
+ .bind("tst"))))
+ .bind("base")))
+ .bind("subj"),
+ this);
+}
+
+void DeprecatedIteratorBaseCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ SourceManager &SM = *Result.SourceManager;
+ ASTContext &Context = *Result.Context;
+ const auto &Subj = *Result.Nodes.getNodeAs<CXXRecordDecl>("subj");
+ const auto &Base = *Result.Nodes.getNodeAs<CXXBaseSpecifier>("base");
+ const auto &TST = *Result.Nodes.getNodeAs<TemplateSpecializationType>("tst");
+ const auto TSTL = getTSTLoc(Base.getTypeSourceInfo()->getTypeLoc());
+
+ auto Diag = diag(Base.getBaseTypeLoc(),
+ "inheriting from 'std::iterator' is deprecated");
+
+ // Non public inheritance from std::iterator? Skip the strange beast.
+ if (Base.getAccessSpecifier() != AS_public)
+ return;
+
+ StringRef IndentAccSpec;
+ if (const AccessSpecDecl *ASD = getAnyAccSpecDecl(Subj))
+ IndentAccSpec = Lexer::getIndentationForLine(ASD->getLocation(), SM);
+ else
+ IndentAccSpec = Lexer::getIndentationForLine(Subj.getBeginLoc(), SM);
+
+ StringRef Indent;
+ if (const Decl *D = getAnyNonAccSpecDecl(Subj))
+ Indent = Lexer::getIndentationForLine(D->getLocation(), SM);
+ else
+ Indent = IndentAccSpec;
+
+ auto GetRealRange = [&](SourceRange Range) {
+ return Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range), SM,
+ getLangOpts());
+ };
+
+ static const StringRef Names[] = {
+ "iterator_category", "value_type ", "difference_type ",
+ "pointer ", "reference ",
+ };
+
+ SourceRange Overrides[std::size(Names)];
+ llvm::transform(Names, std::begin(Overrides), [&](StringRef Name) -> SourceRange {
+ if (const auto *ND = selectFirst<const NamedDecl>(
+ "arg", match(cxxRecordDecl(
+ has(namedDecl(hasName(Name.rtrim())).bind("arg"))),
+ Subj, Context))) {
+ SourceRange Range = GetRealRange(ND->getSourceRange()).getAsRange();
+ Range.setEnd(findNextTerminator(Range.getEnd(), SM, getLangOpts())
+ .getLocWithOffset(1));
+ return Range;
+ }
+ return {};
+ });
+
+ auto ArgToVal = [&](unsigned Idx) -> std::string {
+ if (Idx < TSTL.getNumArgs()) {
+ SourceRange Range = TSTL.getArgLoc(Idx).getSourceRange();
+ CharSourceRange CharRange = GetRealRange(Range);
+ return Lexer::getSourceText(CharRange, SM, getLangOpts()).str();
+ }
+ switch (Idx) {
+ case ARG_difference_type:
+ return "std::ptrdiff_t";
+ case ARG_pointer:
+ return Context.getPointerType(TST.getArg(ARG_value_type).getAsType())
+ .getAsString(getLangOpts());
+ case ARG_reference:
+ return Context
+ .getLValueReferenceType(TST.getArg(ARG_value_type).getAsType())
+ .getAsString(getLangOpts());
+ default:
+ llvm_unreachable("unexpected argument index");
+ }
+ };
+ auto GenAliasForArg = [&](unsigned Idx) {
+ return (llvm::Twine("using ") + Names[Idx] + " = " + ArgToVal(Idx) + ";")
+ .str();
+ };
+ auto EmitterAnchor =
+ llvm::find_if(Overrides, [](SourceRange SR) { return SR.isValid(); });
+ if (EmitterAnchor != std::end(Overrides)) {
+ const auto B = std::begin(Overrides);
+ for (auto I = B; I != EmitterAnchor; ++I)
+ Diag << FixItHint::CreateInsertion(
+ EmitterAnchor->getBegin(),
+ (llvm::Twine(GenAliasForArg(std::distance(B, I))) + "\n" + Indent)
+ .str());
+
+ for (auto I = std::next(EmitterAnchor); I != std::end(Overrides); ++I)
+ if (I->isValid())
+ EmitterAnchor = I;
+ else
+ Diag << FixItHint::CreateInsertion(
+ EmitterAnchor->getEnd(),
+ (llvm::Twine("\n") + Indent + GenAliasForArg(std::distance(B, I)))
+ .str());
+ } else {
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream StrOS(Buf);
+
+ if (isOpeningRequired(Subj))
+ StrOS << '\n' << IndentAccSpec << "public:";
+
+ for (auto I : llvm::seq<unsigned>(0, ARG_num))
+ StrOS << '\n' << Indent << GenAliasForArg(I);
+
+ StrOS << '\n';
+
+ if (isClosingRequired(Subj))
+ StrOS << '\n' << IndentAccSpec << "private:";
+
+ Diag << FixItHint::CreateInsertion(getInsertLoc(Subj), StrOS.str());
+ }
+ Diag << createBaseRemoval(Subj, Base, SM, getLangOpts());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -6,6 +6,7 @@
ConcatNestedNamespacesCheck.cpp
DeprecatedHeadersCheck.cpp
DeprecatedIosBaseAliasesCheck.cpp
+ DeprecatedIteratorBaseCheck.cpp
LoopConvertCheck.cpp
LoopConvertUtils.cpp
MakeSharedCheck.cpp
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits