Manikishan created this revision.
Manikishan added reviewers: cfe-commits, mgorny, christos, MyDeveloperDay.
Herald added a subscriber: krytarowski.
Herald added a project: clang.
Manikishan added subscribers: mgorny, christos.

This new Style rule is made as a part of adding support for NetBSD KNF in 
clang-format. NetBSD have it's own priority of includes which should be 
followed while formatting NetBSD code. This style sorts the Cpp Includes 
according to the priorities of NetBSD, as mentioned in the Style Guide 
<http://cvsweb.netbsd.org/bsdweb.cgi/src/share/misc/style?rev=HEAD&content-type=text/x-cvsweb-markup>
 The working of this Style rule shown below:

**Configuration:**
In addition to this commit I am also adding another diff which Introduces 
NetBSD Style in clang-format, The required configurations are already made in 
this patch and can be used by

  clang-format -style=NetBSD ...

(The NetBSD Style is not fully implemented but it supports SortNetBSDIncludes 
for now).

Here is an example how this Style sorts cpp includes according to NetBSD KNF.
**Before Formatting:  **

  #include <sys/param.h>                
  #include <sys/types.h>                
  #include <sys/ioctl.h>                
  #include <net/if_dl.h>
  #include <net/route.h>
  #include <netinet/in.h>
  #include <sys/socket.h>               
  #include <sys/stat.h>
  #include <sys/wait.h>         
  #include <net/if.h>
  #include <protocols/rwhod.h>
  #include <assert.h>
  #include <paths.h>
  #include "pathnames.h"                
  #include <errno.h>
  #include <inttypes.h>
  #include <stdio.h>
  #include <stdlib.h>

**After Formatting: **

  #include <sys/param.h>
  #include <sys/types.h>
  #include <sys/ioctl.h>
  #include <sys/socket.h>
  #include <sys/stat.h>
  #include <sys/wait.h>
  
  #include <net/if.h>
  #include <net/if_dl.h>
  #include <net/route.h>
  #include <netinet/in.h>
  #include <protocols/rwhod.h>
  
  #include <assert.h>
  #include <errno.h>
  #include <inttypes.h>
  #include <stdio.h>
  #include <stdlib.h>
  
  #include <paths.h>
  
  #include "pathnames.h"




Repository:
  rC Clang

https://reviews.llvm.org/D64695

Files:
  docs/ClangFormatStyleOptions.rst
  include/clang/Format/Format.h
  lib/Format/Format.cpp
  unittests/Format/SortIncludesTest.cpp

Index: unittests/Format/SortIncludesTest.cpp
===================================================================
--- unittests/Format/SortIncludesTest.cpp
+++ unittests/Format/SortIncludesTest.cpp
@@ -665,6 +665,67 @@
                  "#include \"a.h\""));
 }
 
+TEST_F(SortIncludesTest, ParamAndTypesCheck) {
+  FmtStyle = getNetBSDStyle();
+  EXPECT_EQ("#include <sys/param.h>\n"
+			"#include <sys/types.h>\n"
+			"#include <sys/ioctl.h>\n"
+			"#include <sys/socket.h>\n"
+			"#include <sys/stat.h>\n"
+			"#include <sys/wait.h>\n",
+			sort("#include <sys/ioctl.h>\n"
+				 "#include <sys/stat.h>\n"
+				 "#include <sys/socket.h>\n"
+				 "#include <sys/types.h>\n"
+				 "#include <sys/param.h>\n"
+				 "#include <sys/wait.h>\n"));
+	
+}
+
+TEST_F(SortIncludesTest, SortedIncludesInSingleBlockReGroupWithNetBSDSpecifications) {
+  FmtStyle = getNetBSDStyle();
+  EXPECT_EQ("#include <sys/param.h>\n"  
+      "#include <sys/types.h>\n"  
+      "#include <sys/ioctl.h>\n"  
+      "#include <sys/socket.h>\n" 
+      "#include <sys/stat.h>\n"
+      "#include <sys/wait.h>\n"
+      "\n"
+      "#include <net/if.h>\n"
+      "#include <net/if_dl.h>\n"
+      "#include <net/route.h>\n"
+      "#include <netinet/in.h>\n"
+      "#include <protocols/rwhod.h>\n"
+      "\n"
+      "#include <assert.h>\n"
+      "#include <errno.h>\n"
+      "#include <inttypes.h>\n"
+      "#include <stdio.h>\n"
+      "#include <stdlib.h>\n"
+      "\n"
+      "#include <paths.h>\n"
+      "\n"
+      "#include \"pathnames.h\"\n",
+      sort("#include <sys/param.h>\n"		
+           "#include <sys/types.h>\n"		
+           "#include <sys/ioctl.h>\n"		
+           "#include <net/if_dl.h>\n"
+           "#include <net/route.h>\n"
+           "#include <netinet/in.h>\n"
+           "#include <sys/socket.h>\n"	
+           "#include <sys/stat.h>\n"
+           "#include <sys/wait.h>\n"		
+           "#include <net/if.h>\n"
+           "#include <protocols/rwhod.h>\n"
+           "#include <assert.h>\n"
+           "#include <paths.h>\n"
+           "#include \"pathnames.h\"\n"		
+           "#include <errno.h>\n"
+           "#include <inttypes.h>\n"
+           "#include <stdio.h>\n"
+           "#include <stdlib.h>\n"));
+}
+
 } // end namespace
 } // end namespace format
 } // end namespace clang
Index: lib/Format/Format.cpp
===================================================================
--- lib/Format/Format.cpp
+++ lib/Format/Format.cpp
@@ -479,6 +479,7 @@
     IO.mapOptional("RawStringFormats", Style.RawStringFormats);
     IO.mapOptional("ReflowComments", Style.ReflowComments);
     IO.mapOptional("SortIncludes", Style.SortIncludes);
+    IO.mapOptional("SortNetBSDIncludes", Style.SortNetBSDIncludes);
     IO.mapOptional("SortUsingDeclarations", Style.SortUsingDeclarations);
     IO.mapOptional("SpaceAfterCStyleCast", Style.SpaceAfterCStyleCast);
     IO.mapOptional("SpaceAfterLogicalNot", Style.SpaceAfterLogicalNot);
@@ -609,7 +610,7 @@
     return Style;
   FormatStyle Expanded = Style;
   Expanded.BraceWrapping = {false, false, false, false, false, false,
-                            false, false, false, false, false,
+                            false, false, false, false, false, 
                             false, false, true,  true,  true};
   switch (Style.BreakBeforeBraces) {
   case FormatStyle::BS_Linux:
@@ -1035,6 +1036,12 @@
   NetBSDStyle.FixNamespaceComments = true;
   NetBSDStyle.IndentCaseLabels = false;
   NetBSDStyle.IndentWidth = 8;
+  NetBSDStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup;
+  NetBSDStyle.IncludeStyle.IncludeCategories = {
+      {"^<sys/", 7},        {"^<uvm/", 6},  {"^<(net.*|protocols)/", 5},
+      {"^<(fs*)", 4},       {"^<path*", 3}, {"^<[^\/].*\.h>", 2},
+      {"^\"\w.*\.h\"$", 1}, {".*", 0}};
+  NetBSDStyle.SortNetBSDIncludes = true;
   NetBSDStyle.TabWidth = 8;
   NetBSDStyle.UseTab = FormatStyle::UT_Always;
   return NetBSDStyle;
@@ -1782,7 +1789,163 @@
   }
   return std::make_pair(CursorIndex, OffsetToEOL);
 }
+enum CppIncludeHeadersKind {
+  IHK_KERNELHEADERS,
+  IHK_NETWORKHEADERS,
+  IHK_FILESYSHEADERS,
+  IHK_MACHINESHEADERS,
+  IHK_ARCHHEADERS,
+  IHK_USERHEADERS,
+  IHK_INCLUDESWITHQUOTES,
+};
+
+CppIncludeHeadersKind getHeadersKind(std::string Filename) {
+  SmallVector<StringRef, 4> Matches;
+  const char KernelHeaderPattern[] = R"(^<(sys.*|uvm|dev)/)";
+  const char NetworkHeaderPattern[] = R"(^<(net.*|protocols)/)";
+  const char FilesSystemHeaderPattern[] =
+      R"(^<(fs|miscfs|msdosfs|nfs|ntfs|ufs))";
+  const char MachineHeaderPattern[] = R"(^<machine/)";
+  const char ArchHeadersPattern[] = R"(^<(x86|amd64|i386|xen)/)";
+  const char UserHeaderPattern[] = R"(^<[a-zA-Z0-9]*\.h>)";
+  const char IncludeWithQuotesPattern[] = R"(^\"*$\")";
+  llvm::Regex MatchKernel = llvm::Regex(KernelHeaderPattern);
+  llvm::Regex MatchNetwork = llvm::Regex(NetworkHeaderPattern);
+  llvm::Regex MatchFileSys = llvm::Regex(FilesSystemHeaderPattern);
+  llvm::Regex MatchMachine = llvm::Regex(MachineHeaderPattern);
+  llvm::Regex MatchArch = llvm::Regex(ArchHeadersPattern);
+  llvm::Regex MatchUser = llvm::Regex(UserHeaderPattern);
+  llvm::Regex MatchQuotes = llvm::Regex(IncludeWithQuotesPattern);
+  if (MatchKernel.match(Filename))
+    return IHK_KERNELHEADERS;
+  else if (MatchNetwork.match(Filename))
+    return IHK_NETWORKHEADERS;
+  else if (MatchArch.match(Filename))
+    return IHK_ARCHHEADERS;
+  else if (MatchQuotes.match(Filename))
+    return IHK_INCLUDESWITHQUOTES;
+  else if (MatchUser.match(Filename))
+    return IHK_USERHEADERS;
+  else if (MatchFileSys.match(Filename))
+    return IHK_FILESYSHEADERS;
+  else if (MatchMachine.match(Filename))
+    return IHK_MACHINESHEADERS;
+  return IHK_INCLUDESWITHQUOTES;
+}
+
+unsigned getNetBSDIncludePriority(std::string Filename) {
+  CppIncludeHeadersKind CK = getHeadersKind(Filename);
+  switch (CK) {
+  case IHK_KERNELHEADERS:
+    return llvm::StringSwitch<unsigned>(Filename)
+        .Case("<sys/param.h>", 0)
+        .Case("<sys/types.h>", 1)
+        .StartsWith("<sys/", 2)
+        .StartsWith("<uvm/", 3)
+        .StartsWith("<dev/", 7)
+        .Default(1002);
+  case IHK_NETWORKHEADERS:
+    return llvm::StringSwitch<unsigned>(Filename)
+        .StartsWith("<net", 4)
+        .StartsWith("<protocols", 5)
+        .Default(1002);
+  case IHK_FILESYSHEADERS:
+    return 6;
+  case IHK_MACHINESHEADERS:
+    return 8;
+  case IHK_ARCHHEADERS:
+    return 9;
+  case IHK_USERHEADERS:
+    return llvm::StringSwitch<unsigned>(Filename)
+      .StartsWith("<path",11)
+      .Default(10);
+  case IHK_INCLUDESWITHQUOTES:
+    return 12;
+  }
+  return 100;
+}
+static void sortNetBSDIncludes(
+    const FormatStyle &Style, const SmallVectorImpl<IncludeDirective> &Includes,
+    ArrayRef<tooling::Range> Ranges, StringRef FileName, StringRef Code,
+    tooling::Replacements &Replaces, unsigned *Cursor) {
+  unsigned IncludesBeginOffset = Includes.front().Offset;
+  unsigned IncludesEndOffset =
+      Includes.back().Offset + Includes.back().Text.size();
+  unsigned IncludesBlockSize = IncludesEndOffset - IncludesBeginOffset;
+  if (!affectsRange(Ranges, IncludesBeginOffset, IncludesEndOffset))
+    return;
+  SmallVector<unsigned, 16> Indices;
+  SmallVector<unsigned, 16> Includes_p;
+  for (unsigned i = 0, e = Includes.size(); i != e; ++i) {
+    unsigned pl = getNetBSDIncludePriority(Includes[i].Filename);
+    Includes_p.push_back(pl);
+    Indices.push_back(i);
+  }
+  for (unsigned i = 0, e = Includes.size(); i != e; ++i) {
+    Indices.push_back(i);
+  }
+  llvm::stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
+    return std::tie(Includes_p[LHSI], Includes[LHSI].Filename) <
+           std::tie(Includes_p[RHSI], Includes[RHSI].Filename);
+  });
+
+  // The index of the include on which the cursor will be put after
+  // sorting/deduplicating.
+  unsigned CursorIndex;
+  // The offset from cursor to the end of line.
+  unsigned CursorToEOLOffset;
+  if (Cursor)
+    std::tie(CursorIndex, CursorToEOLOffset) =
+        FindCursorIndex(Includes, Indices, *Cursor);
+  // Deduplicate #includes.
+  Indices.erase(std::unique(Indices.begin(), Indices.end(),
+                            [&](unsigned LHSI, unsigned RHSI) {
+                              return Includes[LHSI].Text == Includes[RHSI].Text;
+                            }),
+                Indices.end());
+
+  int CurrentCategory = Includes.front().Category;
+
+  // If the #includes are out of order, we generate a single replacement fixing
+  // the entire block. Otherwise, no replacement is generated.
+  // In case Style.IncldueStyle.IncludeBlocks != IBS_Preserve, this check is not
+  // enough as additional newlines might be added or removed across #include
+  // blocks. This we handle below by generating the updated #imclude blocks and
+  // comparing it to the original.
+  if (Indices.size() == Includes.size() &&
+      std::is_sorted(Indices.begin(), Indices.end()) &&
+      Style.IncludeStyle.IncludeBlocks == tooling::IncludeStyle::IBS_Preserve)
+    return;
 
+  std::string result;
+  for (unsigned Index : Indices) {
+    if (!result.empty()) {
+      result += "\n";
+      if ((Style.IncludeStyle.IncludeBlocks ==
+           tooling::IncludeStyle::IBS_Regroup) &&
+          CurrentCategory != Includes[Index].Category)
+        result += "\n";
+    }
+    result += Includes[Index].Text;
+    if (Cursor && CursorIndex == Index)
+      *Cursor = IncludesBeginOffset + result.size() - CursorToEOLOffset;
+    CurrentCategory = Includes[Index].Category;
+  }
+
+  // If the #includes are out of order, we generate a single replacement fixing
+  // the entire range of blocks. Otherwise, no replacement is generated.
+  if (result == Code.substr(IncludesBeginOffset, IncludesBlockSize))
+    return;
+
+  auto Err = Replaces.add(tooling::Replacement(
+      FileName, Includes.front().Offset, IncludesBlockSize, result));
+  // FIXME: better error handling. For now, just skip the replacement for the
+  // release version.
+  if (Err) {
+    llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+    assert(false);
+  }
+}
 // Sorts and deduplicate a block of includes given by 'Includes' alphabetically
 // adding the necessary replacement to 'Replaces'. 'Includes' must be in strict
 // source order.
@@ -1793,8 +1956,8 @@
 static void sortCppIncludes(const FormatStyle &Style,
                             const SmallVectorImpl<IncludeDirective> &Includes,
                             ArrayRef<tooling::Range> Ranges, StringRef FileName,
-                            StringRef Code,
-                            tooling::Replacements &Replaces, unsigned *Cursor) {
+                            StringRef Code, tooling::Replacements &Replaces,
+                            unsigned *Cursor) {
   unsigned IncludesBeginOffset = Includes.front().Offset;
   unsigned IncludesEndOffset =
       Includes.back().Offset + Includes.back().Text.size();
@@ -1925,8 +2088,12 @@
           MainIncludeFound = true;
         IncludesInBlock.push_back({IncludeName, Line, Prev, Category});
       } else if (!IncludesInBlock.empty() && !EmptyLineSkipped) {
-        sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code,
-                        Replaces, Cursor);
+        if (Style.SortNetBSDIncludes)
+          sortNetBSDIncludes(Style, IncludesInBlock, Ranges, FileName, Code,
+                             Replaces, Cursor);
+        else
+          sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code,
+                          Replaces, Cursor);
         IncludesInBlock.clear();
         FirstIncludeBlock = false;
       }
@@ -1937,8 +2104,12 @@
     SearchFrom = Pos + 1;
   }
   if (!IncludesInBlock.empty()) {
-    sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, Replaces,
-                    Cursor);
+    if (Style.SortNetBSDIncludes)
+      sortNetBSDIncludes(Style, IncludesInBlock, Ranges, FileName, Code,
+                         Replaces, Cursor);
+    else
+      sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, Replaces,
+                      Cursor);
   }
   return Replaces;
 }
Index: include/clang/Format/Format.h
===================================================================
--- include/clang/Format/Format.h
+++ include/clang/Format/Format.h
@@ -1681,6 +1681,9 @@
   /// \endcode
   bool SortIncludes;
 
+  /// If ``true``, clang-format will sort ``#includes`` according to NetBSD Style.
+  bool SortNetBSDIncludes;
+
   /// If ``true``, clang-format will sort using declarations.
   ///
   /// The order of using declarations is defined as follows:
@@ -1995,6 +1998,7 @@
                R.PenaltyBreakTemplateDeclaration &&
            PointerAlignment == R.PointerAlignment &&
            RawStringFormats == R.RawStringFormats &&
+           SortNetBSDIncludes == R.SortNetBSDIncludes &&
            SpaceAfterCStyleCast == R.SpaceAfterCStyleCast &&
            SpaceAfterLogicalNot == R.SpaceAfterLogicalNot &&
            SpaceAfterTemplateKeyword == R.SpaceAfterTemplateKeyword &&
Index: docs/ClangFormatStyleOptions.rst
===================================================================
--- docs/ClangFormatStyleOptions.rst
+++ docs/ClangFormatStyleOptions.rst
@@ -2001,6 +2001,10 @@
      #include "b.h"                 vs.     #include "a.h"
      #include "a.h"                         #include "b.h"
 
+**SortNetBSDIncludes** (``bool``)
+  If ``true``, clang-format will sort ``#includes`` according to NetBSD style.
+
+  
 **SortUsingDeclarations** (``bool``)
   If ``true``, clang-format will sort using declarations.
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to