https://github.com/owenca updated https://github.com/llvm/llvm-project/pull/76327
>From 4afd12db61528b40d842a7fbee9af37c2235822c Mon Sep 17 00:00:00 2001 From: Owen Pan <owenpi...@gmail.com> Date: Sun, 24 Dec 2023 01:18:55 -0800 Subject: [PATCH 1/6] [clang-format] Add .clang-format.ignore for ignoring files Closes #52975. --- clang/docs/ClangFormat.rst | 18 ++++++ clang/test/Format/clang-format-ignore.cpp | 24 ++++++++ clang/tools/clang-format/ClangFormat.cpp | 71 ++++++++++++++++++++++- 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 clang/test/Format/clang-format-ignore.cpp diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst index f52f35550d03eb..a0b28f2273991f 100644 --- a/clang/docs/ClangFormat.rst +++ b/clang/docs/ClangFormat.rst @@ -131,6 +131,24 @@ An easy way to create the ``.clang-format`` file is: Available style options are described in :doc:`ClangFormatStyleOptions`. +You can create ``.clang-format-ignore`` files to make ``clang-format`` ignore +certain files. A ``.clang-format-ignore`` file consists of patterns of file path +names. It has the following format: +- A blank line is skipped. +- Leading and trailing spaces of a line are trimmed. +- A line starting with a hash (``#``) is a comment. +- A non-comment line is a single pattern. +- The slash (``/``) is used as the directory separator. +- A pattern is relative to the directory of the ``.clang-format-ignore`` file + (or the root directory if the pattern starts with a slash). +- Patterns follow the rules specified in POSIX 2.13.1, 2.13.2, and Rule 1 of + 2.13.3. +- 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 +the directory of the ``.clang-format-ignore`` file, use ``*``. +Multiple ``.clang-format-ignore`` files are supported similar to the +``.clang-format`` files, with a lower directory level file voiding the higher +level ones. Vim Integration =============== diff --git a/clang/test/Format/clang-format-ignore.cpp b/clang/test/Format/clang-format-ignore.cpp new file mode 100644 index 00000000000000..a2210266034d4c --- /dev/null +++ b/clang/test/Format/clang-format-ignore.cpp @@ -0,0 +1,24 @@ +// RUN: mkdir -p %t.dir/level1/level2 + +// RUN: cd %t.dir +// RUN: printf "*\nlevel*/*.c*\n*/*2/foo.*\n" > .clang-format-ignore +// RUN: touch foo.cc +// RUN: clang-format -verbose .clang-format-ignore foo.cc 2> %t.stderr +// RUN: not grep "Formatting" %t.stderr + +// RUN: cd level1 +// RUN: touch bar.cc baz.c +// RUN: clang-format -verbose bar.cc baz.c 2> %t.stderr +// RUN: not grep "Formatting" %t.stderr + +// RUN: cd level2 +// RUN: touch foo.c foo.js +// RUN: clang-format -verbose foo.c foo.js 2> %t.stderr +// RUN: not grep "Formatting" %t.stderr +// RUN: printf "*.js\n" > .clang-format-ignore +// RUN: clang-format -verbose foo.c foo.js 2> %t.stderr +// RUN: grep -E "Formatting (.*)foo.c(.*)" %t.stderr +// RUN: not grep -E "Formatting (.*)foo.js(.*)" %t.stderr + +// RUN: cd ../../.. +// RUN: rm -rf %t.dir diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp index d2e3d8d43aef21..be78f8cbebf5e1 100644 --- a/clang/tools/clang-format/ClangFormat.cpp +++ b/clang/tools/clang-format/ClangFormat.cpp @@ -12,6 +12,7 @@ /// //===----------------------------------------------------------------------===// +#include "../../lib/Format/MatchFilePath.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" @@ -570,6 +571,71 @@ static int dumpConfig(bool IsSTDIN) { return 0; } +// Check whether `FilePath` is ignored according to the nearest +// .clang-format-ignore file based on the rules below: +// - A blank line is skipped. +// - Leading and trailing spaces of a line are trimmed. +// - A line starting with a hash (`#`) is a comment. +// - A non-comment line is a single pattern. +// - The slash (`/`) is used as the directory separator. +// - A pattern is relative to the directory of the .clang-format-ignore file (or +// the root directory if the pattern starts with a slash). +// - A pattern is negated if it starts with a bang (`!`). +static bool isIgnored(const StringRef FilePath) { + if (!llvm::sys::fs::is_regular_file(FilePath)) + return false; + + using namespace llvm::sys::path; + SmallString<128> Path, AbsPath{convert_to_slash(FilePath)}; + + llvm::vfs::getRealFileSystem()->makeAbsolute(AbsPath); + remove_dots(AbsPath, /*remove_dot_dot=*/true); + + StringRef IgnoreDir{AbsPath}; + do { + IgnoreDir = parent_path(IgnoreDir); + if (IgnoreDir.empty()) + return false; + + Path = IgnoreDir; + append(Path, ".clang-format-ignore"); + } while (!llvm::sys::fs::is_regular_file(Path)); + + std::ifstream IgnoreFile{Path.c_str()}; + if (!IgnoreFile.good()) + return false; + + bool HasMatch = false; + for (std::string Pattern; std::getline(IgnoreFile, Pattern);) { + Pattern = StringRef(Pattern).trim(); + if (Pattern.empty() || Pattern[0] == '#') + continue; + + const bool IsNegated = Pattern[0] == '!'; + if (IsNegated) + Pattern.erase(0, 1); + + if (Pattern.empty()) + continue; + + Pattern = StringRef(Pattern).ltrim(); + if (Pattern[0] != '/') { + Path = IgnoreDir; + append(Path, Pattern); + remove_dots(Path, /*remove_dot_dot=*/true); + Pattern = Path.str(); + } + + if (clang::format::matchFilePath(Pattern, AbsPath.str()) == !IsNegated) { + HasMatch = true; + break; + } + } + + IgnoreFile.close(); + return HasMatch; +} + int main(int argc, const char **argv) { llvm::InitLLVM X(argc, argv); @@ -618,11 +684,14 @@ int main(int argc, const char **argv) { unsigned FileNo = 1; bool Error = false; for (const auto &FileName : FileNames) { + const bool IsSTDIN = FileName == "-"; + if (!IsSTDIN && isIgnored(FileName)) + continue; if (Verbose) { errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] " << FileName << "\n"; } - Error |= clang::format::format(FileName, FileName == "-"); + Error |= clang::format::format(FileName, IsSTDIN); } return Error ? 1 : 0; } >From 0abdba2a457be700921e903f66b5a3d7ca54c55f Mon Sep 17 00:00:00 2001 From: Owen Pan <owenpi...@gmail.com> Date: Sun, 24 Dec 2023 01:41:22 -0800 Subject: [PATCH 2/6] Update clang/docs/ClangFormat.rst --- clang/docs/ClangFormat.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst index a0b28f2273991f..822839c6842521 100644 --- a/clang/docs/ClangFormat.rst +++ b/clang/docs/ClangFormat.rst @@ -140,9 +140,9 @@ names. It has the following format: - A non-comment line is a single pattern. - The slash (``/``) is used as the directory separator. - A pattern is relative to the directory of the ``.clang-format-ignore`` file - (or the root directory if the pattern starts with a slash). +(or the root directory if the pattern starts with a slash). - Patterns follow the rules specified in POSIX 2.13.1, 2.13.2, and Rule 1 of - 2.13.3. +2.13.3. - 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 the directory of the ``.clang-format-ignore`` file, use ``*``. >From fbf142f563a72ecc9b561296d14c96f2558f80ea Mon Sep 17 00:00:00 2001 From: Owen Pan <owenpi...@gmail.com> Date: Tue, 26 Dec 2023 00:48:01 -0800 Subject: [PATCH 3/6] Fix bugs for Windows. --- clang/docs/ClangFormat.rst | 1 + clang/test/Format/clang-format-ignore.cpp | 21 ++++++++++++++------- clang/tools/clang-format/ClangFormat.cpp | 15 +++++++++------ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst index 822839c6842521..67fdffbd116d8a 100644 --- a/clang/docs/ClangFormat.rst +++ b/clang/docs/ClangFormat.rst @@ -144,6 +144,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. - 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 the directory of the ``.clang-format-ignore`` file, use ``*``. Multiple ``.clang-format-ignore`` files are supported similar to the diff --git a/clang/test/Format/clang-format-ignore.cpp b/clang/test/Format/clang-format-ignore.cpp index a2210266034d4c..3209588de80b02 100644 --- a/clang/test/Format/clang-format-ignore.cpp +++ b/clang/test/Format/clang-format-ignore.cpp @@ -1,24 +1,31 @@ +// RUN: rm -rf %t.dir // RUN: mkdir -p %t.dir/level1/level2 // RUN: cd %t.dir -// RUN: printf "*\nlevel*/*.c*\n*/*2/foo.*\n" > .clang-format-ignore +// RUN: printf "%%s\n" "\*" "level*/*.c*" "*/*2/foo.*" > .clang-format-ignore // RUN: touch foo.cc // RUN: clang-format -verbose .clang-format-ignore foo.cc 2> %t.stderr -// RUN: not grep "Formatting" %t.stderr +// RUN: not grep Formatting %t.stderr // RUN: cd level1 // RUN: touch bar.cc baz.c // RUN: clang-format -verbose bar.cc baz.c 2> %t.stderr -// RUN: not grep "Formatting" %t.stderr +// RUN: not grep Formatting %t.stderr // RUN: cd level2 // RUN: touch foo.c foo.js // RUN: clang-format -verbose foo.c foo.js 2> %t.stderr -// RUN: not grep "Formatting" %t.stderr -// RUN: printf "*.js\n" > .clang-format-ignore +// RUN: not grep Formatting %t.stderr + +// RUN: touch .clang-format-ignore +// RUN: clang-format -verbose foo.c foo.js 2> %t.stderr +// RUN: grep "Formatting \[1/2] foo.c" %t.stderr +// RUN: grep "Formatting \[2/2] foo.js" %t.stderr + +// RUN: printf "%%s\n" "*.js" > .clang-format-ignore // RUN: clang-format -verbose foo.c foo.js 2> %t.stderr -// RUN: grep -E "Formatting (.*)foo.c(.*)" %t.stderr -// RUN: not grep -E "Formatting (.*)foo.js(.*)" %t.stderr +// RUN: grep "Formatting \[1/2] foo.c" %t.stderr +// RUN: not grep "Formatting \[2/2] foo.js" %t.stderr // RUN: cd ../../.. // RUN: rm -rf %t.dir diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp index be78f8cbebf5e1..7d39aab9ff37dc 100644 --- a/clang/tools/clang-format/ClangFormat.cpp +++ b/clang/tools/clang-format/ClangFormat.cpp @@ -581,14 +581,15 @@ static int dumpConfig(bool IsSTDIN) { // - A pattern is relative to the directory of the .clang-format-ignore file (or // the root directory if the pattern starts with a slash). // - A pattern is negated if it starts with a bang (`!`). -static bool isIgnored(const StringRef FilePath) { - if (!llvm::sys::fs::is_regular_file(FilePath)) +static bool isIgnored(StringRef FilePath) { + using namespace llvm::sys::fs; + if (!is_regular_file(FilePath)) return false; using namespace llvm::sys::path; - SmallString<128> Path, AbsPath{convert_to_slash(FilePath)}; + SmallString<128> Path, AbsPath{FilePath}; - llvm::vfs::getRealFileSystem()->makeAbsolute(AbsPath); + make_absolute(AbsPath); remove_dots(AbsPath, /*remove_dot_dot=*/true); StringRef IgnoreDir{AbsPath}; @@ -599,12 +600,14 @@ static bool isIgnored(const StringRef FilePath) { Path = IgnoreDir; append(Path, ".clang-format-ignore"); - } while (!llvm::sys::fs::is_regular_file(Path)); + } while (!is_regular_file(Path)); std::ifstream IgnoreFile{Path.c_str()}; if (!IgnoreFile.good()) return false; + AbsPath = convert_to_slash(AbsPath); + bool HasMatch = false; for (std::string Pattern; std::getline(IgnoreFile, Pattern);) { Pattern = StringRef(Pattern).trim(); @@ -623,7 +626,7 @@ static bool isIgnored(const StringRef FilePath) { Path = IgnoreDir; append(Path, Pattern); remove_dots(Path, /*remove_dot_dot=*/true); - Pattern = Path.str(); + Pattern = convert_to_slash(Path); } if (clang::format::matchFilePath(Pattern, AbsPath.str()) == !IsNegated) { >From 1a6c747eff6d4583a72611639a191be91930e3fc Mon Sep 17 00:00:00 2001 From: Owen Pan <owenpi...@gmail.com> Date: Tue, 26 Dec 2023 01:34:00 -0800 Subject: [PATCH 4/6] Update clang/test/Format/clang-format-ignore.cpp --- clang/test/Format/clang-format-ignore.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/test/Format/clang-format-ignore.cpp b/clang/test/Format/clang-format-ignore.cpp index 3209588de80b02..38dd6212da3b5c 100644 --- a/clang/test/Format/clang-format-ignore.cpp +++ b/clang/test/Format/clang-format-ignore.cpp @@ -2,7 +2,9 @@ // RUN: mkdir -p %t.dir/level1/level2 // RUN: cd %t.dir -// RUN: printf "%%s\n" "\*" "level*/*.c*" "*/*2/foo.*" > .clang-format-ignore +// 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 -verbose .clang-format-ignore foo.cc 2> %t.stderr // RUN: not grep Formatting %t.stderr >From 840f2ddc2767e4dd68de69c03f87d2eaea271438 Mon Sep 17 00:00:00 2001 From: Owen Pan <owenpi...@gmail.com> Date: Tue, 26 Dec 2023 01:34:41 -0800 Subject: [PATCH 5/6] Make clang/test/Format/clang-format-ignore.cpp work on Windows --- clang/test/Format/clang-format-ignore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Format/clang-format-ignore.cpp b/clang/test/Format/clang-format-ignore.cpp index 38dd6212da3b5c..0d6396a64a668d 100644 --- a/clang/test/Format/clang-format-ignore.cpp +++ b/clang/test/Format/clang-format-ignore.cpp @@ -24,7 +24,7 @@ // RUN: grep "Formatting \[1/2] foo.c" %t.stderr // RUN: grep "Formatting \[2/2] foo.js" %t.stderr -// RUN: printf "%%s\n" "*.js" > .clang-format-ignore +// RUN: echo "*.js" > .clang-format-ignore // RUN: clang-format -verbose foo.c foo.js 2> %t.stderr // RUN: grep "Formatting \[1/2] foo.c" %t.stderr // RUN: not grep "Formatting \[2/2] foo.js" %t.stderr >From 36be87713572fd962bf60bb9252c4dd613665155 Mon Sep 17 00:00:00 2001 From: Owen Pan <owenpi...@gmail.com> Date: Tue, 26 Dec 2023 07:18:50 -0800 Subject: [PATCH 6/6] Update clang/tools/clang-format/ClangFormat.cpp --- clang/tools/clang-format/ClangFormat.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp index 7d39aab9ff37dc..e0ebbcb87d0239 100644 --- a/clang/tools/clang-format/ClangFormat.cpp +++ b/clang/tools/clang-format/ClangFormat.cpp @@ -623,10 +623,10 @@ static bool isIgnored(StringRef FilePath) { Pattern = StringRef(Pattern).ltrim(); if (Pattern[0] != '/') { - Path = IgnoreDir; - append(Path, Pattern); - remove_dots(Path, /*remove_dot_dot=*/true); - Pattern = convert_to_slash(Path); + Path = convert_to_slash(IgnoreDir); + append(Path, Style::posix, Pattern); + remove_dots(Path, /*remove_dot_dot=*/true, Style::posix); + Pattern = Path.str(); } if (clang::format::matchFilePath(Pattern, AbsPath.str()) == !IsNegated) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits