[clang] [clang-format] Honor later negated .clang-format-ignore patterns (PR #195432)
via cfe-commits
cfe-commits at lists.llvm.org
Sat May 2 01:33:35 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-format
Author: Dudenwatschn
<details>
<summary>Changes</summary>
This addresses (#<!-- -->178344).
In this issue the negation (`!`) for paths inside .clang-format-ignore doesn't behave as intended, as clang-format stops processing patterns on the first match, rather than processing further rules/patterns.
Rather than
```
foo/*
!foo/*.h
```
un-ignoring `.h`-files in `foo/` - the header-files remain ignored, since the ignore-condition exits early and considers the files ignored when checking `foo/*`.
I've tried to make negation work with the old behaviour (like mentioned in #<!-- -->178344), but could find no sensible way to use it.
>From the draft/proposal of .clang-format-ignore (#<!-- -->52975) it looks like the intent of the negation pattern was to behave similar to .gitignore - where pattern matching continues after the first match is found.
With this change **patterns will continue to be evaluated after the first match**, closer resembling the behaviour of .gitignore.
I have also added some simple unit-tests for the negation-pattern, as there weren't any beforehand.
---
Full diff: https://github.com/llvm/llvm-project/pull/195432.diff
3 Files Affected:
- (modified) clang/test/Format/clang-format-ignore.cpp (+20)
- (modified) clang/test/Format/list-ignored.cpp (+11)
- (modified) clang/tools/clang-format/ClangFormat.cpp (+4-3)
``````````diff
diff --git a/clang/test/Format/clang-format-ignore.cpp b/clang/test/Format/clang-format-ignore.cpp
index 67d68aebde5d0..efed100886aa3 100644
--- a/clang/test/Format/clang-format-ignore.cpp
+++ b/clang/test/Format/clang-format-ignore.cpp
@@ -56,5 +56,25 @@
// RUN: | FileCheck %s -check-prefix=CHECK7 -match-full-lines
// CHECK7: int i;
+// RUN: echo "foo.h" > .clang-format-ignore
+// RUN: echo "!foo.c" >> .clang-format-ignore
+// RUN: echo "int i ;" > foo.c
+// RUN: echo "int i ;" > foo.h
+// RUN: clang-format -verbose foo.c 2>&1 \
+// RUN: | FileCheck %s -check-prefix=CHECK8 -match-full-lines
+// CHECK8: Formatting [1/1] foo.c
+// RUN: clang-format -verbose foo.h 2>&1 \
+// RUN: | FileCheck %s -check-prefix=CHECK9 -allow-empty
+// CHECK9-NOT: int
+
+// RUN: mkdir -p %t.dir/ignore.dir/format.dir
+// RUN: echo "ignore.dir/*" > .clang-format-ignore
+// RUN: echo "!ignore.dir/format.dir/*" >> .clang-format-ignore
+// RUN: echo "int i ;" > ignore.dir/foo.c
+// RUN: echo "int i ;" > ignore.dir/format.dir/bar.c
+// RUN: clang-format -verbose ignore.dir/**/*.c 2>&1 \
+// RUN: | FileCheck %s -check-prefix=CHECK10 -match-full-lines
+// CHECK10: {{Formatting \[1/1] .*ignore\.dir/format\.dir/bar\.c}}
+
// RUN: cd ..
// RUN: rm -r %t.dir
diff --git a/clang/test/Format/list-ignored.cpp b/clang/test/Format/list-ignored.cpp
index 6e65a68a6f996..d5cc449c837f6 100644
--- a/clang/test/Format/list-ignored.cpp
+++ b/clang/test/Format/list-ignored.cpp
@@ -56,5 +56,16 @@
// CHECK7-NOT: foo.c
// CHECK7: foo.js
+// RUN: echo "!foo.c" > .clang-format-ignore
+// RUN: clang-format -list-ignored foo.c foo.h 2>&1 \
+// RUN: | FileCheck %s -allow-empty -check-prefix=CHECK8
+// CHECK8-NOT: foo.c
+
+// RUN: echo "*" > .clang-format-ignore
+// RUN: echo "!foo.*" >> .clang-format-ignore
+// RUN: clang-format -list-ignored foo.c 2>&1 \
+// RUN: | FileCheck %s -allow-empty -check-prefix=CHECK9
+// CHECK9-NOT: foo.c
+
// RUN: cd ..
// RUN: rm -r %t.dir
diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp
index 37d0eb83414f4..1a38ef195cbac 100644
--- a/clang/tools/clang-format/ClangFormat.cpp
+++ b/clang/tools/clang-format/ClangFormat.cpp
@@ -636,6 +636,7 @@ static bool isIgnored(StringRef FilePath) {
if (IgnoreDir.empty())
return false;
+ bool IsIgnored = false;
const auto Pathname{convert_to_slash(AbsPath)};
for (const auto &Pat : Patterns) {
const bool IsNegated = Pat[0] == '!';
@@ -657,11 +658,11 @@ static bool isIgnored(StringRef FilePath) {
Pattern = Path;
}
- if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)
- return true;
+ if (clang::format::matchFilePath(Pattern, Pathname))
+ IsIgnored = !IsNegated;
}
- return false;
+ return IsIgnored;
}
int main(int argc, const char **argv) {
``````````
</details>
https://github.com/llvm/llvm-project/pull/195432
More information about the cfe-commits
mailing list