-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang-format] Support globstar in .clang-format-ignore #121404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-format Author: Owen Pan (owenca) ChangesCloses #114969. Full diff: https://github.com/llvm/llvm-project/pull/121404.diff 4 Files Affected:
diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst
index c8f1d7f5a77581..21cf68236b8917 100644
--- a/clang/docs/ClangFormat.rst
+++ b/clang/docs/ClangFormat.rst
@@ -150,6 +150,10 @@ 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, i.e. "two adjacent ``*``s used as a single pattern
+ will match all files and zero or more directories and subdirectories. If
+ followed by a ``/``, two adjacent ``*``s will match only directories and
+ subdirectories."
* 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
|
@llvm/pr-subscribers-clang Author: Owen Pan (owenca) ChangesCloses #114969. Full diff: https://github.com/llvm/llvm-project/pull/121404.diff 4 Files Affected:
diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst
index c8f1d7f5a77581..21cf68236b8917 100644
--- a/clang/docs/ClangFormat.rst
+++ b/clang/docs/ClangFormat.rst
@@ -150,6 +150,10 @@ 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, i.e. "two adjacent ``*``s used as a single pattern
+ will match all files and zero or more directories and subdirectories. If
+ followed by a ``/``, two adjacent ``*``s will match only directories and
+ subdirectories."
* 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
|
leijurv
reviewed
Jan 1, 2025
owenca
commented
Jan 1, 2025
mydeveloperday
approved these changes
Jan 1, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Closes #110160.
Closes #114969.