Skip to content

Commit

Permalink
[clang-format] Add option for case sensitive regexes for sorted includes
Browse files Browse the repository at this point in the history
I think the title says everything.

Reviewed By: MyDeveloperDay

Patch By:  HazardyKnusperkeks

Differential Revision: https://reviews.llvm.org/D91507
  • Loading branch information
mydeveloperday committed Dec 5, 2020
1 parent ce6269f commit 8668eae
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 30 deletions.
4 changes: 4 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Expand Up @@ -1719,6 +1719,9 @@ the configuration (without a prefix: ``Auto``).
priority for order.``SortPriority`` is set to the value of ``Priority``
as default if it is not assigned.

Each regular expression can be marked as case sensitive with the field
``CaseSensitive``, per default it is not.

To configure this in the .clang-format file, use:

.. code-block:: yaml
Expand All @@ -1727,6 +1730,7 @@ the configuration (without a prefix: ``Auto``).
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 2
CaseSensitive: true
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '<[[:alnum:].]+>'
Expand Down
16 changes: 12 additions & 4 deletions clang/include/clang/Tooling/Inclusions/IncludeStyle.h
Expand Up @@ -60,8 +60,11 @@ struct IncludeStyle {
int Priority;
/// The custom priority to sort before grouping.
int SortPriority;
/// If the regular expression is case sensitive.
bool RegexIsCaseSensitive;
bool operator==(const IncludeCategory &Other) const {
return Regex == Other.Regex && Priority == Other.Priority;
return Regex == Other.Regex && Priority == Other.Priority &&
RegexIsCaseSensitive == Other.RegexIsCaseSensitive;
}
};

Expand All @@ -84,20 +87,25 @@ struct IncludeStyle {
/// (https://llvm.org/docs/CodingStandards.html#include-style). However, you
/// can also assign negative priorities if you have certain headers that
/// always need to be first.
///
///
/// There is a third and optional field ``SortPriority`` which can used while
/// ``IncludeBloks = IBS_Regroup`` to define the priority in which ``#includes``
/// should be ordered, and value of ``Priority`` defines the order of
/// ``IncludeBloks = IBS_Regroup`` to define the priority in which
/// ``#includes`` should be ordered, and value of ``Priority`` defines the
/// order of
/// ``#include blocks`` and also enables to group ``#includes`` of different
/// priority for order.``SortPriority`` is set to the value of ``Priority``
/// as default if it is not assigned.
///
/// Each regular expression can be marked as case sensitive with the field
/// ``CaseSensitive``, per default it is not.
///
/// To configure this in the .clang-format file, use:
/// \code{.yaml}
/// IncludeCategories:
/// - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
/// Priority: 2
/// SortPriority: 2
/// CaseSensitive: true
/// - Regex: '^(<|"(gtest|gmock|isl|json)/)'
/// Priority: 3
/// - Regex: '<[[:alnum:].]+>'
Expand Down
14 changes: 7 additions & 7 deletions clang/lib/Format/Format.cpp
Expand Up @@ -916,9 +916,9 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.ForEachMacros.push_back("Q_FOREACH");
LLVMStyle.ForEachMacros.push_back("BOOST_FOREACH");
LLVMStyle.IncludeStyle.IncludeCategories = {
{"^\"(llvm|llvm-c|clang|clang-c)/", 2, 0},
{"^(<|\"(gtest|gmock|isl|json)/)", 3, 0},
{".*", 1, 0}};
{"^\"(llvm|llvm-c|clang|clang-c)/", 2, 0, false},
{"^(<|\"(gtest|gmock|isl|json)/)", 3, 0, false},
{".*", 1, 0, false}};
LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$";
LLVMStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Preserve;
LLVMStyle.IndentCaseLabels = false;
Expand Down Expand Up @@ -1016,10 +1016,10 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) {
GoogleStyle.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes;
GoogleStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true;
GoogleStyle.DerivePointerAlignment = true;
GoogleStyle.IncludeStyle.IncludeCategories = {{"^<ext/.*\\.h>", 2, 0},
{"^<.*\\.h>", 1, 0},
{"^<.*", 2, 0},
{".*", 3, 0}};
GoogleStyle.IncludeStyle.IncludeCategories = {{"^<ext/.*\\.h>", 2, 0, false},
{"^<.*\\.h>", 1, 0, false},
{"^<.*", 2, 0, false},
{".*", 3, 0, false}};
GoogleStyle.IncludeStyle.IncludeIsMainRegex = "([-_](test|unittest))?$";
GoogleStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup;
GoogleStyle.IndentCaseLabels = true;
Expand Down
7 changes: 5 additions & 2 deletions clang/lib/Tooling/Inclusions/HeaderIncludes.cpp
Expand Up @@ -190,8 +190,11 @@ StringRef matchingStem(llvm::StringRef Path) {
IncludeCategoryManager::IncludeCategoryManager(const IncludeStyle &Style,
StringRef FileName)
: Style(Style), FileName(FileName) {
for (const auto &Category : Style.IncludeCategories)
CategoryRegexs.emplace_back(Category.Regex, llvm::Regex::IgnoreCase);
for (const auto &Category : Style.IncludeCategories) {
CategoryRegexs.emplace_back(Category.Regex, Category.RegexIsCaseSensitive
? llvm::Regex::NoFlags
: llvm::Regex::IgnoreCase);
}
IsMainFile = FileName.endswith(".c") || FileName.endswith(".cc") ||
FileName.endswith(".cpp") || FileName.endswith(".c++") ||
FileName.endswith(".cxx") || FileName.endswith(".m") ||
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Tooling/Inclusions/IncludeStyle.cpp
Expand Up @@ -18,6 +18,7 @@ void MappingTraits<IncludeStyle::IncludeCategory>::mapping(
IO.mapOptional("Regex", Category.Regex);
IO.mapOptional("Priority", Category.Priority);
IO.mapOptional("SortPriority", Category.SortPriority);
IO.mapOptional("CaseSensitive", Category.RegexIsCaseSensitive);
}

void ScalarEnumerationTraits<IncludeStyle::IncludeBlocksStyle>::enumeration(
Expand Down
5 changes: 3 additions & 2 deletions clang/unittests/Format/FormatTest.cpp
Expand Up @@ -14526,12 +14526,13 @@ TEST_F(FormatTest, ParsesConfiguration) {

Style.IncludeStyle.IncludeCategories.clear();
std::vector<tooling::IncludeStyle::IncludeCategory> ExpectedCategories = {
{"abc/.*", 2, 0}, {".*", 1, 0}};
{"abc/.*", 2, 0, false}, {".*", 1, 0, true}};
CHECK_PARSE("IncludeCategories:\n"
" - Regex: abc/.*\n"
" Priority: 2\n"
" - Regex: .*\n"
" Priority: 1",
" Priority: 1\n"
" CaseSensitive: true\n",
IncludeStyle.IncludeCategories, ExpectedCategories);
CHECK_PARSE("IncludeIsMainRegex: 'abc$'", IncludeStyle.IncludeIsMainRegex,
"abc$");
Expand Down
82 changes: 67 additions & 15 deletions clang/unittests/Format/SortIncludesTest.cpp
Expand Up @@ -72,19 +72,19 @@ TEST_F(SortIncludesTest, BasicSorting) {
TEST_F(SortIncludesTest, SortedIncludesUsingSortPriorityAttribute) {
FmtStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup;
FmtStyle.IncludeStyle.IncludeCategories = {
{"^<sys/param\\.h>", 1, 0},
{"^<sys/types\\.h>", 1, 1},
{"^<sys.*/", 1, 2},
{"^<uvm/", 2, 3},
{"^<machine/", 3, 4},
{"^<dev/", 4, 5},
{"^<net.*/", 5, 6},
{"^<protocols/", 5, 7},
{"^<(fs|miscfs|msdosfs|nfs|ntfs|ufs)/", 6, 8},
{"^<(x86|amd64|i386|xen)/", 7, 8},
{"<path", 9, 11},
{"^<[^/].*\\.h>", 8, 10},
{"^\".*\\.h\"", 10, 12}};
{"^<sys/param\\.h>", 1, 0, false},
{"^<sys/types\\.h>", 1, 1, false},
{"^<sys.*/", 1, 2, false},
{"^<uvm/", 2, 3, false},
{"^<machine/", 3, 4, false},
{"^<dev/", 4, 5, false},
{"^<net.*/", 5, 6, false},
{"^<protocols/", 5, 7, false},
{"^<(fs|miscfs|msdosfs|nfs|ntfs|ufs)/", 6, 8, false},
{"^<(x86|amd64|i386|xen)/", 7, 8, false},
{"<path", 9, 11, false},
{"^<[^/].*\\.h>", 8, 10, false},
{"^\".*\\.h\"", 10, 12, false}};
EXPECT_EQ("#include <sys/param.h>\n"
"#include <sys/types.h>\n"
"#include <sys/ioctl.h>\n"
Expand Down Expand Up @@ -600,8 +600,59 @@ TEST_F(SortIncludesTest, SupportCaseInsensitiveMatching) {
"a_TEST.cc"));
}

TEST_F(SortIncludesTest, SupportOptionalCaseSensitiveMachting) {
Style.IncludeBlocks = clang::tooling::IncludeStyle::IBS_Regroup;
Style.IncludeCategories = {{"^\"", 1, 0, false},
{"^<.*\\.h>$", 2, 0, false},
{"^<Q[A-Z][^\\.]*>", 3, 0, false},
{"^<Qt[^\\.]*>", 4, 0, false},
{"^<", 5, 0, false}};

StringRef UnsortedCode = "#include <QWidget>\n"
"#include \"qt.h\"\n"
"#include <algorithm>\n"
"#include <windows.h>\n"
"#include <QLabel>\n"
"#include \"qa.h\"\n"
"#include <queue>\n"
"#include <qtwhatever.h>\n"
"#include <QtGlobal>\n";

EXPECT_EQ("#include \"qa.h\"\n"
"#include \"qt.h\"\n"
"\n"
"#include <qtwhatever.h>\n"
"#include <windows.h>\n"
"\n"
"#include <QLabel>\n"
"#include <QWidget>\n"
"#include <QtGlobal>\n"
"#include <queue>\n"
"\n"
"#include <algorithm>\n",
sort(UnsortedCode));

Style.IncludeCategories[2].RegexIsCaseSensitive = true;
Style.IncludeCategories[3].RegexIsCaseSensitive = true;
EXPECT_EQ("#include \"qa.h\"\n"
"#include \"qt.h\"\n"
"\n"
"#include <qtwhatever.h>\n"
"#include <windows.h>\n"
"\n"
"#include <QLabel>\n"
"#include <QWidget>\n"
"\n"
"#include <QtGlobal>\n"
"\n"
"#include <algorithm>\n"
"#include <queue>\n",
sort(UnsortedCode));
}

TEST_F(SortIncludesTest, NegativePriorities) {
Style.IncludeCategories = {{".*important_os_header.*", -1, 0}, {".*", 1, 0}};
Style.IncludeCategories = {{".*important_os_header.*", -1, 0, false},
{".*", 1, 0, false}};
EXPECT_EQ("#include \"important_os_header.h\"\n"
"#include \"c_main.h\"\n"
"#include \"a_other.h\"\n",
Expand All @@ -621,7 +672,8 @@ TEST_F(SortIncludesTest, NegativePriorities) {
}

TEST_F(SortIncludesTest, PriorityGroupsAreSeparatedWhenRegroupping) {
Style.IncludeCategories = {{".*important_os_header.*", -1, 0}, {".*", 1, 0}};
Style.IncludeCategories = {{".*important_os_header.*", -1, 0, false},
{".*", 1, 0, false}};
Style.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup;

EXPECT_EQ("#include \"important_os_header.h\"\n"
Expand Down

0 comments on commit 8668eae

Please sign in to comment.