@@ -571,6 +571,11 @@ static int dumpConfig(bool IsSTDIN) {
571571 return 0 ;
572572}
573573
574+ using String = SmallString<128 >;
575+ static String IgnoreDir; // Directory of .clang-format-ignore file.
576+ static StringRef PrevDir; // Directory of previous `FilePath`.
577+ static SmallVector<String> Patterns; // Patterns in .clang-format-ignore file.
578+
574579// Check whether `FilePath` is ignored according to the nearest
575580// .clang-format-ignore file based on the rules below:
576581// - A blank line is skipped.
@@ -586,45 +591,65 @@ static bool isIgnored(StringRef FilePath) {
586591 if (!is_regular_file (FilePath))
587592 return false ;
588593
589- using namespace llvm ::sys::path ;
590- SmallString< 128 > Path, AbsPath{FilePath};
594+ String Path ;
595+ String AbsPath{FilePath};
591596
597+ using namespace llvm ::sys::path;
592598 make_absolute (AbsPath);
593599 remove_dots (AbsPath, /* remove_dot_dot=*/ true );
594600
595- StringRef IgnoreDir{AbsPath};
596- do {
597- IgnoreDir = parent_path (IgnoreDir);
598- if (IgnoreDir.empty ())
601+ if (StringRef Dir{parent_path (AbsPath)}; PrevDir != Dir) {
602+ PrevDir = Dir;
603+
604+ for (;;) {
605+ Path = Dir;
606+ append (Path, " .clang-format-ignore" );
607+ if (is_regular_file (Path))
608+ break ;
609+ Dir = parent_path (Dir);
610+ if (Dir.empty ())
611+ return false ;
612+ }
613+
614+ IgnoreDir = convert_to_slash (Dir);
615+
616+ std::ifstream IgnoreFile{Path.c_str ()};
617+ if (!IgnoreFile.good ())
599618 return false ;
600619
601- Path = IgnoreDir;
602- append (Path, " .clang-format-ignore" );
603- } while (!is_regular_file (Path));
620+ Patterns.clear ();
604621
605- std::ifstream IgnoreFile{Path.c_str ()};
606- if (!IgnoreFile.good ())
607- return false ;
622+ for (std::string Line; std::getline (IgnoreFile, Line);) {
623+ if (const auto Pattern{StringRef{Line}.trim ()};
624+ // Skip empty and comment lines.
625+ !Pattern.empty () && Pattern[0 ] != ' #' ) {
626+ Patterns.push_back (Pattern);
627+ }
628+ }
629+ }
608630
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 ;
631+ if (IgnoreDir.empty ())
632+ return false ;
614633
615- const bool IsNegated = Pattern[0 ] == ' !' ;
634+ const auto Pathname{convert_to_slash (AbsPath)};
635+ for (const auto &Pat : Patterns) {
636+ const bool IsNegated = Pat[0 ] == ' !' ;
637+ StringRef Pattern{Pat};
616638 if (IsNegated)
617639 Pattern = Pattern.drop_front ();
618640
619641 if (Pattern.empty ())
620642 continue ;
621643
622644 Pattern = Pattern.ltrim ();
645+
646+ // `Pattern` is relative to `IgnoreDir` unless it starts with a slash.
647+ // This doesn't support patterns containing drive names (e.g. `C:`).
623648 if (Pattern[0 ] != ' /' ) {
624- Path = convert_to_slash ( IgnoreDir) ;
649+ Path = IgnoreDir;
625650 append (Path, Style::posix, Pattern);
626651 remove_dots (Path, /* remove_dot_dot=*/ true , Style::posix);
627- Pattern = Path. str () ;
652+ Pattern = Path;
628653 }
629654
630655 if (clang::format::matchFilePath (Pattern, Pathname) == !IsNegated)
0 commit comments