nick updated this revision to Diff 336533.
nick edited the summary of this revision.
nick added reviewers: njames93, steveire.
nick added a comment.

Rebased. Now using native `cxxBaseSpecifier` and `hasDirectBase`.


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
@@ -221,6 +221,7 @@
    `modernize-concat-nested-namespaces <modernize-concat-nested-namespaces.html>`_, "Yes"
    `modernize-deprecated-headers <modernize-deprecated-headers.html>`_, "Yes"
    `modernize-deprecated-ios-base-aliases <modernize-deprecated-ios-base-aliases.html>`_, "Yes"
+   `modernize-deprecated-iterator-base <modernize-deprecated-iterator-base.html>`_, "Yes"
    `modernize-loop-convert <modernize-loop-convert.html>`_, "Yes"
    `modernize-make-shared <modernize-make-shared.html>`_, "Yes"
    `modernize-make-unique <modernize-make-unique.html>`_, "Yes"
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -95,6 +95,12 @@
   Finds member initializations in the constructor body which can be placed into
   the initialization list instead.
 
+- 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 check aliases
 ^^^^^^^^^^^^^^^^^
 
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"
@@ -58,6 +59,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,322 @@
+//===--- 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;
+
+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 {
+
+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()),
+          hasDirectBase(cxxBaseSpecifier(
+                            hasType(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
@@ -9,6 +9,7 @@
   ConcatNestedNamespacesCheck.cpp
   DeprecatedHeadersCheck.cpp
   DeprecatedIosBaseAliasesCheck.cpp
+  DeprecatedIteratorBaseCheck.cpp
   LoopConvertCheck.cpp
   LoopConvertUtils.cpp
   MakeSharedCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to