[clang] [clang-format] clang-format-ignore: Add support for double asterisk patterns (PR #110560)

Ameer J via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 30 12:50:17 PDT 2024


https://github.com/ameerj created https://github.com/llvm/llvm-project/pull/110560

Adds `**` support for `.clang-format-ignore` to support similar pattern matching to [.gitignore](https://mirrors.edge.kernel.org/pub/software/scm/git/docs/gitignore.html#_pattern_format)

closes #110160

>From bf284fe6a25f7947d73b9df12221cefb5363db64 Mon Sep 17 00:00:00 2001
From: ameerj <ameerj99 at outlook.com>
Date: Mon, 30 Sep 2024 15:45:40 -0400
Subject: [PATCH] MatchFilePath: Add support for double asterisk patterns

---
 clang/lib/Format/MatchFilePath.cpp           | 22 +++++++++++--
 clang/unittests/Format/MatchFilePathTest.cpp | 34 ++++++++++++++++++++
 2 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Format/MatchFilePath.cpp b/clang/lib/Format/MatchFilePath.cpp
index 062b334dcdd8fd..3d7861499b9a46 100644
--- a/clang/lib/Format/MatchFilePath.cpp
+++ b/clang/lib/Format/MatchFilePath.cpp
@@ -49,11 +49,29 @@ bool matchFilePath(StringRef Pattern, StringRef FilePath) {
         return false;
       break;
     case '*': {
-      while (++I < EOP && Pattern[I] == '*') { // Skip consecutive stars.
+      if (I + 1 < EOP && Pattern[I + 1] == '*') {
+        // Handle '**' pattern
+        while (++I < EOP && Pattern[I] == '*') { // Skip consecutive stars.
+        }
+        if (I == EOP)
+          return true; // '**' at the end matches everything
+        if (Pattern[I] == Separator) {
+          // Try to match the rest of the pattern without consuming the
+          // separator for the case where we want to match "zero" directories
+          // e.g. "a/**/b" matches "a/b"
+          if (matchFilePath(Pattern.substr(I + 1), FilePath.substr(J)))
+            return true;
+        }
+        while (J < End) {
+          if (matchFilePath(Pattern.substr(I), FilePath.substr(J)))
+            return true;
+          ++J;
+        }
+        return 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.
+      if (++I == EOP) // `Pattern` ends with a star.
         return NoMoreSeparatorsInFilePath;
       // `Pattern` ends with a lone backslash.
       if (Pattern[I] == '\\' && ++I == EOP)
diff --git a/clang/unittests/Format/MatchFilePathTest.cpp b/clang/unittests/Format/MatchFilePathTest.cpp
index 28f665635718e5..a6df090a802128 100644
--- a/clang/unittests/Format/MatchFilePathTest.cpp
+++ b/clang/unittests/Format/MatchFilePathTest.cpp
@@ -164,6 +164,40 @@ TEST_F(MatchFilePathTest, Path) {
   EXPECT_FALSE(match("foo\\", R"(foo*\)"));
 }
 
+TEST_F(MatchFilePathTest, DoubleAsterisk) {
+  EXPECT_TRUE(match("a/b/c/d.cpp", "**b**"));
+  EXPECT_TRUE(match("a/b/c/d.cpp", "**/b/**"));
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "**d_*"));
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "**/d_*"));
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "**d_**"));
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "**/d_**"));
+
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "**/b/c/**"));
+
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "a/**/b/c/d_e.cpp"));
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "a/**/c/d_e.cpp"));
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "a/**/b/**/d_e.cpp"));
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "**/b/**/d_e.cpp"));
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "a/**/**/b/**"));
+
+  EXPECT_FALSE(match("a/b/c/d_e.cpp", "**/d"));
+  EXPECT_FALSE(match("a/b/c/d_e.cpp", "**/b/d"));
+  EXPECT_FALSE(match("a/b/c/d_e.cpp", "**/b/d/**"));
+  EXPECT_FALSE(match("a/b/c/d_e.cpp", "**/b/c/"));
+
+  // Multiple consecutive asterisks are treated as **
+  EXPECT_TRUE(match("a/b/c/d.cpp", "***b****"));
+  EXPECT_TRUE(match("a/b/c/d.cpp", "****/b/***"));
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "***d_**"));
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "****/d_*"));
+  EXPECT_TRUE(match("a/b/c/d_e.cpp", "***/b/c/*****"));
+
+  EXPECT_FALSE(match("a/b/c/d_e.cpp", "*****/d"));
+  EXPECT_FALSE(match("a/b/c/d_e.cpp", "***/b/d"));
+  EXPECT_FALSE(match("a/b/c/d_e.cpp", "*****/b/d/***"));
+  EXPECT_FALSE(match("a/b/c/d_e.cpp", "***/b/c"));
+}
+
 } // namespace
 } // namespace format
 } // namespace clang



More information about the cfe-commits mailing list