compositeprimes created this revision.
compositeprimes added a reviewer: alexfh.
compositeprimes added a project: clang-tools-extra.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
compositeprimes requested review of this revision.

Update IncludeSorter/IncludeInserter to support objective-c google style (part 
1):

1. Correctly consider .mm/.m extensions
2. Correctly categorize category headers.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D89276

Files:
  clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp
  clang-tools-extra/clang-tidy/utils/IncludeSorter.h
  clang-tools-extra/unittests/clang-tidy/IncludeInserterTest.cpp

Index: clang-tools-extra/unittests/clang-tidy/IncludeInserterTest.cpp
===================================================================
--- clang-tools-extra/unittests/clang-tidy/IncludeInserterTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/IncludeInserterTest.cpp
@@ -28,8 +28,10 @@
 
 class IncludeInserterCheckBase : public ClangTidyCheck {
 public:
-  IncludeInserterCheckBase(StringRef CheckName, ClangTidyContext *Context)
-      : ClangTidyCheck(CheckName, Context) {}
+  IncludeInserterCheckBase(StringRef CheckName, ClangTidyContext *Context,
+                           utils::IncludeSorter::IncludeStyle Style =
+                               utils::IncludeSorter::IS_Google)
+      : ClangTidyCheck(CheckName, Context), Inserter(Style) {}
 
   void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
                            Preprocessor *ModuleExpanderPP) override {
@@ -50,7 +52,7 @@
 
   virtual std::vector<StringRef> headersToInclude() const = 0;
 
-  utils::IncludeInserter Inserter{utils::IncludeSorter::IS_Google};
+  utils::IncludeInserter Inserter;
 };
 
 class NonSystemHeaderInserterCheck : public IncludeInserterCheckBase {
@@ -111,6 +113,30 @@
   }
 };
 
+class ObjCEarlyInAlphabetHeaderInserterCheck : public IncludeInserterCheckBase {
+public:
+  ObjCEarlyInAlphabetHeaderInserterCheck(StringRef CheckName,
+                                         ClangTidyContext *Context)
+      : IncludeInserterCheckBase(CheckName, Context,
+                                 utils::IncludeSorter::IS_Google_ObjC) {}
+
+  std::vector<StringRef> headersToInclude() const override {
+    return {"a/header.h"};
+  }
+};
+
+class ObjCCategoryHeaderInserterCheck : public IncludeInserterCheckBase {
+public:
+  ObjCCategoryHeaderInserterCheck(StringRef CheckName,
+                                         ClangTidyContext *Context)
+      : IncludeInserterCheckBase(CheckName, Context,
+                                 utils::IncludeSorter::IS_Google_ObjC) {}
+
+  std::vector<StringRef> headersToInclude() const override {
+    return {"clang_tidy/tests/insert_includes_test_header+foo.h"};
+  }
+};
+
 template <typename Check>
 std::string runCheckOnCode(StringRef Code, StringRef Filename) {
   std::vector<ClangTidyError> Errors;
@@ -120,6 +146,10 @@
                                       {"clang_tidy/tests/"
                                        "insert_includes_test_header.h",
                                        "\n"},
+                                      // ObjC category.
+                                      {"clang_tidy/tests/"
+                                       "insert_includes_test_header+foo.h",
+                                       "\n"},
                                       // Non system headers
                                       {"a/header.h", "\n"},
                                       {"path/to/a/header.h", "\n"},
@@ -635,6 +665,47 @@
                                    "insert_includes_test_header.cc"));
 }
 
+TEST(IncludeInserterTest, InsertHeaderObjectiveC) {
+  const char *PreCode = R"(
+#import "clang_tidy/tests/insert_includes_test_header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#import "clang_tidy/tests/insert_includes_test_header.h"
+
+#import "a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<ObjCEarlyInAlphabetHeaderInserterCheck>(
+                          PreCode, "repo/clang_tidy/tests/"
+                                   "insert_includes_test_header.mm"));
+}
+
+TEST(IncludeInserterTest, InsertCategoryHeaderObjectiveC) {
+  const char *PreCode = R"(
+#import "clang_tidy/tests/insert_includes_test_header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#import "clang_tidy/tests/insert_includes_test_header.h"
+#import "clang_tidy/tests/insert_includes_test_header+foo.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<ObjCCategoryHeaderInserterCheck>(
+                          PreCode, "devtools/cymbal/clang_tidy/tests/"
+                                   "insert_includes_test_header.mm"));
+}
+
 } // anonymous namespace
 } // namespace tidy
 } // namespace clang
Index: clang-tools-extra/clang-tidy/utils/IncludeSorter.h
===================================================================
--- clang-tools-extra/clang-tidy/utils/IncludeSorter.h
+++ clang-tools-extra/clang-tidy/utils/IncludeSorter.h
@@ -23,7 +23,7 @@
 class IncludeSorter {
 public:
   /// Supported include styles.
-  enum IncludeStyle { IS_LLVM = 0, IS_Google = 1 };
+  enum IncludeStyle { IS_LLVM = 0, IS_Google = 1, IS_Google_ObjC };
 
   /// The classifications of inclusions, in the order they should be sorted.
   enum IncludeKinds {
Index: clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp
===================================================================
--- clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp
+++ clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "IncludeSorter.h"
+#include <algorithm>
 #include "clang/Basic/SourceManager.h"
 #include "clang/Lex/Lexer.h"
 
@@ -36,6 +37,17 @@
     return RemoveFirstSuffix(
         RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}), {"Test"});
   }
+  if (Style == IncludeSorter::IS_Google_ObjC) {
+    StringRef canonical =
+        RemoveFirstSuffix(RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h",
+                                                  ".hpp", ".mm", ".m"}),
+                          {"_unittest", "_regtest", "_test", "Test"});
+
+    // Objective-C categories have a `+suffix` format, but should be grouped
+    // with the file they are a category of.
+    return canonical.substr(
+        0, canonical.find_first_of('+', canonical.find_last_of('/')));
+  }
   return RemoveFirstSuffix(
       RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}),
       {"_unittest", "_regtest", "_test"});
@@ -65,7 +77,8 @@
       || CanonicalInclude.endswith(CanonicalFile)) {
     return IncludeSorter::IK_MainTUInclude;
   }
-  if (Style == IncludeSorter::IS_Google) {
+  if ((Style == IncludeSorter::IS_Google)
+      || (Style == IncludeSorter::IS_Google_ObjC)) {
     std::pair<StringRef, StringRef> Parts = CanonicalInclude.split("/public/");
     std::string AltCanonicalInclude =
         Parts.first.str() + "/internal/" + Parts.second.str();
@@ -81,6 +94,24 @@
   return IncludeSorter::IK_NonSystemInclude;
 }
 
+
+int CompareHeaders(StringRef LHS, StringRef RHS,
+                   IncludeSorter::IncludeStyle Style) {
+  if (Style == IncludeSorter::IncludeStyle::IS_Google_ObjC) {
+    const std::pair<const char *, const char *> &mismatch =
+        std::mismatch(LHS.begin(), LHS.end(), RHS.begin());
+    if ((mismatch.first != LHS.end()) && (mismatch.second != RHS.end())) {
+      if ((*mismatch.first == '.') && (*mismatch.second == '+')) {
+        return -1;
+      }
+      if ((*mismatch.first == '+') && (*mismatch.second == '.')) {
+        return 1;
+      }
+    }
+  }
+  return LHS.compare(RHS);
+}
+
 } // namespace
 
 IncludeSorter::IncludeSorter(const SourceManager *SourceMgr,
@@ -112,9 +143,16 @@
 
 Optional<FixItHint> IncludeSorter::CreateIncludeInsertion(StringRef FileName,
                                                           bool IsAngled) {
-  std::string IncludeStmt =
-      IsAngled ? llvm::Twine("#include <" + FileName + ">\n").str()
-               : llvm::Twine("#include \"" + FileName + "\"\n").str();
+  std::string IncludeStmt;
+  if (Style == IncludeStyle::IS_Google_ObjC) {
+    IncludeStmt = IsAngled
+                      ? llvm::Twine("#import <" + FileName + ">\n").str()
+                      : llvm::Twine("#import \"" + FileName + "\"\n").str();
+  } else {
+    IncludeStmt = IsAngled
+                      ? llvm::Twine("#include <" + FileName + ">\n").str()
+                      : llvm::Twine("#include \"" + FileName + "\"\n").str();
+  }
   if (SourceLocations.empty()) {
     // If there are no includes in this file, add it in the first line.
     // FIXME: insert after the file comment or the header guard, if present.
@@ -128,7 +166,7 @@
 
   if (!IncludeBucket[IncludeKind].empty()) {
     for (const std::string &IncludeEntry : IncludeBucket[IncludeKind]) {
-      if (FileName < IncludeEntry) {
+      if (CompareHeaders(FileName, IncludeEntry, Style) < 0) {
         const auto &Location = IncludeLocations[IncludeEntry][0];
         return FixItHint::CreateInsertion(Location.getBegin(), IncludeStmt);
       } else if (FileName == IncludeEntry) {
@@ -181,7 +219,8 @@
 OptionEnumMapping<utils::IncludeSorter::IncludeStyle>::getEnumMapping() {
   static constexpr std::pair<utils::IncludeSorter::IncludeStyle, StringRef>
       Mapping[] = {{utils::IncludeSorter::IS_LLVM, "llvm"},
-                   {utils::IncludeSorter::IS_Google, "google"}};
+                   {utils::IncludeSorter::IS_Google, "google"},
+                   {utils::IncludeSorter::IS_Google_ObjC, "google-objc"}};
   return makeArrayRef(Mapping);
 }
 } // namespace tidy
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to