Skip to content

Commit 0930812

Browse files
authored
[clang-format] Add .clang-format-ignore for ignoring files (#76327)
Closes #52975.
1 parent 589a24b commit 0930812

File tree

3 files changed

+120
-1
lines changed

3 files changed

+120
-1
lines changed

clang/docs/ClangFormat.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,25 @@ An easy way to create the ``.clang-format`` file is:
131131
132132
Available style options are described in :doc:`ClangFormatStyleOptions`.
133133

134+
You can create ``.clang-format-ignore`` files to make ``clang-format`` ignore
135+
certain files. A ``.clang-format-ignore`` file consists of patterns of file path
136+
names. It has the following format:
137+
- A blank line is skipped.
138+
- Leading and trailing spaces of a line are trimmed.
139+
- A line starting with a hash (``#``) is a comment.
140+
- A non-comment line is a single pattern.
141+
- The slash (``/``) is used as the directory separator.
142+
- A pattern is relative to the directory of the ``.clang-format-ignore`` file
143+
(or the root directory if the pattern starts with a slash).
144+
- Patterns follow the rules specified in POSIX 2.13.1, 2.13.2, and Rule 1 of
145+
2.13.3.
146+
- A pattern is negated if it starts with a bang (``!``).
147+
148+
To match all files in a directory, use e.g. ``foo/bar/*``. To match all files in
149+
the directory of the ``.clang-format-ignore`` file, use ``*``.
150+
Multiple ``.clang-format-ignore`` files are supported similar to the
151+
``.clang-format`` files, with a lower directory level file voiding the higher
152+
level ones.
134153

135154
Vim Integration
136155
===============
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: rm -rf %t.dir
2+
// RUN: mkdir -p %t.dir/level1/level2
3+
4+
// RUN: cd %t.dir
5+
// RUN: echo "*" > .clang-format-ignore
6+
// RUN: echo "level*/*.c*" >> .clang-format-ignore
7+
// RUN: echo "*/*2/foo.*" >> .clang-format-ignore
8+
// RUN: touch foo.cc
9+
// RUN: clang-format -verbose .clang-format-ignore foo.cc 2> %t.stderr
10+
// RUN: not grep Formatting %t.stderr
11+
12+
// RUN: cd level1
13+
// RUN: touch bar.cc baz.c
14+
// RUN: clang-format -verbose bar.cc baz.c 2> %t.stderr
15+
// RUN: not grep Formatting %t.stderr
16+
17+
// RUN: cd level2
18+
// RUN: touch foo.c foo.js
19+
// RUN: clang-format -verbose foo.c foo.js 2> %t.stderr
20+
// RUN: not grep Formatting %t.stderr
21+
22+
// RUN: touch .clang-format-ignore
23+
// RUN: clang-format -verbose foo.c foo.js 2> %t.stderr
24+
// RUN: grep "Formatting \[1/2] foo.c" %t.stderr
25+
// RUN: grep "Formatting \[2/2] foo.js" %t.stderr
26+
27+
// RUN: echo "*.js" > .clang-format-ignore
28+
// RUN: clang-format -verbose foo.c foo.js 2> %t.stderr
29+
// RUN: grep "Formatting \[1/2] foo.c" %t.stderr
30+
// RUN: not grep "Formatting \[2/2] foo.js" %t.stderr
31+
32+
// RUN: cd ../../..
33+
// RUN: rm -rf %t.dir

clang/tools/clang-format/ClangFormat.cpp

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
///
1313
//===----------------------------------------------------------------------===//
1414

15+
#include "../../lib/Format/MatchFilePath.h"
1516
#include "clang/Basic/Diagnostic.h"
1617
#include "clang/Basic/DiagnosticOptions.h"
1718
#include "clang/Basic/FileManager.h"
@@ -570,6 +571,69 @@ static int dumpConfig(bool IsSTDIN) {
570571
return 0;
571572
}
572573

574+
// Check whether `FilePath` is ignored according to the nearest
575+
// .clang-format-ignore file based on the rules below:
576+
// - A blank line is skipped.
577+
// - Leading and trailing spaces of a line are trimmed.
578+
// - A line starting with a hash (`#`) is a comment.
579+
// - A non-comment line is a single pattern.
580+
// - The slash (`/`) is used as the directory separator.
581+
// - A pattern is relative to the directory of the .clang-format-ignore file (or
582+
// the root directory if the pattern starts with a slash).
583+
// - A pattern is negated if it starts with a bang (`!`).
584+
static bool isIgnored(StringRef FilePath) {
585+
using namespace llvm::sys::fs;
586+
if (!is_regular_file(FilePath))
587+
return false;
588+
589+
using namespace llvm::sys::path;
590+
SmallString<128> Path, AbsPath{FilePath};
591+
592+
make_absolute(AbsPath);
593+
remove_dots(AbsPath, /*remove_dot_dot=*/true);
594+
595+
StringRef IgnoreDir{AbsPath};
596+
do {
597+
IgnoreDir = parent_path(IgnoreDir);
598+
if (IgnoreDir.empty())
599+
return false;
600+
601+
Path = IgnoreDir;
602+
append(Path, ".clang-format-ignore");
603+
} while (!is_regular_file(Path));
604+
605+
std::ifstream IgnoreFile{Path.c_str()};
606+
if (!IgnoreFile.good())
607+
return false;
608+
609+
const auto Pathname = convert_to_slash(AbsPath);
610+
for (std::string Line; std::getline(IgnoreFile, Line);) {
611+
auto Pattern = StringRef(Line).trim();
612+
if (Pattern.empty() || Pattern[0] == '#')
613+
continue;
614+
615+
const bool IsNegated = Pattern[0] == '!';
616+
if (IsNegated)
617+
Pattern = Pattern.drop_front();
618+
619+
if (Pattern.empty())
620+
continue;
621+
622+
Pattern = Pattern.ltrim();
623+
if (Pattern[0] != '/') {
624+
Path = convert_to_slash(IgnoreDir);
625+
append(Path, Style::posix, Pattern);
626+
remove_dots(Path, /*remove_dot_dot=*/true, Style::posix);
627+
Pattern = Path.str();
628+
}
629+
630+
if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)
631+
return true;
632+
}
633+
634+
return false;
635+
}
636+
573637
int main(int argc, const char **argv) {
574638
llvm::InitLLVM X(argc, argv);
575639

@@ -618,11 +682,14 @@ int main(int argc, const char **argv) {
618682
unsigned FileNo = 1;
619683
bool Error = false;
620684
for (const auto &FileName : FileNames) {
685+
const bool IsSTDIN = FileName == "-";
686+
if (!IsSTDIN && isIgnored(FileName))
687+
continue;
621688
if (Verbose) {
622689
errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
623690
<< FileName << "\n";
624691
}
625-
Error |= clang::format::format(FileName, FileName == "-");
692+
Error |= clang::format::format(FileName, IsSTDIN);
626693
}
627694
return Error ? 1 : 0;
628695
}

0 commit comments

Comments
 (0)