From 5b12ee708d07fc648e3aea349f9832bca0c6db76 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Mon, 21 Mar 2016 14:11:27 +0000 Subject: [PATCH] clang-format: Make include sorting's main include detection configurable. This patch adds a regular expression to configure suffixes of an included file to check whether it is the "main" include of the current file. Previously, clang-format has allowed arbitrary suffixes on the formatted file, which is still the case when no IncludeMainRegex is specified. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263943 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/ClangFormatStyleOptions.rst | 29 +++++++++++++++++++++++++++ include/clang/Format/Format.h | 13 ++++++++++++ lib/Format/Format.cpp | 11 ++++++++-- unittests/Format/FormatTest.cpp | 2 ++ unittests/Format/SortIncludesTest.cpp | 21 ++++++++++++++++++- 5 files changed, 73 insertions(+), 3 deletions(-) diff --git a/docs/ClangFormatStyleOptions.rst b/docs/ClangFormatStyleOptions.rst index f713e3a6d2..a548e835a5 100644 --- a/docs/ClangFormatStyleOptions.rst +++ b/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/include/clang/Format/Format.h b/include/clang/Format/Format.h index d22f56ef31..eef651e636 100644 --- a/include/clang/Format/Format.h +++ b/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/lib/Format/Format.cpp b/lib/Format/Format.cpp index 683a57661e..5d81ff5ec5 100644 --- a/lib/Format/Format.cpp +++ b/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/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index ff932c88c7..d2ec4e645c 100644 --- a/unittests/Format/FormatTest.cpp +++ b/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/unittests/Format/SortIncludesTest.cpp b/unittests/Format/SortIncludesTest.cpp index 9e5f2675f0..c8a43fc624 100644 --- a/unittests/Format/SortIncludesTest.cpp +++ b/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" -- 2.40.0