diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 9ef1fd5f36d1d..9033bdc9018ae 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -3727,6 +3727,11 @@ the configuration (without a prefix: ``Auto``). namespace Extra { }}} +.. _ConfigFile: + +**ConfigFile** (``String``) :versionbadge:`clang-format 20` :ref:`¶ ` + Specify the absolute or relative path of another config file to process. + .. _ConstructorInitializerAllOnOneLineOrOnePerLine: **ConstructorInitializerAllOnOneLineOrOnePerLine** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ ` diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index c9b72e65cb236..44ea7d25ee0e7 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2481,6 +2481,10 @@ struct FormatStyle { /// \version 5 bool CompactNamespaces; + /// Specify the absolute or relative path of another config file to process. + /// \version 20 + std::string ConfigFile; + /// This option is **deprecated**. See ``CurrentLine`` of /// ``PackConstructorInitializers``. /// \version 3.7 @@ -5195,6 +5199,7 @@ struct FormatStyle { BreakTemplateDeclarations == R.BreakTemplateDeclarations && ColumnLimit == R.ColumnLimit && CommentPragmas == R.CommentPragmas && CompactNamespaces == R.CompactNamespaces && + ConfigFile == R.ConfigFile && ConstructorInitializerIndentWidth == R.ConstructorInitializerIndentWidth && ContinuationIndentWidth == R.ContinuationIndentWidth && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 0cf4cdbeab31f..b502e8022de42 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1163,6 +1163,7 @@ template <> struct MappingTraits { IO.mapOptional("TemplateNames", Style.TemplateNames); IO.mapOptional("TypeNames", Style.TypeNames); IO.mapOptional("TypenameMacros", Style.TypenameMacros); + IO.mapOptional("ConfigFile", Style.ConfigFile); IO.mapOptional("UseTab", Style.UseTab); IO.mapOptional("VerilogBreakBetweenInstancePorts", Style.VerilogBreakBetweenInstancePorts); @@ -2046,6 +2047,13 @@ ParseError validateQualifierOrder(FormatStyle *Style) { return ParseError::Success; } +llvm::ErrorOr> +loadAndParseConfigFile(StringRef ConfigFile, llvm::vfs::FileSystem *FS, + FormatStyle *Style, bool AllowUnknownOptions, + llvm::SourceMgr::DiagHandlerTy DiagHandler); + +static constexpr StringRef Source{""}; + std::error_code parseConfiguration(llvm::MemoryBufferRef Config, FormatStyle *Style, bool AllowUnknownOptions, llvm::SourceMgr::DiagHandlerTy DiagHandler, @@ -2107,8 +2115,41 @@ std::error_code parseConfiguration(llvm::MemoryBufferRef Config, // See comment on FormatStyle::TSC_Wrapped. return make_error_code(ParseError::BinPackTrailingCommaConflict); } - if (Style->QualifierAlignment != FormatStyle::QAS_Leave) - return make_error_code(validateQualifierOrder(Style)); + if (Style->QualifierAlignment != FormatStyle::QAS_Leave) { + const auto EC = validateQualifierOrder(Style); + if (EC != ParseError::Success) + return make_error_code(EC); + } + if (!Style->InheritsParentConfig && !Style->ConfigFile.empty()) { + auto *FS = llvm::vfs::getRealFileSystem().get(); + assert(FS); + SmallString<128> ConfigFile{Style->ConfigFile}; + Style->ConfigFile.clear(); + switch (ConfigFile[0]) { + case '~': + llvm::sys::fs::expand_tilde(ConfigFile, ConfigFile); + break; + case '/': + break; + default: { + const auto BufferId = Config.getBufferIdentifier(); + if (BufferId == Source) { + llvm::sys::fs::make_absolute(ConfigFile); + } else { + llvm::sys::fs::make_absolute(llvm::sys::path::parent_path(BufferId), + ConfigFile); + } + } + } + if (!llvm::sys::fs::exists(ConfigFile)) { + llvm::errs() << ConfigFile << ": " << "file not found\n"; + return make_error_code(ParseError::Error); + } + const auto Text = loadAndParseConfigFile(ConfigFile, FS, Style, + AllowUnknownOptions, DiagHandler); + if (Text.getError()) + return make_error_code(ParseError::Error); + } return make_error_code(ParseError::Success); } @@ -4032,7 +4073,6 @@ Expected getStyle(StringRef StyleName, StringRef FileName, if (StyleName.starts_with("{")) { // Parse YAML/JSON style from the command line. - StringRef Source = ""; if (std::error_code ec = parseConfiguration(llvm::MemoryBufferRef(StyleName, Source), &Style, AllowUnknownOptions, DiagHandler)) {