[clang] [clang-format] Support globstar in .clang-format-ignore (PR #121404)
Owen Pan via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 1 02:05:53 PST 2025
https://github.com/owenca updated https://github.com/llvm/llvm-project/pull/121404
>From 36efaa2849ee6276be5b3c0739f295d0921d1e6e Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Tue, 31 Dec 2024 08:13:53 -0800
Subject: [PATCH 1/3] [clang-format] Support globstar in .clang-format-ignore
Closes #114969.
---
clang/docs/ClangFormat.rst | 1 +
clang/docs/ReleaseNotes.rst | 1 +
clang/lib/Format/MatchFilePath.cpp | 35 ++++++++++++++------
clang/unittests/Format/MatchFilePathTest.cpp | 35 ++++++++++++++++++++
4 files changed, 61 insertions(+), 11 deletions(-)
diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst
index c8f1d7f5a77581..e1f677178c00ab 100644
--- a/clang/docs/ClangFormat.rst
+++ b/clang/docs/ClangFormat.rst
@@ -150,6 +150,7 @@ names. It has the following format:
* Patterns follow the rules specified in `POSIX 2.13.1, 2.13.2, and Rule 1 of
2.13.3 <https://pubs.opengroup.org/onlinepubs/9699919799/utilities/
V3_chap02.html#tag_18_13>`_.
+* Bash globstar (``**``) is supported.
* A pattern is negated if it starts with a bang (``!``).
To match all files in a directory, use e.g. ``foo/bar/*``. To match all files in
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b7da12bcf65818..2d59d74ddf35fc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1124,6 +1124,7 @@ clang-format
- Adds ``RemoveEmptyLinesInUnwrappedLines`` option.
- Adds ``KeepFormFeed`` option and set it to ``true`` for ``GNU`` style.
- Adds ``AllowShortNamespacesOnASingleLine`` option.
+- Adds support for bash globstar in ``.clang-format-ignore``.
libclang
--------
diff --git a/clang/lib/Format/MatchFilePath.cpp b/clang/lib/Format/MatchFilePath.cpp
index 062b334dcdd8fd..aca3433dc7fa78 100644
--- a/clang/lib/Format/MatchFilePath.cpp
+++ b/clang/lib/Format/MatchFilePath.cpp
@@ -49,25 +49,38 @@ bool matchFilePath(StringRef Pattern, StringRef FilePath) {
return false;
break;
case '*': {
- while (++I < EOP && Pattern[I] == '*') { // Skip consecutive stars.
+ const bool MaybeGlobstar = I == 0 || Pattern[I - 1] == Separator;
+ int StarCount = 1;
+ for (; ++I < EOP && Pattern[I] == '*'; ++StarCount) {
+ // Skip consecutive stars.
}
const auto K = FilePath.find(Separator, J); // Index of next `Separator`.
const bool NoMoreSeparatorsInFilePath = K == StringRef::npos;
+ bool Globstar = MaybeGlobstar && StarCount == 2;
if (I == EOP) // `Pattern` ends with a star.
- return NoMoreSeparatorsInFilePath;
- // `Pattern` ends with a lone backslash.
- if (Pattern[I] == '\\' && ++I == EOP)
- return false;
+ return Globstar || NoMoreSeparatorsInFilePath;
+ if (Pattern[I] != Separator) {
+ Globstar = false;
+ // `Pattern` ends with a lone backslash.
+ if (Pattern[I] == '\\' && ++I == EOP)
+ return false;
+ }
// The star is followed by a (possibly escaped) `Separator`.
if (Pattern[I] == Separator) {
- if (NoMoreSeparatorsInFilePath)
- return false;
- J = K; // Skip to next `Separator` in `FilePath`.
- break;
+ if (!Globstar) {
+ if (NoMoreSeparatorsInFilePath)
+ return false;
+ J = K; // Skip to next `Separator` in `FilePath`.
+ break;
+ }
+ if (I + 1 < EOP &&
+ matchFilePath(Pattern.substr(I + 1), FilePath.substr(J))) {
+ return true;
+ }
}
// Recurse.
- for (auto Pat = Pattern.substr(I); J < End && FilePath[J] != Separator;
- ++J) {
+ for (auto Pat = Pattern.substr(I);
+ J < End && (Globstar || FilePath[J] != Separator); ++J) {
if (matchFilePath(Pat, FilePath.substr(J)))
return true;
}
diff --git a/clang/unittests/Format/MatchFilePathTest.cpp b/clang/unittests/Format/MatchFilePathTest.cpp
index 28f665635718e5..9876a4583c226a 100644
--- a/clang/unittests/Format/MatchFilePathTest.cpp
+++ b/clang/unittests/Format/MatchFilePathTest.cpp
@@ -164,6 +164,41 @@ TEST_F(MatchFilePathTest, Path) {
EXPECT_FALSE(match("foo\\", R"(foo*\)"));
}
+TEST_F(MatchFilePathTest, Globstar) {
+ EXPECT_TRUE(match("/", "**"));
+ EXPECT_TRUE(match("foo", "**"));
+ EXPECT_TRUE(match("/foo", "**"));
+ EXPECT_TRUE(match("foo/", "**"));
+ EXPECT_TRUE(match("foo/bar", "**"));
+
+ EXPECT_TRUE(match("/", "**/"));
+ EXPECT_TRUE(match("foo/", "**/"));
+ EXPECT_TRUE(match("/foo/", "**/"));
+ EXPECT_TRUE(match("foo/bar/", "**/"));
+
+ EXPECT_TRUE(match("/", "/**"));
+ EXPECT_TRUE(match("/foo", "/**"));
+ EXPECT_TRUE(match("/foo/", "/**"));
+ EXPECT_TRUE(match("/foo/bar", "/**"));
+
+ EXPECT_TRUE(match("foo", "**/foo"));
+ EXPECT_TRUE(match("/foo", "**/foo"));
+ EXPECT_TRUE(match("foo/bar", "**/bar"));
+ EXPECT_TRUE(match("/foo/bar", "**/foo/bar"));
+ EXPECT_TRUE(match("foo/bar/baz", "**/bar/baz"));
+
+ EXPECT_TRUE(match("abc/foo", "abc/**"));
+ EXPECT_TRUE(match("abc/foo/", "abc/**"));
+ EXPECT_TRUE(match("abc/foo/bar", "abc/**"));
+
+ EXPECT_TRUE(match("a/b", "a/**/b"));
+ EXPECT_TRUE(match("a/x/b", "a/**/b"));
+ EXPECT_TRUE(match("a/x/y/b", "a/**/b"));
+
+ EXPECT_FALSE(match("a/x/b", "a**/b"));
+ EXPECT_FALSE(match("a/x/y/b", "a/**b"));
+}
+
} // namespace
} // namespace format
} // namespace clang
>From 1ffe78c89414d2328b2cc80a5216a7e4d20bbcc8 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Tue, 31 Dec 2024 19:44:10 -0800
Subject: [PATCH 2/3] NFC: get rid of `MaybeGlobstar`.
---
clang/lib/Format/MatchFilePath.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Format/MatchFilePath.cpp b/clang/lib/Format/MatchFilePath.cpp
index aca3433dc7fa78..e81d0ba1ea762b 100644
--- a/clang/lib/Format/MatchFilePath.cpp
+++ b/clang/lib/Format/MatchFilePath.cpp
@@ -49,14 +49,15 @@ bool matchFilePath(StringRef Pattern, StringRef FilePath) {
return false;
break;
case '*': {
- const bool MaybeGlobstar = I == 0 || Pattern[I - 1] == Separator;
+ bool Globstar = I == 0 || Pattern[I - 1] == Separator;
int StarCount = 1;
for (; ++I < EOP && Pattern[I] == '*'; ++StarCount) {
// Skip consecutive stars.
}
+ if (StarCount != 2)
+ Globstar = false;
const auto K = FilePath.find(Separator, J); // Index of next `Separator`.
const bool NoMoreSeparatorsInFilePath = K == StringRef::npos;
- bool Globstar = MaybeGlobstar && StarCount == 2;
if (I == EOP) // `Pattern` ends with a star.
return Globstar || NoMoreSeparatorsInFilePath;
if (Pattern[I] != Separator) {
>From d1ca62864396a62880c35d427bcb8a67b258c0a0 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Wed, 1 Jan 2025 02:05:45 -0800
Subject: [PATCH 3/3] NFC: make a test case less confusing.
---
clang/unittests/Format/MatchFilePathTest.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/unittests/Format/MatchFilePathTest.cpp b/clang/unittests/Format/MatchFilePathTest.cpp
index 9876a4583c226a..346ea7c31e6157 100644
--- a/clang/unittests/Format/MatchFilePathTest.cpp
+++ b/clang/unittests/Format/MatchFilePathTest.cpp
@@ -196,7 +196,7 @@ TEST_F(MatchFilePathTest, Globstar) {
EXPECT_TRUE(match("a/x/y/b", "a/**/b"));
EXPECT_FALSE(match("a/x/b", "a**/b"));
- EXPECT_FALSE(match("a/x/y/b", "a/**b"));
+ EXPECT_FALSE(match("a/x/b", "a/**b"));
}
} // namespace
More information about the cfe-commits
mailing list