[llvm-branch-commits] [clang] release/19.x: [clang-format] Fix a serious bug in `git clang-format -f` (#102629) (PR #102770)
Tobias Hieta via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Aug 13 00:00:53 PDT 2024
https://github.com/tru updated https://github.com/llvm/llvm-project/pull/102770
>From efdd0e9fda292f9040367fcd6a9b2c2590fea739 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Sat, 10 Aug 2024 13:31:35 -0700
Subject: [PATCH] [clang-format] Fix a serious bug in `git clang-format -f`
(#102629)
With the --force (or -f) option, git-clang-format wipes out input files
excluded by a .clang-format-ignore file if they have unstaged changes.
This patch adds a hidden clang-format option --list-ignored that lists
such excluded files for git-clang-format to filter out.
Fixes #102459.
(cherry picked from commit 986bc3d0719af653fecb77e8cfc59f39bec148fd)
---
clang/test/Format/list-ignored.cpp | 60 +++++++++++++++++++++++
clang/tools/clang-format/ClangFormat.cpp | 12 ++++-
clang/tools/clang-format/git-clang-format | 15 +++++-
3 files changed, 84 insertions(+), 3 deletions(-)
create mode 100644 clang/test/Format/list-ignored.cpp
diff --git a/clang/test/Format/list-ignored.cpp b/clang/test/Format/list-ignored.cpp
new file mode 100644
index 00000000000000..6e65a68a6f9968
--- /dev/null
+++ b/clang/test/Format/list-ignored.cpp
@@ -0,0 +1,60 @@
+// RUN: rm -rf %t.dir
+// RUN: mkdir -p %t.dir/level1/level2
+
+// RUN: cd %t.dir
+// RUN: echo "*" > .clang-format-ignore
+// RUN: echo "level*/*.c*" >> .clang-format-ignore
+// RUN: echo "*/*2/foo.*" >> .clang-format-ignore
+
+// RUN: touch foo.cc
+// RUN: clang-format -list-ignored .clang-format-ignore foo.cc \
+// RUN: | FileCheck %s
+// CHECK: .clang-format-ignore
+// CHECK-NEXT: foo.cc
+
+// RUN: cd level1
+// RUN: touch bar.cc baz.c
+// RUN: clang-format -list-ignored bar.cc baz.c \
+// RUN: | FileCheck %s -check-prefix=CHECK2
+// CHECK2: bar.cc
+// CHECK2-NEXT: baz.c
+
+// RUN: cd level2
+// RUN: touch foo.c foo.js
+// RUN: clang-format -list-ignored foo.c foo.js \
+// RUN: | FileCheck %s -check-prefix=CHECK3
+// CHECK3: foo.c
+// CHECK3-NEXT: foo.js
+
+// RUN: touch .clang-format-ignore
+// RUN: clang-format -list-ignored foo.c foo.js \
+// RUN: | FileCheck %s -allow-empty -check-prefix=CHECK4
+// CHECK4-NOT: foo.c
+// CHECK4-NOT: foo.js
+
+// RUN: echo "*.js" > .clang-format-ignore
+// RUN: clang-format -list-ignored foo.c foo.js \
+// RUN: | FileCheck %s -check-prefix=CHECK5
+// CHECK5-NOT: foo.c
+// CHECK5: foo.js
+
+// RUN: cd ../..
+// RUN: clang-format -list-ignored *.cc level1/*.c* level1/level2/foo.* \
+// RUN: | FileCheck %s -check-prefix=CHECK6
+// CHECK6: foo.cc
+// CHECK6-NEXT: bar.cc
+// CHECK6-NEXT: baz.c
+// CHECK6-NOT: foo.c
+// CHECK6-NEXT: foo.js
+
+// RUN: rm .clang-format-ignore
+// RUN: clang-format -list-ignored *.cc level1/*.c* level1/level2/foo.* \
+// RUN: | FileCheck %s -check-prefix=CHECK7
+// CHECK7-NOT: foo.cc
+// CHECK7-NOT: bar.cc
+// CHECK7-NOT: baz.c
+// CHECK7-NOT: foo.c
+// CHECK7: foo.js
+
+// RUN: cd ..
+// RUN: rm -r %t.dir
diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp
index 6cba1267f3b0db..c4b6209a71a88f 100644
--- a/clang/tools/clang-format/ClangFormat.cpp
+++ b/clang/tools/clang-format/ClangFormat.cpp
@@ -210,6 +210,10 @@ static cl::opt<bool> FailOnIncompleteFormat(
cl::desc("If set, fail with exit code 1 on incomplete format."),
cl::init(false), cl::cat(ClangFormatCategory));
+static cl::opt<bool> ListIgnored("list-ignored",
+ cl::desc("List ignored files."),
+ cl::cat(ClangFormatCategory), cl::Hidden);
+
namespace clang {
namespace format {
@@ -715,7 +719,13 @@ int main(int argc, const char **argv) {
unsigned FileNo = 1;
bool Error = false;
for (const auto &FileName : FileNames) {
- if (isIgnored(FileName))
+ const bool Ignored = isIgnored(FileName);
+ if (ListIgnored) {
+ if (Ignored)
+ outs() << FileName << '\n';
+ continue;
+ }
+ if (Ignored)
continue;
if (Verbose) {
errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
diff --git a/clang/tools/clang-format/git-clang-format b/clang/tools/clang-format/git-clang-format
index d33fd478d77fd9..714ba8a6e77d51 100755
--- a/clang/tools/clang-format/git-clang-format
+++ b/clang/tools/clang-format/git-clang-format
@@ -173,11 +173,12 @@ def main():
# those files.
cd_to_toplevel()
filter_symlinks(changed_lines)
+ filter_ignored_files(changed_lines, binary=opts.binary)
if opts.verbose >= 1:
ignored_files.difference_update(changed_lines)
if ignored_files:
- print(
- 'Ignoring changes in the following files (wrong extension or symlink):')
+ print('Ignoring the following files (wrong extension, symlink, or '
+ 'ignored by clang-format):')
for filename in ignored_files:
print(' %s' % filename)
if changed_lines:
@@ -399,6 +400,16 @@ def filter_symlinks(dictionary):
del dictionary[filename]
+def filter_ignored_files(dictionary, binary):
+ """Delete every key in `dictionary` that is ignored by clang-format."""
+ ignored_files = run(binary, '-list-ignored', *dictionary.keys())
+ if not ignored_files:
+ return
+ ignored_files = ignored_files.split('\n')
+ for filename in ignored_files:
+ del dictionary[filename]
+
+
def cd_to_toplevel():
"""Change to the top level of the git repository."""
toplevel = run('git', 'rev-parse', '--show-toplevel')
More information about the llvm-branch-commits
mailing list