Author: Owen Pan Date: 2025-01-01T15:37:59-08:00 New Revision: cd239493c1023cbccfe6b1e9be32e68592a7f304
URL: https://github.com/llvm/llvm-project/commit/cd239493c1023cbccfe6b1e9be32e68592a7f304 DIFF: https://github.com/llvm/llvm-project/commit/cd239493c1023cbccfe6b1e9be32e68592a7f304.diff LOG: [clang-format] Support globstar in .clang-format-ignore (#121404) Closes #110160. Closes #114969. Added: Modified: clang/docs/ClangFormat.rst clang/docs/ReleaseNotes.rst clang/lib/Format/MatchFilePath.cpp clang/unittests/Format/MatchFilePathTest.cpp Removed: ################################################################################ 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 2a688a677294f8..662c575bad3e8b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1125,6 +1125,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..3d8614838e5356 100644 --- a/clang/lib/Format/MatchFilePath.cpp +++ b/clang/lib/Format/MatchFilePath.cpp @@ -25,9 +25,11 @@ bool matchFilePath(StringRef Pattern, StringRef FilePath) { assert(!Pattern.empty()); assert(!FilePath.empty()); + const auto FilePathBack = FilePath.back(); + // No match if `Pattern` ends with a non-meta character not equal to the last // character of `FilePath`. - if (const auto C = Pattern.back(); !strchr("?*]", C) && C != FilePath.back()) + if (const auto C = Pattern.back(); !strchr("?*]", C) && C != FilePathBack) return false; constexpr auto Separator = '/'; @@ -49,25 +51,37 @@ bool matchFilePath(StringRef Pattern, StringRef FilePath) { return false; break; case '*': { - while (++I < EOP && Pattern[I] == '*') { // Skip consecutive stars. + 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; 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 == EOP) + return FilePathBack == Separator; } // 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..346ea7c31e6157 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/b", "a/**b")); +} + } // namespace } // namespace format } // namespace clang _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits