Skip to content

Commit 36efaa2

Browse files
committed
[clang-format] Support globstar in .clang-format-ignore
Closes #114969.
1 parent 486ec4b commit 36efaa2

File tree

4 files changed

+61
-11
lines changed

4 files changed

+61
-11
lines changed

clang/docs/ClangFormat.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ names. It has the following format:
150150
* Patterns follow the rules specified in `POSIX 2.13.1, 2.13.2, and Rule 1 of
151151
2.13.3 <https://pubs.opengroup.org/onlinepubs/9699919799/utilities/
152152
V3_chap02.html#tag_18_13>`_.
153+
* Bash globstar (``**``) is supported.
153154
* A pattern is negated if it starts with a bang (``!``).
154155

155156
To match all files in a directory, use e.g. ``foo/bar/*``. To match all files in

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,7 @@ clang-format
11241124
- Adds ``RemoveEmptyLinesInUnwrappedLines`` option.
11251125
- Adds ``KeepFormFeed`` option and set it to ``true`` for ``GNU`` style.
11261126
- Adds ``AllowShortNamespacesOnASingleLine`` option.
1127+
- Adds support for bash globstar in ``.clang-format-ignore``.
11271128

11281129
libclang
11291130
--------

clang/lib/Format/MatchFilePath.cpp

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,25 +49,38 @@ bool matchFilePath(StringRef Pattern, StringRef FilePath) {
4949
return false;
5050
break;
5151
case '*': {
52-
while (++I < EOP && Pattern[I] == '*') { // Skip consecutive stars.
52+
const bool MaybeGlobstar = I == 0 || Pattern[I - 1] == Separator;
53+
int StarCount = 1;
54+
for (; ++I < EOP && Pattern[I] == '*'; ++StarCount) {
55+
// Skip consecutive stars.
5356
}
5457
const auto K = FilePath.find(Separator, J); // Index of next `Separator`.
5558
const bool NoMoreSeparatorsInFilePath = K == StringRef::npos;
59+
bool Globstar = MaybeGlobstar && StarCount == 2;
5660
if (I == EOP) // `Pattern` ends with a star.
57-
return NoMoreSeparatorsInFilePath;
58-
// `Pattern` ends with a lone backslash.
59-
if (Pattern[I] == '\\' && ++I == EOP)
60-
return false;
61+
return Globstar || NoMoreSeparatorsInFilePath;
62+
if (Pattern[I] != Separator) {
63+
Globstar = false;
64+
// `Pattern` ends with a lone backslash.
65+
if (Pattern[I] == '\\' && ++I == EOP)
66+
return false;
67+
}
6168
// The star is followed by a (possibly escaped) `Separator`.
6269
if (Pattern[I] == Separator) {
63-
if (NoMoreSeparatorsInFilePath)
64-
return false;
65-
J = K; // Skip to next `Separator` in `FilePath`.
66-
break;
70+
if (!Globstar) {
71+
if (NoMoreSeparatorsInFilePath)
72+
return false;
73+
J = K; // Skip to next `Separator` in `FilePath`.
74+
break;
75+
}
76+
if (I + 1 < EOP &&
77+
matchFilePath(Pattern.substr(I + 1), FilePath.substr(J))) {
78+
return true;
79+
}
6780
}
6881
// Recurse.
69-
for (auto Pat = Pattern.substr(I); J < End && FilePath[J] != Separator;
70-
++J) {
82+
for (auto Pat = Pattern.substr(I);
83+
J < End && (Globstar || FilePath[J] != Separator); ++J) {
7184
if (matchFilePath(Pat, FilePath.substr(J)))
7285
return true;
7386
}

clang/unittests/Format/MatchFilePathTest.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,41 @@ TEST_F(MatchFilePathTest, Path) {
164164
EXPECT_FALSE(match("foo\\", R"(foo*\)"));
165165
}
166166

167+
TEST_F(MatchFilePathTest, Globstar) {
168+
EXPECT_TRUE(match("/", "**"));
169+
EXPECT_TRUE(match("foo", "**"));
170+
EXPECT_TRUE(match("/foo", "**"));
171+
EXPECT_TRUE(match("foo/", "**"));
172+
EXPECT_TRUE(match("foo/bar", "**"));
173+
174+
EXPECT_TRUE(match("/", "**/"));
175+
EXPECT_TRUE(match("foo/", "**/"));
176+
EXPECT_TRUE(match("/foo/", "**/"));
177+
EXPECT_TRUE(match("foo/bar/", "**/"));
178+
179+
EXPECT_TRUE(match("/", "/**"));
180+
EXPECT_TRUE(match("/foo", "/**"));
181+
EXPECT_TRUE(match("/foo/", "/**"));
182+
EXPECT_TRUE(match("/foo/bar", "/**"));
183+
184+
EXPECT_TRUE(match("foo", "**/foo"));
185+
EXPECT_TRUE(match("/foo", "**/foo"));
186+
EXPECT_TRUE(match("foo/bar", "**/bar"));
187+
EXPECT_TRUE(match("/foo/bar", "**/foo/bar"));
188+
EXPECT_TRUE(match("foo/bar/baz", "**/bar/baz"));
189+
190+
EXPECT_TRUE(match("abc/foo", "abc/**"));
191+
EXPECT_TRUE(match("abc/foo/", "abc/**"));
192+
EXPECT_TRUE(match("abc/foo/bar", "abc/**"));
193+
194+
EXPECT_TRUE(match("a/b", "a/**/b"));
195+
EXPECT_TRUE(match("a/x/b", "a/**/b"));
196+
EXPECT_TRUE(match("a/x/y/b", "a/**/b"));
197+
198+
EXPECT_FALSE(match("a/x/b", "a**/b"));
199+
EXPECT_FALSE(match("a/x/y/b", "a/**b"));
200+
}
201+
167202
} // namespace
168203
} // namespace format
169204
} // namespace clang

0 commit comments

Comments
 (0)