Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -259,6 +259,21 @@ /// For example: BOOST_FOREACH. std::vector ForEachMacros; + /// \brief Regular expressions denoting the different #include categories used + /// for ordering #includes. + /// + /// These regular expressions are matched against the filename of an include + /// (including the <> or "") in order. The value belonging to the first + /// matching regular expression is assigned and #includes are sorted first + /// according to increasing category number and then alphabetically within + /// each category. + /// + /// If none of the regular expressions match, UINT_MAX is assigned as + /// category. The main header for a source file automatically gets category 0, + /// so that it is kept at the beginning of the #includes + /// (http://llvm.org/docs/CodingStandards.html#include-style). + std::vector> IncludeCategories; + /// \brief Indent case labels one level from the switch statement. /// /// When \c false, use the same indentation level as for the switch statement. Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -13,6 +13,7 @@ /// //===----------------------------------------------------------------------===// +#include "clang/Format/Format.h" #include "ContinuationIndenter.h" #include "TokenAnnotator.h" #include "UnwrappedLineFormatter.h" @@ -21,7 +22,6 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/SourceManager.h" -#include "clang/Format/Format.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Allocator.h" @@ -375,6 +375,9 @@ LLVMStyle.ForEachMacros.push_back("foreach"); LLVMStyle.ForEachMacros.push_back("Q_FOREACH"); LLVMStyle.ForEachMacros.push_back("BOOST_FOREACH"); + LLVMStyle.IncludeCategories = {{"^\"(llvm|llvm-c|clang|clang-c)/", 2}, + {"^(<|\"(gtest|isl|json)/)", 3}, + {".*", 1}}; LLVMStyle.IndentCaseLabels = false; LLVMStyle.IndentWrappedFunctionNames = false; LLVMStyle.IndentWidth = 2; @@ -423,6 +426,7 @@ GoogleStyle.AlwaysBreakTemplateDeclarations = true; GoogleStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true; GoogleStyle.DerivePointerAlignment = true; + GoogleStyle.IncludeCategories = {{"^<.*\\.h>", 1}, {"^<.*", 2}, {".*", 3}}; GoogleStyle.IndentCaseLabels = true; GoogleStyle.KeepEmptyLinesAtTheStartOfBlocks = false; GoogleStyle.ObjCSpaceAfterProperty = false; @@ -1575,7 +1579,7 @@ StringRef Filename; StringRef Text; unsigned Offset; - bool IsAngled; + unsigned Category; }; } // end anonymous namespace @@ -1605,7 +1609,8 @@ for (unsigned i = 0, e = Includes.size(); i != e; ++i) Indices.push_back(i); std::sort(Indices.begin(), Indices.end(), [&](unsigned LHSI, unsigned RHSI) { - return Includes[LHSI].Filename < Includes[RHSI].Filename; + return std::tie(Includes[LHSI].Category, Includes[LHSI].Filename) < + std::tie(Includes[RHSI].Category, Includes[RHSI].Filename); }); // If the #includes are out of order, we generate a single replacement fixing @@ -1642,22 +1647,46 @@ tooling::Replacements Replaces; unsigned Prev = 0; unsigned SearchFrom = 0; - llvm::Regex IncludeRegex(R"(^[\t\ ]*#[\t\ ]*include[^"<]*["<]([^">]*)([">]))"); + llvm::Regex IncludeRegex( + R"(^[\t\ ]*#[\t\ ]*include[^"<]*(["<][^">]*[">]))"); SmallVector Matches; SmallVector IncludesInBlock; + + // In compiled files, consider the first #include to be the main #include of + // the file if it is not a system #include. This ensures that the header + // doesn't have hidden dependencies + // (http://llvm.org/docs/CodingStandards.html#include-style). + bool LookForMainHeader = FileName.endswith(".c") || + FileName.endswith(".cc") || + FileName.endswith(".cpp")|| + FileName.endswith(".c++")|| + FileName.endswith(".cxx"); + + // Create pre-compile regular expressions for the #include categories. + SmallVector CategoryRegexs; + for (const auto &IncludeBlock : Style.IncludeCategories) + CategoryRegexs.emplace_back(IncludeBlock.first); + for (;;) { auto Pos = Code.find('\n', SearchFrom); StringRef Line = Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev); if (!Line.endswith("\\")) { if (IncludeRegex.match(Line, &Matches)) { - bool IsAngled = Matches[2] == ">"; - if (!IncludesInBlock.empty() && - IsAngled != IncludesInBlock.back().IsAngled) { - sortIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces); - IncludesInBlock.clear(); + unsigned Category; + if (LookForMainHeader &&! Matches[1].startswith("<")) { + Category = 0; + } else { + Category = UINT_MAX; + for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i) { + if (CategoryRegexs[i].match(Matches[1])) { + Category = Style.IncludeCategories[i].second; + break; + } + } } - IncludesInBlock.push_back({Matches[1], Line, Prev, Matches[2] == ">"}); + LookForMainHeader = false; + IncludesInBlock.push_back({Matches[1], Line, Prev, Category}); } else if (!IncludesInBlock.empty()) { sortIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces); IncludesInBlock.clear(); Index: tools/clang-format/ClangFormat.cpp =================================================================== --- tools/clang-format/ClangFormat.cpp +++ tools/clang-format/ClangFormat.cpp @@ -73,7 +73,7 @@ cl::init("LLVM"), cl::cat(ClangFormatCategory)); static cl::opt -AssumeFilename("assume-filename", +AssumeFileName("assume-filename", cl::desc("When reading from stdin, clang-format assumes this\n" "filename to look for a style config file (with\n" "-style=file) and to determine the language."), @@ -239,13 +239,13 @@ std::vector Ranges; if (fillRanges(Code.get(), Ranges)) return true; - FormatStyle FormatStyle = getStyle( - Style, (FileName == "-") ? AssumeFilename : FileName, FallbackStyle); + StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName; + FormatStyle FormatStyle = getStyle(Style, AssumedFileName, FallbackStyle); Replacements Replaces; std::string ChangedCode; if (SortIncludes) { Replaces = - sortIncludes(FormatStyle, Code->getBuffer(), Ranges, FileName); + sortIncludes(FormatStyle, Code->getBuffer(), Ranges, AssumedFileName); ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces); for (const auto &R : Replaces) Ranges.push_back({R.getOffset(), R.getLength()}); @@ -324,7 +324,7 @@ if (DumpConfig) { std::string Config = clang::format::configurationAsText(clang::format::getStyle( - Style, FileNames.empty() ? AssumeFilename : FileNames[0], + Style, FileNames.empty() ? AssumeFileName : FileNames[0], FallbackStyle)); llvm::outs() << Config << "\n"; return 0; Index: unittests/Format/SortIncludesTest.cpp =================================================================== --- unittests/Format/SortIncludesTest.cpp +++ unittests/Format/SortIncludesTest.cpp @@ -20,13 +20,15 @@ class SortIncludesTest : public ::testing::Test { protected: - std::string sort(llvm::StringRef Code) { + std::string sort(llvm::StringRef Code, StringRef FileName = "input.cpp") { std::vector Ranges(1, tooling::Range(0, Code.size())); - std::string Sorted = applyAllReplacements( - Code, sortIncludes(getLLVMStyle(), Code, Ranges, "input.cpp")); - return applyAllReplacements( - Sorted, reformat(getLLVMStyle(), Sorted, Ranges, "input.cpp")); + std::string Sorted = + applyAllReplacements(Code, sortIncludes(Style, Code, Ranges, FileName)); + return applyAllReplacements(Sorted, + reformat(Style, Sorted, Ranges, FileName)); } + + FormatStyle Style = getLLVMStyle(); }; TEST_F(SortIncludesTest, BasicSorting) { @@ -76,13 +78,23 @@ "#include \"c.h\"\n" "\n" "#include \"b.h\"\n", - sort("#include \"c.h\"\n" - "#include \"a.h\"\n" + sort("#include \"a.h\"\n" + "#include \"c.h\"\n" "\n" "#include \"b.h\"\n")); } TEST_F(SortIncludesTest, HandlesAngledIncludesAsSeparateBlocks) { + EXPECT_EQ("#include \"a.h\"\n" + "#include \"c.h\"\n" + "#include \n" + "#include \n", + sort("#include \n" + "#include \n" + "#include \"c.h\"\n" + "#include \"a.h\"\n")); + + Style = getGoogleStyle(FormatStyle::LK_Cpp); EXPECT_EQ("#include \n" "#include \n" "#include \"a.h\"\n" @@ -103,6 +115,24 @@ "#include \"b.h\"\n")); } +TEST_F(SortIncludesTest, LeavesMainHeaderFirst) { + EXPECT_EQ("#include \"llvm/a.h\"\n" + "#include \"b.h\"\n" + "#include \"c.h\"\n", + sort("#include \"llvm/a.h\"\n" + "#include \"c.h\"\n" + "#include \"b.h\"\n")); + + // Don't do this in headers. + EXPECT_EQ("#include \"b.h\"\n" + "#include \"c.h\"\n" + "#include \"llvm/a.h\"\n", + sort("#include \"llvm/a.h\"\n" + "#include \"c.h\"\n" + "#include \"b.h\"\n", + "some_header.h")); +} + } // end namespace } // end namespace format } // end namespace clang