https://github.com/owenca created https://github.com/llvm/llvm-project/pull/182791
Add support for `BasedOnStyle: InheritParentConfig=<directory-path>` in config files to redirect inheritance to the `.clang-format` or `_clang-format` file in the `<directory_path>` directory. Closes #107808 >From 3f97c7842012d2644341a7081f158064189bd92d Mon Sep 17 00:00:00 2001 From: Owen Pan <[email protected]> Date: Sun, 22 Feb 2026 18:07:52 -0800 Subject: [PATCH] [clang-format] Allow InheritParentConfig to accept a directory Add support for `BasedOnStyle: InheritParentConfig=<directory-path>` in config files to redirect inheritance to the `.clang-format` or `_clang-format` file in the `<directory_path>` directory. Closes #107808 --- clang/docs/ClangFormatStyleOptions.rst | 10 +++-- clang/include/clang/Format/Format.h | 2 +- clang/lib/Format/Format.cpp | 58 ++++++++++++++++++++------ 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 4f81a084dd65b..13f5b92a9ea0b 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -182,11 +182,13 @@ the configuration (without a prefix: ``Auto``). Not a real style, but allows to use the ``.clang-format`` file from the parent directory (or its parent if there is none). If there is no parent file found it falls back to the ``fallback`` style, and applies the changes - to that. - - With this option you can overwrite some parts of your main style for your - subdirectories. This is also possible through the command line, e.g.: + to that. With this option you can overwrite some parts of your main style + for your subdirectories. This is also possible through the command line, + e.g.: ``--style={BasedOnStyle: InheritParentConfig, ColumnLimit: 20}`` + * ``InheritParentConfig=<directory-path>`` + Same as the above except that the inheritance is redirected to + ``<directory-path``. This is only supported in configuration files. .. START_FORMAT_STYLE_OPTIONS diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index c7e57d47f9ed1..1b3656a14a84f 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -56,7 +56,7 @@ struct FormatStyle { // If the BasedOn: was InheritParentConfig and this style needs the file from // the parent directories. It is not part of the actual style for formatting. // Thus the // instead of ///. - bool InheritsParentConfig; + std::string InheritConfig; /// The extra indent or outdent of access modifiers, e.g. ``public:``. /// \version 3.3 diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index f0e9aff2fd21a..180e631cd423a 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -24,6 +24,7 @@ #include "UsingDeclarationsSorter.h" #include "clang/Tooling/Inclusions/HeaderIncludes.h" #include "llvm/ADT/Sequence.h" +#include "llvm/ADT/StringSet.h" #include <limits> #define DEBUG_TYPE "format-formatter" @@ -1762,7 +1763,6 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.IndentRequiresClause = true; LLVMStyle.IndentWidth = 2; LLVMStyle.IndentWrappedFunctionNames = false; - LLVMStyle.InheritsParentConfig = false; LLVMStyle.InsertBraces = false; LLVMStyle.InsertNewlineAtEOF = false; LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None; @@ -2207,6 +2207,8 @@ FormatStyle getNoStyle() { bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, FormatStyle *Style) { + constexpr StringRef Prefix("inheritparentconfig="); + if (Name.equals_insensitive("llvm")) *Style = getLLVMStyle(Language); else if (Name.equals_insensitive("chromium")) @@ -2225,8 +2227,10 @@ bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, *Style = getClangFormatStyle(); else if (Name.equals_insensitive("none")) *Style = getNoStyle(); - else if (Name.equals_insensitive("inheritparentconfig")) - Style->InheritsParentConfig = true; + else if (Name.equals_insensitive(Prefix.drop_back())) + Style->InheritConfig = ".."; + else if (Name.size() > Prefix.size() && Name.starts_with_insensitive(Prefix)) + Style->InheritConfig = Name.substr(Prefix.size()); else return false; @@ -4424,7 +4428,7 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, return make_string_error("Error parsing -style: " + ec.message()); } - if (!Style.InheritsParentConfig) + if (Style.InheritConfig.empty()) return Style; ChildFormatTextToApply.emplace_back( @@ -4438,7 +4442,7 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, const bool IsDotHFile = FileName.ends_with(".h"); // User provided clang-format file using -style=file:path/to/format/file. - if (!Style.InheritsParentConfig && + if (Style.InheritConfig.empty() && StyleName.starts_with_insensitive("file:")) { auto ConfigFile = StyleName.substr(5); llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text = @@ -4452,7 +4456,7 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, LLVM_DEBUG(llvm::dbgs() << "Using configuration file " << ConfigFile << "\n"); - if (!Style.InheritsParentConfig) + if (Style.InheritConfig.empty()) return Style; // Search for parent configs starting from the parent directory of @@ -4464,10 +4468,10 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, // If the style inherits the parent configuration it is a command line // configuration, which wants to inherit, so we have to skip the check of the // StyleName. - if (!Style.InheritsParentConfig && !StyleName.equals_insensitive("file")) { + if (Style.InheritConfig.empty() && !StyleName.equals_insensitive("file")) { if (!getPredefinedStyle(StyleName, Style.Language, &Style)) return make_string_error("Invalid value for -style"); - if (!Style.InheritsParentConfig) + if (Style.InheritConfig.empty()) return Style; } @@ -4476,7 +4480,7 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, return make_string_error(EC.message()); // Reset possible inheritance - Style.InheritsParentConfig = false; + Style.InheritConfig.clear(); auto dropDiagnosticHandler = [](const llvm::SMDiagnostic &, void *) {}; @@ -4496,9 +4500,12 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, FilesToLookFor.push_back(".clang-format"); FilesToLookFor.push_back("_clang-format"); - SmallString<128> UnsuitableConfigFiles; + llvm::StringSet<> Directories; // Inherited directories. + bool Redirected = false; + llvm::SmallString<128> Dir, UnsuitableConfigFiles; for (StringRef Directory = Path; !Directory.empty(); - Directory = llvm::sys::path::parent_path(Directory)) { + Directory = Redirected ? Dir.str() + : llvm::sys::path::parent_path(Directory)) { auto Status = FS->status(Directory); if (!Status || Status->getType() != llvm::sys::fs::file_type::directory_file) { @@ -4534,7 +4541,7 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, LLVM_DEBUG(llvm::dbgs() << "Using configuration file " << ConfigFile << "\n"); - if (!Style.InheritsParentConfig) { + if (Style.InheritConfig.empty()) { if (!ChildFormatTextToApply.empty()) { LLVM_DEBUG(llvm::dbgs() << "Applying child configurations\n"); applyChildFormatTexts(&Style); @@ -4542,10 +4549,35 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, return Style; } + if (!Directories.insert(Directory).second) { + return make_string_error( + "Loop detected when inheriting configuration file in " + + Directory); + } + LLVM_DEBUG(llvm::dbgs() << "Inherits parent configuration\n"); + if (Style.InheritConfig == "..") { + Redirected = false; + } else { + Redirected = true; + SmallString<128> D; + switch (Style.InheritConfig[0]) { + case '/': + case '~': + D = Style.InheritConfig; + break; + default: { + D = Directory; + D.append("/"); + D.append(Style.InheritConfig); + } + } + llvm::sys::fs::real_path(D, Dir, /*expand_tilde=*/true); + } + // Reset inheritance of style - Style.InheritsParentConfig = false; + Style.InheritConfig.clear(); ChildFormatTextToApply.emplace_back(std::move(*Text)); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
