[clang] [clang-format] Add -r option for recursing into directories (PR #160299)

via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 23 06:46:06 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-format

Author: James Henderson (jh7370)

<details>
<summary>Changes</summary>

The option recursively replaces any directories on the command-line with all entries within the specified directories, making it easier to reformat an entire directory tree.

Fixes #<!-- -->62108

---
Full diff: https://github.com/llvm/llvm-project/pull/160299.diff


4 Files Affected:

- (modified) clang/docs/ClangFormat.rst (+1) 
- (modified) clang/docs/ReleaseNotes.rst (+1) 
- (added) clang/test/Format/recursive.test (+21) 
- (modified) clang/tools/clang-format/ClangFormat.cpp (+29) 


``````````diff
diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst
index 92af06e5083d6..cd00bedefa21b 100644
--- a/clang/docs/ClangFormat.rst
+++ b/clang/docs/ClangFormat.rst
@@ -93,6 +93,7 @@ to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# code.
     --output-replacements-xml      - Output replacements as XML.
     --qualifier-alignment=<string> - If set, overrides the qualifier alignment style
                                      determined by the QualifierAlignment style flag
+    -r                             - Recursively format files in any specified directories
     --sort-includes                - If set, overrides the include sorting behavior
                                      determined by the SortIncludes style flag
     --style=<string>               - Set coding style. <string> can be:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index fb429a675476d..e64ef490d292a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -532,6 +532,7 @@ clang-format
   literals.
 - Add ``Leave`` suboption to ``IndentPPDirectives``.
 - Add ``AllowBreakBeforeQtProperty`` option.
+- Add ``-r`` option for recursing into specified directories when formatting.
 
 libclang
 --------
diff --git a/clang/test/Format/recursive.test b/clang/test/Format/recursive.test
new file mode 100644
index 0000000000000..f0f3b9e5a3f07
--- /dev/null
+++ b/clang/test/Format/recursive.test
@@ -0,0 +1,21 @@
+RUN: rm -rf %t && mkdir %t
+RUN: split-file %s %t
+
+RUN: clang-format -i -r --verbose %t 2>&1 | FileCheck %s -DDIR=%t -DSEP=%{fs-sep}
+
+CHECK: Formatting [1/4] [[DIR]][[SEP]]1.cpp
+CHECK: Formatting [2/4] [[DIR]][[SEP]]2[[SEP]]3.cpp
+CHECK: Formatting [3/4] [[DIR]][[SEP]]2[[SEP]]4[[SEP]]5.cpp
+CHECK: Formatting [4/4] [[DIR]][[SEP]]2[[SEP]]6.cpp
+
+#--- 1.cpp
+int x;
+
+#--- 2/3.cpp
+int x;
+
+#--- 2/4/5.cpp
+int x;
+
+#--- 2/6.cpp
+int x;
diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp
index 5f6502f7f18a8..8267417e311f0 100644
--- a/clang/tools/clang-format/ClangFormat.cpp
+++ b/clang/tools/clang-format/ClangFormat.cpp
@@ -131,6 +131,10 @@ static cl::opt<std::string> Files(
     cl::desc("A file containing a list of files to process, one per line."),
     cl::value_desc("filename"), cl::init(""), cl::cat(ClangFormatCategory));
 
+static cl::opt<bool> Recursive("r",
+                             cl::desc("Recursively format files in any specified directories"),
+                             cl::cat(ClangFormatCategory));
+
 static cl::opt<bool>
     Verbose("verbose", cl::desc("If set, shows the list of processed files"),
             cl::cat(ClangFormatCategory));
@@ -700,6 +704,31 @@ int main(int argc, const char **argv) {
     errs() << "Clang-formatting " << LineNo << " files\n";
   }
 
+  if (Recursive) {
+    SmallVector<std::string> ExpandedNames;
+    for (const std::string &Path : FileNames) {
+      if (sys::fs::is_directory(Path)) {
+        std::error_code ErrorCode;
+        for (sys::fs::recursive_directory_iterator I(Path, ErrorCode), E;
+             I != E && !ErrorCode; I.increment(ErrorCode)) {
+          bool Result = false;
+          ErrorCode = sys::fs::is_regular_file(I->path(), Result);
+          // Conservatively assume that any unopenable entries are also regular
+          // files. Later code will emit an error when trying to format them, if
+          // they aren't valid by then.
+          if (ErrorCode || Result)
+            ExpandedNames.push_back(I->path());
+        }
+      } else {
+        ExpandedNames.push_back(std::move(Path));
+      }
+    }
+
+    FileNames.clear();
+    for (const std::string &Name : ExpandedNames)
+      FileNames.push_back(Name);
+  }
+
   if (FileNames.empty()) {
     if (isIgnored(AssumeFileName))
       return 0;

``````````

</details>


https://github.com/llvm/llvm-project/pull/160299


More information about the cfe-commits mailing list