-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[clang-format] Add .clang-format-ignore for ignoring files #76327
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-format Author: Owen Pan (owenca) ChangesCloses #52975. Full diff: https://github.com/llvm/llvm-project/pull/76327.diff 3 Files Affected:
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;
}
|
In the description (and the commit messages?) you wrote |
Post #76327 the build rule required the header in lib as source too. Tried to just do minimal change specific to build.
FYI commit 0930812 added a test that does not pass on Windows (MSVC). An example failure:
|
Closes #52975.