Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -401,6 +401,19 @@ /// \endcode std::vector IncludeCategories; + /// \brief Specify a regular expression of suffixes that are allowed in the + /// file-to-main-include mapping. + /// + /// When guessing whether a #include is the "main" include (to assign + /// category 0, see above), use this regex of allowed suffixes to the header + /// stem. A partial match is done, so that: + /// - "" means "arbitrary suffix" + /// - "$" means "no suffix" + /// + /// For example, if configured to "(_test)?$", then a header a.h would be seen + /// as the "main" include in both a.cc and a_test.cc. + std::string IncludeMainRegex; + /// \brief Indent case labels one level from the switch statement. /// /// When ``false``, use the same indentation level as for the switch statement. Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -300,6 +300,7 @@ Style.ExperimentalAutoDetectBinPacking); IO.mapOptional("ForEachMacros", Style.ForEachMacros); IO.mapOptional("IncludeCategories", Style.IncludeCategories); + IO.mapOptional("IncludeMainRegex", Style.IncludeMainRegex); IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels); IO.mapOptional("IndentWidth", Style.IndentWidth); IO.mapOptional("IndentWrappedFunctionNames", @@ -517,6 +518,7 @@ LLVMStyle.IncludeCategories = {{"^\"(llvm|llvm-c|clang|clang-c)/", 2}, {"^(<|\"(gtest|isl|json)/)", 3}, {".*", 1}}; + LLVMStyle.IncludeMainRegex = "$"; LLVMStyle.IndentCaseLabels = false; LLVMStyle.IndentWrappedFunctionNames = false; LLVMStyle.IndentWidth = 2; @@ -569,6 +571,7 @@ GoogleStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true; GoogleStyle.DerivePointerAlignment = true; GoogleStyle.IncludeCategories = {{"^<.*\\.h>", 1}, {"^<.*", 2}, {".*", 3}}; + GoogleStyle.IncludeMainRegex = "([-_](test|unittest))?$"; GoogleStyle.IndentCaseLabels = true; GoogleStyle.KeepEmptyLinesAtTheStartOfBlocks = false; GoogleStyle.ObjCSpaceAfterProperty = false; @@ -1961,8 +1964,12 @@ StringRef HeaderStem = llvm::sys::path::stem(IncludeName.drop_front(1).drop_back(1)); if (FileStem.startswith(HeaderStem)) { - Category = 0; - MainIncludeFound = true; + llvm::Regex MainFileRegex( + (Twine(HeaderStem) + Style.IncludeMainRegex).str()); + if (MainFileRegex.match(FileStem)) { + Category = 0; + MainIncludeFound = true; + } } } IncludesInBlock.push_back({IncludeName, Line, Prev, Category}); Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -9941,6 +9941,7 @@ SpacesBeforeTrailingComments, 1234u); CHECK_PARSE("IndentWidth: 32", IndentWidth, 32u); CHECK_PARSE("ContinuationIndentWidth: 11", ContinuationIndentWidth, 11u); + CHECK_PARSE("CommentPragmas: '// abc$'", CommentPragmas, "// abc$"); Style.PointerAlignment = FormatStyle::PAS_Middle; CHECK_PARSE("PointerAlignment: Left", PointerAlignment, @@ -10099,6 +10100,7 @@ " - Regex: .*\n" " Priority: 1", IncludeCategories, ExpectedCategories); + CHECK_PARSE("IncludeMainRegex: 'abc$'", IncludeMainRegex, "abc$"); } TEST_F(FormatTest, ParsesConfigurationWithLanguages) { Index: unittests/Format/SortIncludesTest.cpp =================================================================== --- unittests/Format/SortIncludesTest.cpp +++ unittests/Format/SortIncludesTest.cpp @@ -175,6 +175,7 @@ } TEST_F(SortIncludesTest, LeavesMainHeaderFirst) { + Style.IncludeMainRegex = "([-_](test|unittest))?$"; EXPECT_EQ("#include \"llvm/a.h\"\n" "#include \"b.h\"\n" "#include \"c.h\"\n", @@ -188,7 +189,7 @@ sort("#include \"llvm/a.h\"\n" "#include \"c.h\"\n" "#include \"b.h\"\n", - "a_main.cc")); + "a_test.cc")); EXPECT_EQ("#include \"llvm/input.h\"\n" "#include \"b.h\"\n" "#include \"c.h\"\n", @@ -197,6 +198,24 @@ "#include \"b.h\"\n", "input.mm")); + // Don't allow prefixes. + EXPECT_EQ("#include \"b.h\"\n" + "#include \"c.h\"\n" + "#include \"llvm/not_a.h\"\n", + sort("#include \"llvm/not_a.h\"\n" + "#include \"c.h\"\n" + "#include \"b.h\"\n", + "a.cc")); + + // Don't do this for _main and other suffixes. + 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", + "a_main.cc")); + // Don't do this in headers. EXPECT_EQ("#include \"b.h\"\n" "#include \"c.h\"\n"