diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst index 819d9ee9f9cde..8d4017b29fb8e 100644 --- a/clang/docs/ClangFormat.rst +++ b/clang/docs/ClangFormat.rst @@ -131,9 +131,6 @@ An easy way to create the ``.clang-format`` file is: Available style options are described in :doc:`ClangFormatStyleOptions`. -.clang-format-ignore -==================== - 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: @@ -144,8 +141,7 @@ 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). Patterns - containing drive names (e.g. ``C:``) are not supported. + (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 `_. diff --git a/clang/test/Format/clang-format-ignore.cpp b/clang/test/Format/clang-format-ignore.cpp index 5a2267b302d22..0d6396a64a668 100644 --- a/clang/test/Format/clang-format-ignore.cpp +++ b/clang/test/Format/clang-format-ignore.cpp @@ -21,26 +21,13 @@ // RUN: touch .clang-format-ignore // RUN: clang-format -verbose foo.c foo.js 2> %t.stderr -// RUN: grep -Fx "Formatting [1/2] foo.c" %t.stderr -// RUN: grep -Fx "Formatting [2/2] foo.js" %t.stderr +// RUN: grep "Formatting \[1/2] foo.c" %t.stderr +// RUN: grep "Formatting \[2/2] foo.js" %t.stderr // RUN: echo "*.js" > .clang-format-ignore // RUN: clang-format -verbose foo.c foo.js 2> %t.stderr -// RUN: grep -Fx "Formatting [1/2] foo.c" %t.stderr -// RUN: not grep -F 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: clang-format -verbose *.cc level1/*.c* level1/level2/foo.* 2> %t.stderr -// RUN: grep -x "Formatting \[1/5] .*foo\.c" %t.stderr -// RUN: not grep -F foo.js %t.stderr - -// RUN: rm .clang-format-ignore -// RUN: clang-format -verbose *.cc level1/*.c* level1/level2/foo.* 2> %t.stderr -// RUN: grep -x "Formatting \[1/5] .*foo\.cc" %t.stderr -// RUN: grep -x "Formatting \[2/5] .*bar\.cc" %t.stderr -// RUN: grep -x "Formatting \[3/5] .*baz\.c" %t.stderr -// RUN: grep -x "Formatting \[4/5] .*foo\.c" %t.stderr -// RUN: not grep -F foo.js %t.stderr - -// RUN: cd .. -// RUN: rm -r %t.dir +// RUN: cd ../../.. +// RUN: rm -rf %t.dir diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp index 787e56a6eccc0..be34dbbe886a1 100644 --- a/clang/tools/clang-format/ClangFormat.cpp +++ b/clang/tools/clang-format/ClangFormat.cpp @@ -571,11 +571,6 @@ static int dumpConfig(bool IsSTDIN) { return 0; } -using String = SmallString<128>; -static String IgnoreDir; // Directory of .clang-format-ignore file. -static StringRef PrevDir; // Directory of previous `FilePath`. -static SmallVector Patterns; // Patterns in .clang-format-ignore file. - // Check whether `FilePath` is ignored according to the nearest // .clang-format-ignore file based on the rules below: // - A blank line is skipped. @@ -591,50 +586,33 @@ static bool isIgnored(StringRef FilePath) { if (!is_regular_file(FilePath)) return false; - String Path; - String AbsPath{FilePath}; - using namespace llvm::sys::path; + SmallString<128> Path, AbsPath{FilePath}; + make_absolute(AbsPath); remove_dots(AbsPath, /*remove_dot_dot=*/true); - if (StringRef Dir{parent_path(AbsPath)}; PrevDir != Dir) { - PrevDir = Dir; - - for (;;) { - Path = Dir; - append(Path, ".clang-format-ignore"); - if (is_regular_file(Path)) - break; - Dir = parent_path(Dir); - if (Dir.empty()) - return false; - } - - IgnoreDir = convert_to_slash(Dir); - - std::ifstream IgnoreFile{Path.c_str()}; - if (!IgnoreFile.good()) + StringRef IgnoreDir{AbsPath}; + do { + IgnoreDir = parent_path(IgnoreDir); + if (IgnoreDir.empty()) return false; - Patterns.clear(); + Path = IgnoreDir; + append(Path, ".clang-format-ignore"); + } while (!is_regular_file(Path)); - for (std::string Line; std::getline(IgnoreFile, Line);) { - if (const auto Pattern{StringRef{Line}.trim()}; - // Skip empty and comment lines. - !Pattern.empty() && Pattern[0] != '#') { - Patterns.push_back(Pattern); - } - } - } - - if (IgnoreDir.empty()) + std::ifstream IgnoreFile{Path.c_str()}; + if (!IgnoreFile.good()) return false; - const auto Pathname{convert_to_slash(AbsPath)}; - for (const auto &Pat : Patterns) { - const bool IsNegated = Pat[0] == '!'; - StringRef Pattern{Pat}; + const auto Pathname = convert_to_slash(AbsPath); + for (std::string Line; std::getline(IgnoreFile, Line);) { + auto Pattern = StringRef(Line).trim(); + if (Pattern.empty() || Pattern[0] == '#') + continue; + + const bool IsNegated = Pattern[0] == '!'; if (IsNegated) Pattern = Pattern.drop_front(); @@ -642,14 +620,11 @@ static bool isIgnored(StringRef FilePath) { continue; Pattern = Pattern.ltrim(); - - // `Pattern` is relative to `IgnoreDir` unless it starts with a slash. - // This doesn't support patterns containing drive names (e.g. `C:`). if (Pattern[0] != '/') { - Path = IgnoreDir; + Path = convert_to_slash(IgnoreDir); append(Path, Style::posix, Pattern); remove_dots(Path, /*remove_dot_dot=*/true, Style::posix); - Pattern = Path; + Pattern = Path.str(); } if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)