diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index f713e3a6d2c5..a548e835a56d 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -519,6 +519,19 @@ the configuration (without a prefix: ``Auto``). - Regex: '.\*' Priority: 1 +**IncludeIsMainRegex** (``std::string``) + 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. + **IndentCaseLabels** (``bool``) Indent case labels one level from the switch statement. @@ -532,6 +545,22 @@ the configuration (without a prefix: ``Auto``). Indent if a function definition or declaration is wrapped after the type. +**JavaScriptQuotes** (``JavaScriptQuoteStyle``) + The JavaScriptQuoteStyle to use for JavaScript strings. + + Possible values: + + * ``JSQS_Leave`` (in configuration: ``Leave``) + Leave string quotes as they are. + + * ``JSQS_Single`` (in configuration: ``Single``) + Always use single quotes. + + * ``JSQS_Double`` (in configuration: ``Double``) + Always use double quotes. + + + **KeepEmptyLinesAtTheStartOfBlocks** (``bool``) If true, empty lines at the start of blocks are kept. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index d22f56ef3111..eef651e6366c 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -401,6 +401,19 @@ struct FormatStyle { /// \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 IncludeIsMainRegex; + /// \brief Indent case labels one level from the switch statement. /// /// When ``false``, use the same indentation level as for the switch statement. diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 683a57661e9a..5d81ff5ec503 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -300,6 +300,7 @@ template <> struct MappingTraits { Style.ExperimentalAutoDetectBinPacking); IO.mapOptional("ForEachMacros", Style.ForEachMacros); IO.mapOptional("IncludeCategories", Style.IncludeCategories); + IO.mapOptional("IncludeIsMainRegex", Style.IncludeIsMainRegex); IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels); IO.mapOptional("IndentWidth", Style.IndentWidth); IO.mapOptional("IndentWrappedFunctionNames", @@ -517,6 +518,7 @@ FormatStyle getLLVMStyle() { LLVMStyle.IncludeCategories = {{"^\"(llvm|llvm-c|clang|clang-c)/", 2}, {"^(<|\"(gtest|isl|json)/)", 3}, {".*", 1}}; + LLVMStyle.IncludeIsMainRegex = "$"; LLVMStyle.IndentCaseLabels = false; LLVMStyle.IndentWrappedFunctionNames = false; LLVMStyle.IndentWidth = 2; @@ -569,6 +571,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { GoogleStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true; GoogleStyle.DerivePointerAlignment = true; GoogleStyle.IncludeCategories = {{"^<.*\\.h>", 1}, {"^<.*", 2}, {".*", 3}}; + GoogleStyle.IncludeIsMainRegex = "([-_](test|unittest))?$"; GoogleStyle.IndentCaseLabels = true; GoogleStyle.KeepEmptyLinesAtTheStartOfBlocks = false; GoogleStyle.ObjCSpaceAfterProperty = false; @@ -1961,8 +1964,12 @@ tooling::Replacements sortIncludes(const FormatStyle &Style, StringRef Code, StringRef HeaderStem = llvm::sys::path::stem(IncludeName.drop_front(1).drop_back(1)); if (FileStem.startswith(HeaderStem)) { - Category = 0; - MainIncludeFound = true; + llvm::Regex MainIncludeRegex( + (HeaderStem + Style.IncludeIsMainRegex).str()); + if (MainIncludeRegex.match(FileStem)) { + Category = 0; + MainIncludeFound = true; + } } } IncludesInBlock.push_back({IncludeName, Line, Prev, Category}); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index ff932c88c7c0..d2ec4e645c92 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -9941,6 +9941,7 @@ TEST_F(FormatTest, ParsesConfiguration) { 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 @@ TEST_F(FormatTest, ParsesConfiguration) { " - Regex: .*\n" " Priority: 1", IncludeCategories, ExpectedCategories); + CHECK_PARSE("IncludeIsMainRegex: 'abc$'", IncludeIsMainRegex, "abc$"); } TEST_F(FormatTest, ParsesConfigurationWithLanguages) { diff --git a/clang/unittests/Format/SortIncludesTest.cpp b/clang/unittests/Format/SortIncludesTest.cpp index 9e5f2675f089..c8a43fc6240c 100644 --- a/clang/unittests/Format/SortIncludesTest.cpp +++ b/clang/unittests/Format/SortIncludesTest.cpp @@ -175,6 +175,7 @@ TEST_F(SortIncludesTest, HandlesMultilineIncludes) { } TEST_F(SortIncludesTest, LeavesMainHeaderFirst) { + Style.IncludeIsMainRegex = "([-_](test|unittest))?$"; EXPECT_EQ("#include \"llvm/a.h\"\n" "#include \"b.h\"\n" "#include \"c.h\"\n", @@ -188,7 +189,7 @@ TEST_F(SortIncludesTest, LeavesMainHeaderFirst) { 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 @@ TEST_F(SortIncludesTest, LeavesMainHeaderFirst) { "#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"