https://github.com/jediry created https://github.com/llvm/llvm-project/pull/107312
At present, the ```BasedOnStyle``` directive can reference a predefined style name, or ```InheritFromParent```, instructing clang-format to search upward in the directory hierarchy for a .clang-format file. This works fine when variations in codebase formatting align with the directory hierarchy, but becomes problematic when it is desired to share formatting across portions of a repository with no common parent directory. For example, consider the case of a large, multi-team repository containing many projects as sub-directories of the repository root, where each of the various engineering teams owns multiple of these projects, and wants a common coding style across all of its owned projects. In this PR, I'm extending ```BasedOnStyle``` to allow referencing an arbitrary file. While this could be an absolute path, that's not typically useful across machines. In typical usage, this will be a relative path (e.g., ```BasedOnStyle: format/team1.clang-format```). For resolving relative paths, I've mimicked the "include path" model of C/C++ (and, in fact, I'm able to leverage ```SourceMgr```'s "include paths" mechanism to implement this). The list of style search paths is specified on the command-line via one or more ```--style-search-path <path>``` parameters. The interesting stuff is in Format.cpp...most of the rest of this change is plumbing to get the StyleSearchPaths from the command-line to the call to ```getStyle()```. Unfinished aspects of this change: * No unit tests. I am happy to write some, but I'm unsure how to do this in a harmonious way, seeing as my change inherently relies on external files on the filesystem. Most of the unit tests I've seen in Clang rely on in-code strings. * ```--style-search-path .``` does not seem to work for specifying the current directory as a search path. Evidently the ```SourceMgr``` include paths mechanism does not handle this natively. Can someone point me to the proper "Clang" way to resolve paths like . or .. into paths that ```SourceMgr``` can handle? >From 163657095741b2292540fce7ff424cb4600391da Mon Sep 17 00:00:00 2001 From: Ryan Saunders <ryans...@microsoft.com> Date: Fri, 2 Aug 2024 15:08:24 -0700 Subject: [PATCH] Add support for BasedOnStyle referencing an arbitrary file, locatable relative to directories specified via --style-search-path Improve command-line help Clean up formatting Fix unit tests Fix other clang tools that leverage format::getStyle() Fix up clangd --- .../tool/ClangApplyReplacementsMain.cpp | 19 ++++ .../ChangeNamespace.cpp | 7 +- .../clang-change-namespace/ChangeNamespace.h | 6 +- .../tool/ClangChangeNamespace.cpp | 18 ++- .../tool/ClangIncludeFixer.cpp | 22 +++- clang-tools-extra/clang-move/Move.cpp | 1 + clang-tools-extra/clang-move/Move.h | 3 + .../clang-move/tool/ClangMove.cpp | 22 +++- clang-tools-extra/clang-tidy/ClangTidy.cpp | 3 +- .../clang-tidy/ClangTidyOptions.h | 4 + .../clang-tidy/misc/IncludeCleanerCheck.cpp | 5 +- .../clang-tidy/misc/IncludeCleanerCheck.h | 1 + .../clang-tidy/tool/ClangTidyMain.cpp | 15 +++ clang-tools-extra/clangd/ClangdLSPServer.cpp | 2 +- clang-tools-extra/clangd/ClangdServer.cpp | 13 ++- clang-tools-extra/clangd/ClangdServer.h | 11 +- clang-tools-extra/clangd/Config.h | 4 + clang-tools-extra/clangd/IncludeCleaner.cpp | 1 + clang-tools-extra/clangd/SourceCode.cpp | 3 + clang-tools-extra/clangd/tool/Check.cpp | 2 +- clang-tools-extra/clangd/tool/ClangdMain.cpp | 23 ++++ .../clangd/unittests/ClangdTests.cpp | 2 +- .../include-cleaner/tool/IncludeCleaner.cpp | 15 ++- .../ChangeNamespaceTests.cpp | 2 +- .../unittests/clang-move/ClangMoveTests.cpp | 4 +- clang/include/clang/Format/Format.h | 29 ++++- clang/include/clang/Tooling/Refactoring.h | 4 +- clang/lib/Format/Format.cpp | 51 +++++++-- clang/lib/Tooling/Refactoring.cpp | 4 +- clang/tools/clang-format/ClangFormat.cpp | 21 +++- clang/unittests/Format/ConfigParseTest.cpp | 106 ++++++++++-------- clang/unittests/Format/FormatTestObjC.cpp | 56 ++++----- .../unittests/Format/FormatTestRawStrings.cpp | 2 +- .../ObjCPropertyAttributeOrderFixerTest.cpp | 6 +- clang/unittests/Format/QualifierFixerTest.cpp | 6 +- clang/unittests/Rename/ClangRenameTest.h | 2 +- clang/unittests/Tooling/RefactoringTest.cpp | 3 +- llvm/include/llvm/Support/YAMLTraits.h | 6 + llvm/lib/Support/YAMLTraits.cpp | 12 ++ 39 files changed, 391 insertions(+), 125 deletions(-) diff --git a/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp b/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp index 68b5743c6540f8..52e1251cb1482a 100644 --- a/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp +++ b/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp @@ -69,6 +69,24 @@ static cl::opt<std::string> FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription), cl::init("LLVM"), cl::cat(FormattingCategory)); +static cl::list<std::string> +StyleSearchPaths( + "style-search-path", + cl::desc("Directory to search for BasedOnStyle files, when the value of the\n" + "BasedOnStyle directive is not one of the predefined styles, nor\n" + "InheritFromParent. Multiple style search paths may be specified,\n" + "and will be searched in order, stopping at the first file found."), + cl::value_desc("directory"), + cl::cat(FormattingCategory)); + +static cl::alias +StyleSearchPathShort( + "S", + cl::desc("Alias for --style-search-path"), + cl::cat(FormattingCategory), + cl::aliasopt(StyleSearchPaths), + cl::NotHidden); + namespace { // Helper object to remove the TUReplacement and TUDiagnostic (triggered by // "remove-change-desc-files" command line option) when exiting current scope. @@ -102,6 +120,7 @@ int main(int argc, char **argv) { // Determine a formatting style from options. auto FormatStyleOrError = format::getStyle(FormatStyleOpt, FormatStyleConfig, + StyleSearchPaths, format::DefaultFallbackStyle); if (!FormatStyleOrError) { llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n"; diff --git a/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp b/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp index 879c0d26d472a8..f1a4716e2c45ea 100644 --- a/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp +++ b/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp @@ -339,8 +339,9 @@ ChangeNamespaceTool::ChangeNamespaceTool( llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, llvm::ArrayRef<std::string> AllowedSymbolPatterns, std::map<std::string, tooling::Replacements> *FileToReplacements, - llvm::StringRef FallbackStyle) - : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements), + llvm::StringRef FallbackStyle, const std::vector<std::string>& StyleSearchPaths) + : FallbackStyle(FallbackStyle), StyleSearchPaths(StyleSearchPaths), + FileToReplacements(*FileToReplacements), OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')), FilePattern(FilePattern), FilePatternRE(FilePattern) { FileToReplacements->clear(); @@ -1004,7 +1005,7 @@ void ChangeNamespaceTool::onEndOfTranslationUnit() { // which refers to the original code. Replaces = Replaces.merge(NewReplacements); auto Style = - format::getStyle(format::DefaultFormatStyle, FilePath, FallbackStyle); + format::getStyle(format::DefaultFormatStyle, FilePath, StyleSearchPaths, FallbackStyle); if (!Style) { llvm::errs() << llvm::toString(Style.takeError()) << "\n"; continue; diff --git a/clang-tools-extra/clang-change-namespace/ChangeNamespace.h b/clang-tools-extra/clang-change-namespace/ChangeNamespace.h index d35119b70a69c4..969d983681ebd5 100644 --- a/clang-tools-extra/clang-change-namespace/ChangeNamespace.h +++ b/clang-tools-extra/clang-change-namespace/ChangeNamespace.h @@ -51,7 +51,8 @@ class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback { llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, llvm::ArrayRef<std::string> AllowedSymbolPatterns, std::map<std::string, tooling::Replacements> *FileToReplacements, - llvm::StringRef FallbackStyle = "LLVM"); + llvm::StringRef FallbackStyle = "LLVM", + const std::vector<std::string>& StyleSearchPaths = {}); void registerMatchers(ast_matchers::MatchFinder *Finder); @@ -109,6 +110,9 @@ class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback { }; std::string FallbackStyle; + // Specifies the list of paths to be searched when FormatStyle or a BasedOnStyle + // in a .clang-format file specifies an arbitrary file to include + std::vector<std::string> StyleSearchPaths; // In match callbacks, this contains replacements for replacing `typeLoc`s in // and deleting forward declarations in the moved namespace blocks. // In `onEndOfTranslationUnit` callback, the previous added replacements are diff --git a/clang-tools-extra/clang-change-namespace/tool/ClangChangeNamespace.cpp b/clang-tools-extra/clang-change-namespace/tool/ClangChangeNamespace.cpp index 22d26db0c11bcf..b78dcfc713a037 100644 --- a/clang-tools-extra/clang-change-namespace/tool/ClangChangeNamespace.cpp +++ b/clang-tools-extra/clang-change-namespace/tool/ClangChangeNamespace.cpp @@ -72,6 +72,22 @@ cl::opt<std::string> Style("style", cl::desc("The style name used for reformatting."), cl::init("LLVM"), cl::cat(ChangeNamespaceCategory)); +cl::list<std::string> StyleSearchPaths( + "style-search-path", + cl::desc("Directory to search for BasedOnStyle files, when the value of the\n" + "BasedOnStyle directive is not one of the predefined styles, nor\n" + "InheritFromParent. Multiple style search paths may be specified,\n" + "and will be searched in order, stopping at the first file found."), + cl::value_desc("directory"), + cl::cat(ChangeNamespaceCategory)); + +cl::alias StyleSearchPathShort( + "S", + cl::desc("Alias for --style-search-path"), + cl::cat(ChangeNamespaceCategory), + cl::aliasopt(StyleSearchPaths), + cl::NotHidden); + cl::opt<std::string> AllowedFile( "allowed_file", cl::desc("A file containing regexes of symbol names that are not expected " @@ -135,7 +151,7 @@ int main(int argc, const char **argv) { SourceManager Sources(Diagnostics, FileMgr); Rewriter Rewrite(Sources, DefaultLangOptions); - if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) { + if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style, StyleSearchPaths)) { llvm::errs() << "Failed applying all replacements.\n"; return 1; } diff --git a/clang-tools-extra/clang-include-fixer/tool/ClangIncludeFixer.cpp b/clang-tools-extra/clang-include-fixer/tool/ClangIncludeFixer.cpp index 3a11a22def1946..febbd4021ac4b7 100644 --- a/clang-tools-extra/clang-include-fixer/tool/ClangIncludeFixer.cpp +++ b/clang-tools-extra/clang-include-fixer/tool/ClangIncludeFixer.cpp @@ -157,6 +157,24 @@ cl::opt<std::string> "headers if there is no clang-format config file found."), cl::init("llvm"), cl::cat(IncludeFixerCategory)); +static cl::list<std::string> +StyleSearchPaths( + "style-search-path", + cl::desc("Directory to search for BasedOnStyle files, when the value of the\n" + "BasedOnStyle directive is not one of the predefined styles, nor\n" + "InheritFromParent. Multiple style search paths may be specified,\n" + "and will be searched in order, stopping at the first file found."), + cl::value_desc("directory"), + cl::cat(IncludeFixerCategory)); + +static cl::alias +StyleSearchPathShort( + "S", + cl::desc("Alias for --style-search-path"), + cl::cat(IncludeFixerCategory), + cl::aliasopt(StyleSearchPaths), + cl::NotHidden ); + std::unique_ptr<include_fixer::SymbolIndexManager> createSymbolIndexManager(StringRef FilePath) { using find_all_symbols::SymbolInfo; @@ -330,7 +348,7 @@ int includeFixerMain(int argc, const char **argv) { return LHS.QualifiedName == RHS.QualifiedName; }); auto InsertStyle = format::getStyle(format::DefaultFormatStyle, - Context.getFilePath(), Style); + Context.getFilePath(), StyleSearchPaths, Style); if (!InsertStyle) { llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n"; return 1; @@ -410,7 +428,7 @@ int includeFixerMain(int argc, const char **argv) { for (const auto &Context : Contexts) { StringRef FilePath = Context.getFilePath(); auto InsertStyle = - format::getStyle(format::DefaultFormatStyle, FilePath, Style); + format::getStyle(format::DefaultFormatStyle, FilePath, StyleSearchPaths, Style); if (!InsertStyle) { llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n"; return 1; diff --git a/clang-tools-extra/clang-move/Move.cpp b/clang-tools-extra/clang-move/Move.cpp index ac16803b46783e..d5068d3087b44f 100644 --- a/clang-tools-extra/clang-move/Move.cpp +++ b/clang-tools-extra/clang-move/Move.cpp @@ -781,6 +781,7 @@ void ClangMoveTool::removeDeclsInOldFiles() { if (SI == FilePathToFileID.end()) continue; llvm::StringRef Code = SM.getBufferData(SI->second); auto Style = format::getStyle(format::DefaultFormatStyle, FilePath, + Context->StyleSearchPaths, Context->FallbackStyle); if (!Style) { llvm::errs() << llvm::toString(Style.takeError()) << "\n"; diff --git a/clang-tools-extra/clang-move/Move.h b/clang-tools-extra/clang-move/Move.h index ea241bbbc4f8a0..a368fac3645dbf 100644 --- a/clang-tools-extra/clang-move/Move.h +++ b/clang-tools-extra/clang-move/Move.h @@ -89,6 +89,9 @@ struct ClangMoveContext { // directory when analyzing the source file. We save the original working // directory in order to get the absolute file path for the fields in Spec. std::string OriginalRunningDirectory; + // Specifies the list of paths to be searched when BasedOnStyle + // in a .clang-format file specifies an arbitrary file to include + std::vector<std::string> StyleSearchPaths; // The name of a predefined code style. std::string FallbackStyle; // Whether dump all declarations in old header. diff --git a/clang-tools-extra/clang-move/tool/ClangMove.cpp b/clang-tools-extra/clang-move/tool/ClangMove.cpp index 1560dcaad67793..5ea3ef218ed0f3 100644 --- a/clang-tools-extra/clang-move/tool/ClangMove.cpp +++ b/clang-tools-extra/clang-move/tool/ClangMove.cpp @@ -76,6 +76,22 @@ cl::opt<bool> "add #include of old header to new header."), cl::init(false), cl::cat(ClangMoveCategory)); +cl::list<std::string> + StyleSearchPaths("style-search-path", + cl::desc("Directory to search for BasedOnStyle files, when the value of the\n" + "BasedOnStyle directive is not one of the predefined styles, nor\n" + "InheritFromParent. Multiple style search paths may be specified,\n" + "and will be searched in order, stopping at the first file found."), + cl::value_desc("directory"), + cl::cat(ClangMoveCategory)); + +cl::alias + StyleSearchPathShort("S", + cl::desc("Alias for --style-search-path"), + cl::cat(ClangMoveCategory), + cl::aliasopt(StyleSearchPaths), + cl::NotHidden); + cl::opt<std::string> Style("style", cl::desc("The style name used for reformatting. Default is \"llvm\""), @@ -131,8 +147,8 @@ int main(int argc, const char **argv) { Twine(EC.message())); move::ClangMoveContext Context{Spec, Tool.getReplacements(), - std::string(InitialDirectory), Style, - DumpDecls}; + std::string(InitialDirectory), StyleSearchPaths, + Style, DumpDecls}; move::DeclarationReporter Reporter; move::ClangMoveActionFactory Factory(&Context, &Reporter); @@ -185,7 +201,7 @@ int main(int argc, const char **argv) { SourceManager SM(Diagnostics, FileMgr); Rewriter Rewrite(SM, LangOptions()); - if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) { + if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style, StyleSearchPaths)) { llvm::errs() << "Failed applying all replacements.\n"; return 1; } diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 1cd7cdd10bc25f..32b0de29291ce4 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -209,7 +209,8 @@ class ErrorReporter { } StringRef Code = Buffer.get()->getBuffer(); auto Style = format::getStyle( - *Context.getOptionsForFile(File).FormatStyle, File, "none"); + *Context.getOptionsForFile(File).FormatStyle, File, + Context.getGlobalOptions().StyleSearchPaths, "none"); if (!Style) { llvm::errs() << llvm::toString(Style.takeError()) << "\n"; continue; diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h index 85d5a02ebbc1bc..e71299c3278361 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h @@ -42,6 +42,10 @@ struct ClangTidyGlobalOptions { /// Output warnings from certain line ranges of certain files only. /// If empty, no warnings will be filtered. std::vector<FileFilter> LineFilter; + + /// Specifies the list of paths to be searched when BasedOnStyle + /// in a .clang-format file specifies an arbitrary file to include + std::vector<std::string> StyleSearchPaths; }; /// Contains options for clang-tidy. These options may be read from diff --git a/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp b/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp index 5e7a0e65690b7a..7d76b13f0735f4 100644 --- a/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp @@ -60,7 +60,8 @@ IncludeCleanerCheck::IncludeCleanerCheck(StringRef Name, IgnoreHeaders(utils::options::parseStringList( Options.getLocalOrGlobal("IgnoreHeaders", ""))), DeduplicateFindings( - Options.getLocalOrGlobal("DeduplicateFindings", true)) { + Options.getLocalOrGlobal("DeduplicateFindings", true)), + StyleSearchPaths(Context->getGlobalOptions().StyleSearchPaths) { for (const auto &Header : IgnoreHeaders) { if (!llvm::Regex{Header}.isValid()) configurationDiag("Invalid ignore headers regex '%0'") << Header; @@ -196,7 +197,7 @@ void IncludeCleanerCheck::check(const MatchFinder::MatchResult &Result) { llvm::StringRef Code = SM->getBufferData(SM->getMainFileID()); auto FileStyle = format::getStyle(format::DefaultFormatStyle, getCurrentMainFile(), - format::DefaultFallbackStyle, Code, + StyleSearchPaths, format::DefaultFallbackStyle, Code, &SM->getFileManager().getVirtualFileSystem()); if (!FileStyle) FileStyle = format::getLLVMStyle(); diff --git a/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h b/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h index b46e409bd6f6a0..229c8b7127cbc9 100644 --- a/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h +++ b/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h @@ -47,6 +47,7 @@ class IncludeCleanerCheck : public ClangTidyCheck { std::vector<StringRef> IgnoreHeaders; // Whether emit only one finding per usage of a symbol. const bool DeduplicateFindings; + std::vector<std::string> StyleSearchPaths; llvm::SmallVector<llvm::Regex> IgnoreHeadersRegex; bool shouldIgnore(const include_cleaner::Header &H); }; diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index d42dafa8ffc362..4148c3a8d9d514 100644 --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -208,6 +208,20 @@ This option overrides the 'FormatStyle` option in cl::init("none"), cl::cat(ClangTidyCategory)); +static cl::list<std::string> StyleSearchPaths("style-search-path", desc(R"( +Directory to search for BasedOnStyle files, when the value of the +BasedOnStyle directive is not one of the predefined styles, nor +InheritFromParent. Multiple style search paths may be specified, +and will be searched in order, stopping at the first file found. +)"), + cl::value_desc("directory"), + cl::cat(ClangTidyCategory)); + +static cl::alias StyleSearchPathShort("S", cl::desc("Alias for --style-search-path"), + cl::cat(ClangTidyCategory), + cl::aliasopt(StyleSearchPaths), + cl::NotHidden); + static cl::opt<bool> ListChecks("list-checks", desc(R"( List all enabled checks and exit. Use with -checks=* to list all available checks. @@ -366,6 +380,7 @@ static void printStats(const ClangTidyStats &Stats) { static std::unique_ptr<ClangTidyOptionsProvider> createOptionsProvider( llvm::IntrusiveRefCntPtr<vfs::FileSystem> FS) { ClangTidyGlobalOptions GlobalOptions; + GlobalOptions.StyleSearchPaths = StyleSearchPaths; if (std::error_code Err = parseLineFilter(LineFilter, GlobalOptions)) { llvm::errs() << "Invalid LineFilter: " << Err.message() << "\n\nUsage:\n"; llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true); diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 06573a57554245..a169a7885c0679 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -1650,7 +1650,7 @@ ClangdLSPServer::ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, assert(!Opts.ContextProvider && "Only one of ConfigProvider and ContextProvider allowed!"); this->Opts.ContextProvider = ClangdServer::createConfiguredContextProvider( - Opts.ConfigProvider, this); + Opts.ConfigProvider, this, Opts.StyleSearchPaths); } LSPBinder Bind(this->Handlers, *this); Bind.method("initialize", this, &ClangdLSPServer::onInitialize); diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index e910a80ba0bae9..cfd60bb8279077 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -223,6 +223,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, PreambleParseForwardingFunctions(Opts.PreambleParseForwardingFunctions), ImportInsertions(Opts.ImportInsertions), PublishInactiveRegions(Opts.PublishInactiveRegions), + StyleSearchPaths(Opts.StyleSearchPaths), WorkspaceRoot(Opts.WorkspaceRoot), Transient(Opts.ImplicitCancellation ? TUScheduler::InvalidateOnUpdate : TUScheduler::NoInvalidation), @@ -335,17 +336,20 @@ std::shared_ptr<const std::string> ClangdServer::getDraft(PathRef File) const { std::function<Context(PathRef)> ClangdServer::createConfiguredContextProvider(const config::Provider *Provider, - Callbacks *Publish) { + Callbacks *Publish, + const std::vector<std::string> &StyleSearchPaths) { if (!Provider) return [](llvm::StringRef) { return Context::current().clone(); }; struct Impl { const config::Provider *Provider; ClangdServer::Callbacks *Publish; + std::vector<std::string> StyleSearchPaths; std::mutex PublishMu; - Impl(const config::Provider *Provider, ClangdServer::Callbacks *Publish) - : Provider(Provider), Publish(Publish) {} + Impl(const config::Provider *Provider, ClangdServer::Callbacks *Publish, + const std::vector<std::string> &StyleSearchPaths) + : Provider(Provider), Publish(Publish), StyleSearchPaths(StyleSearchPaths) {} Context operator()(llvm::StringRef File) { config::Params Params; @@ -378,6 +382,7 @@ ClangdServer::createConfiguredContextProvider(const config::Provider *Provider, Publish->onDiagnosticsReady(Entry.first(), /*Version=*/"", Entry.second); } + C.Style.StyleSearchPaths = StyleSearchPaths; return Context::current().derive(Config::Key, std::move(C)); } @@ -405,7 +410,7 @@ ClangdServer::createConfiguredContextProvider(const config::Provider *Provider, }; // Copyable wrapper. - return [I(std::make_shared<Impl>(Provider, Publish))](llvm::StringRef Path) { + return [I(std::make_shared<Impl>(Provider, Publish, StyleSearchPaths))](llvm::StringRef Path) { return (*I)(Path); }; } diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index a653cdb56b751b..58d5bd6f013494 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -96,7 +96,8 @@ class ClangdServer { /// (This is typically used as ClangdServer::Options::ContextProvider). static std::function<Context(PathRef)> createConfiguredContextProvider(const config::Provider *Provider, - ClangdServer::Callbacks *); + ClangdServer::Callbacks *, + const std::vector<std::string> &StyleSearchPaths); struct Options { /// To process requests asynchronously, ClangdServer spawns worker threads. @@ -142,6 +143,10 @@ class ClangdServer { /// checks will be disabled. TidyProviderRef ClangTidyProvider; + /// Specifies the list of paths to be searched when BasedOnStyle + /// in a .clang-format file specifies an arbitrary file to include + std::vector<std::string> StyleSearchPaths; + /// Clangd's workspace root. Relevant for "workspace" operations not bound /// to a particular file. /// FIXME: If not set, should use the current working directory. @@ -487,6 +492,10 @@ class ClangdServer { // When set, provides clang-tidy options for a specific file. TidyProviderRef ClangTidyProvider; + // Specifies the list of paths to be searched when BasedOnStyle + // in a .clang-format file specifies an arbitrary file to include + std::vector<std::string> StyleSearchPaths; + bool UseDirtyHeaders = false; // Whether the client supports folding only complete lines. diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h index 41143b9ebc8d27..3bb3775bf7e4fa 100644 --- a/clang-tools-extra/clangd/Config.h +++ b/clang-tools-extra/clangd/Config.h @@ -124,6 +124,10 @@ struct Config { // declarations, always spell out the whole name (with or without leading // ::). All nested namespaces are affected as well. std::vector<std::string> FullyQualifiedNamespaces; + + // Specifies the list of paths to be searched when BasedOnStyle + // in a .clang-format file specifies an arbitrary file to include + std::vector<std::string> StyleSearchPaths; } Style; /// Configures code completion feature. diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp index e34706172f0bf6..130b923bbd5efc 100644 --- a/clang-tools-extra/clangd/IncludeCleaner.cpp +++ b/clang-tools-extra/clangd/IncludeCleaner.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "IncludeCleaner.h" +#include "Config.h" #include "Diagnostics.h" #include "Headers.h" #include "ParsedAST.h" diff --git a/clang-tools-extra/clangd/SourceCode.cpp b/clang-tools-extra/clangd/SourceCode.cpp index 3af99b9db056da..3f08159fc6ffd2 100644 --- a/clang-tools-extra/clangd/SourceCode.cpp +++ b/clang-tools-extra/clangd/SourceCode.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "SourceCode.h" +#include "Config.h" #include "FuzzyMatch.h" #include "Preamble.h" #include "Protocol.h" @@ -597,7 +598,9 @@ format::FormatStyle getFormatStyleForFile(llvm::StringRef File, // language is detected. if (!FormatFile) Content = {}; + auto &Cfg = Config::current(); auto Style = format::getStyle(format::DefaultFormatStyle, File, + Cfg.Style.StyleSearchPaths, format::DefaultFallbackStyle, Content, TFS.view(/*CWD=*/std::nullopt).get()); if (!Style) { diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp index bc2eaa77a66eec..9a9b4be967087a 100644 --- a/clang-tools-extra/clangd/tool/Check.cpp +++ b/clang-tools-extra/clangd/tool/Check.cpp @@ -512,7 +512,7 @@ bool check(llvm::StringRef File, const ThreadsafeFS &TFS, config::Provider::combine({Opts.ConfigProvider, &OverrideConfig}); auto ContextProvider = ClangdServer::createConfiguredContextProvider( - ConfigProvider.get(), nullptr); + ConfigProvider.get(), nullptr, Opts.StyleSearchPaths); WithContext Ctx(ContextProvider( FakeFile.empty() ? File diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index 3a5449ac8c7994..e27e9ef0e4fcec 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -69,16 +69,20 @@ bool check(const llvm::StringRef File, const ThreadsafeFS &TFS, namespace { +using llvm::cl::alias; +using llvm::cl::aliasopt; using llvm::cl::cat; using llvm::cl::CommaSeparated; using llvm::cl::desc; using llvm::cl::Hidden; using llvm::cl::init; using llvm::cl::list; +using llvm::cl::NotHidden; using llvm::cl::opt; using llvm::cl::OptionCategory; using llvm::cl::ValueOptional; using llvm::cl::values; +using llvm::cl::value_desc; // All flags must be placed in a category, or they will be shown neither in // --help, nor --help-hidden! @@ -242,6 +246,24 @@ opt<std::string> FallbackStyle{ init(clang::format::DefaultFallbackStyle), }; +list<std::string> StyleSearchPaths{ + "style-search-path", + desc("Directory to search for BasedOnStyle files, when the value of the " + "BasedOnStyle directive is not one of the predefined styles, nor " + "InheritFromParent. Multiple style search paths may be specified, " + "and will be searched in order, stopping at the first file found."), + value_desc("directory"), + cat(Features) +}; + +alias StyleSearchPathShort{ + "S", + desc("Alias for --style-search-path"), + cat(Features), + aliasopt(StyleSearchPaths), + NotHidden +}; + opt<bool> EnableFunctionArgSnippets{ "function-arg-placeholders", cat(Features), @@ -957,6 +979,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var ClangTidyOptProvider = combine(std::move(Providers)); Opts.ClangTidyProvider = ClangTidyOptProvider; } + Opts.StyleSearchPaths = StyleSearchPaths; Opts.UseDirtyHeaders = UseDirtyHeaders; Opts.PreambleParseForwardingFunctions = PreambleParseForwardingFunctions; Opts.ImportInsertions = ImportInsertions; diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp index c324643498d94c..2ea9f06495720b 100644 --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -348,7 +348,7 @@ TEST(ClangdServerTest, RespectsConfig) { auto Opts = ClangdServer::optsForTest(); Opts.ContextProvider = - ClangdServer::createConfiguredContextProvider(&CfgProvider, nullptr); + ClangdServer::createConfiguredContextProvider(&CfgProvider, nullptr, Opts.StyleSearchPaths); OverlayCDB CDB(/*Base=*/nullptr, /*FallbackFlags=*/{}, CommandMangler::forTests()); MockFS FS; diff --git a/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp b/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp index d8a44ab9b6e12e..1b141835402293 100644 --- a/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp +++ b/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp @@ -104,10 +104,23 @@ cl::opt<bool> Remove{ cl::cat(IncludeCleaner), }; +static cl::list<std::string> StyleSearchPaths("style-search-path", cl::desc(R"( +Directory to search for BasedOnStyle files, when the value of the +BasedOnStyle directive is not one of the predefined styles, nor +InheritFromParent. Multiple style search paths may be specified, +and will be searched in order, stopping at the first file found. +)"), + cl::value_desc("directory"), + cl::cat(IncludeCleaner)); + +static cl::alias StyleSearchPathShort("S", cl::desc("Alias for --style-search-path"), + cl::cat(IncludeCleaner), cl::aliasopt(StyleSearchPaths), + cl::NotHidden); + std::atomic<unsigned> Errors = ATOMIC_VAR_INIT(0); format::FormatStyle getStyle(llvm::StringRef Filename) { - auto S = format::getStyle(format::DefaultFormatStyle, Filename, + auto S = format::getStyle(format::DefaultFormatStyle, Filename, StyleSearchPaths, format::DefaultFallbackStyle); if (!S || !S->isCpp()) { consumeError(S.takeError()); diff --git a/clang-tools-extra/unittests/clang-change-namespace/ChangeNamespaceTests.cpp b/clang-tools-extra/unittests/clang-change-namespace/ChangeNamespaceTests.cpp index 4a6352cd5975e6..08db97a4e90734 100644 --- a/clang-tools-extra/unittests/clang-change-namespace/ChangeNamespaceTests.cpp +++ b/clang-tools-extra/unittests/clang-change-namespace/ChangeNamespaceTests.cpp @@ -46,7 +46,7 @@ class ChangeNamespaceTest : public ::testing::Test { if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, {"-std=c++11"}, FileName)) return ""; - formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite); + formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite, "file", {} /*StyleSearchPatsh*/); return format(Context.getRewrittenText(ID)); } diff --git a/clang-tools-extra/unittests/clang-move/ClangMoveTests.cpp b/clang-tools-extra/unittests/clang-move/ClangMoveTests.cpp index 082779358fbfb1..06ec26bc237fba 100644 --- a/clang-tools-extra/unittests/clang-move/ClangMoveTests.cpp +++ b/clang-tools-extra/unittests/clang-move/ClangMoveTests.cpp @@ -226,7 +226,7 @@ runClangMoveOnCode(const move::MoveDefinitionSpec &Spec, CreateFiles(TestCCName, CC); std::map<std::string, tooling::Replacements> FileToReplacements; - ClangMoveContext MoveContext = {Spec, FileToReplacements, Dir.c_str(), "LLVM", + ClangMoveContext MoveContext = {Spec, FileToReplacements, Dir.c_str(), /*StyleSearchPaths*/{}, "LLVM", Reporter != nullptr}; auto Factory = std::make_unique<clang::move::ClangMoveActionFactory>( @@ -236,7 +236,7 @@ runClangMoveOnCode(const move::MoveDefinitionSpec &Spec, Factory->create(), CC, Context.InMemoryFileSystem, {"-std=c++11", "-fparse-all-comments", "-I."}, TestCCName, "clang-move", std::make_shared<PCHContainerOperations>()); - formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite, "llvm"); + formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite, "llvm", /*StyleSearchPaths*/{}); // The Key is file name, value is the new code after moving the class. std::map<std::string, std::string> Results; for (const auto &It : FileToReplacements) { diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index d8b62c7652a0f6..79b0313bb6b3c0 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -5274,9 +5274,17 @@ struct FormatStyle { friend std::error_code parseConfiguration(llvm::MemoryBufferRef Config, FormatStyle *Style, + const std::vector<std::string> &StyleSearchPaths, bool AllowUnknownOptions, llvm::SourceMgr::DiagHandlerTy DiagHandler, void *DiagHandlerCtxt); + + friend std::error_code + parseNestedConfiguration(llvm::MemoryBufferRef Config, FormatStyle *Style, + const std::vector<std::string> &StyleSearchPaths, + bool AllowUnknownOptions, + llvm::SourceMgr::DiagHandlerTy DiagHandler, + void *DiagHandlerCtxt); }; /// Returns a format style complying with the LLVM coding standards: @@ -5340,17 +5348,30 @@ bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, /// If set all diagnostics are emitted through the DiagHandler. std::error_code parseConfiguration(llvm::MemoryBufferRef Config, FormatStyle *Style, + const std::vector<std::string> &StyleSearchPaths, bool AllowUnknownOptions = false, llvm::SourceMgr::DiagHandlerTy DiagHandler = nullptr, void *DiagHandlerCtx = nullptr); /// Like above but accepts an unnamed buffer. inline std::error_code parseConfiguration(StringRef Config, FormatStyle *Style, + const std::vector<std::string> &StyleSearchPaths, bool AllowUnknownOptions = false) { return parseConfiguration(llvm::MemoryBufferRef(Config, "YAML"), Style, - AllowUnknownOptions); + StyleSearchPaths, AllowUnknownOptions); } +/// Like above but discards Style->StyleSet after resolving the desired style. +/// This allows a BasedOnStyle that references a YAML file included via StyleSearchPaths +/// (which might contain styles for multiple langages) to be consumed while maintaining +/// the invariant that a FormatStyle may belong to only one StyleSet. +std::error_code +parseNestedConfiguration(llvm::MemoryBufferRef Config, FormatStyle *Style, + const std::vector<std::string> &StyleSearchPaths, + bool AllowUnknownOptions, + llvm::SourceMgr::DiagHandlerTy DiagHandler, + void *DiagHandlerCtxt); + /// Gets configuration in a YAML string. std::string configurationAsText(const FormatStyle &Style); @@ -5493,6 +5514,8 @@ extern const char *DefaultFallbackStyle; /// above. /// \param[in] FileName Path to start search for .clang-format if ``StyleName`` /// == "file". +/// \param[in] StyleSearchPaths The sequence of directories to be searched when +/// resolving a non-built-in BasedOnStyle to a filesystem path. /// \param[in] FallbackStyle The name of a predefined style used to fallback to /// in case \p StyleName is "file" and no file can be found. /// \param[in] Code The actual code to be formatted. Used to determine the @@ -5507,7 +5530,9 @@ extern const char *DefaultFallbackStyle; /// "file" and no file is found, returns ``FallbackStyle``. If no style could be /// determined, returns an Error. Expected<FormatStyle> -getStyle(StringRef StyleName, StringRef FileName, StringRef FallbackStyle, +getStyle(StringRef StyleName, StringRef FileName, + const std::vector<std::string> &StyleSearchPaths, + StringRef FallbackStyle, StringRef Code = "", llvm::vfs::FileSystem *FS = nullptr, bool AllowUnknownOptions = false, llvm::SourceMgr::DiagHandlerTy DiagHandler = nullptr); diff --git a/clang/include/clang/Tooling/Refactoring.h b/clang/include/clang/Tooling/Refactoring.h index b82b09f0f92dbd..f505300f4d4640 100644 --- a/clang/include/clang/Tooling/Refactoring.h +++ b/clang/include/clang/Tooling/Refactoring.h @@ -87,11 +87,13 @@ class RefactoringTool : public ClangTool { /// \param[in] Rewrite The `Rewritter` to apply replacements on. /// \param[in] Style The style name used for reformatting. See ```getStyle``` in /// "include/clang/Format/Format.h" for all possible style forms. +/// \param[in] StyleSearchPaths A list of directories to search for clang-format +/// files referenced in the BasedOnStyle directive. /// /// \returns true if all replacements applied and formatted. false otherwise. bool formatAndApplyAllReplacements( const std::map<std::string, Replacements> &FileToReplaces, - Rewriter &Rewrite, StringRef Style = "file"); + Rewriter &Rewrite, StringRef Style, const std::vector<std::string>& StyleSearchPaths); } // end namespace tooling } // end namespace clang diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index d2463b892fbb96..d6bc46ffab66bc 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -853,8 +853,21 @@ template <> struct MappingTraits<FormatStyle> { FormatStyle::LanguageKind Language = ((FormatStyle *)IO.getContext())->Language; if (!getPredefinedStyle(BasedOnStyle, Language, &Style)) { - IO.setError(Twine("Unknown value for BasedOnStyle: ", BasedOnStyle)); - return; + // OK, not a predefined style. See if this is an includable file. + SourceMgr& SrcMgr = IO.getSourceMgr(); + std::string IncludedFile; + ErrorOr<std::unique_ptr<MemoryBuffer>> IncFileOrError = SrcMgr.OpenIncludeFile(BasedOnStyle.str(), IncludedFile); + if (!IncFileOrError) { + IO.setError(Twine("BasedOnStyle value is not a predefined style nor a file relative to the style search path: ", BasedOnStyle)); + return; + } + Style.Language = Language; + if (auto EC = parseNestedConfiguration((*IncFileOrError)->getMemBufferRef(), &Style, + SrcMgr.getIncludeDirs(), IO.allowUnknownKeys(), + SrcMgr.getDiagHandler(), SrcMgr.getDiagContext())) { + IO.setError(Twine(EC.message())); + return; + } } Style.Language = OldLanguage; } @@ -2033,7 +2046,9 @@ ParseError validateQualifierOrder(FormatStyle *Style) { } std::error_code parseConfiguration(llvm::MemoryBufferRef Config, - FormatStyle *Style, bool AllowUnknownOptions, + FormatStyle *Style, + const std::vector<std::string> &StyleSearchPaths, + bool AllowUnknownOptions, llvm::SourceMgr::DiagHandlerTy DiagHandler, void *DiagHandlerCtxt) { assert(Style); @@ -2051,6 +2066,7 @@ std::error_code parseConfiguration(llvm::MemoryBufferRef Config, // base style. Input.setContext(Style); Input.setAllowUnknownKeys(AllowUnknownOptions); + Input.getSourceMgr().setIncludeDirs(StyleSearchPaths); Input >> Styles; if (Input.error()) return Input.error(); @@ -2098,6 +2114,18 @@ std::error_code parseConfiguration(llvm::MemoryBufferRef Config, return make_error_code(ParseError::Success); } +std::error_code parseNestedConfiguration(llvm::MemoryBufferRef Config, + FormatStyle *Style, + const std::vector<std::string> &StyleSearchPaths, + bool AllowUnknownOptions, + llvm::SourceMgr::DiagHandlerTy DiagHandler, + void *DiagHandlerCtxt) { + auto EC = parseConfiguration(Config, Style, StyleSearchPaths, AllowUnknownOptions, DiagHandler, DiagHandlerCtxt); + if (!EC) + Style->StyleSet.Clear(); + return EC; +} + std::string configurationAsText(const FormatStyle &Style) { std::string Text; llvm::raw_string_ostream Stream(Text); @@ -3991,13 +4019,15 @@ const char *DefaultFallbackStyle = "LLVM"; llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> loadAndParseConfigFile(StringRef ConfigFile, llvm::vfs::FileSystem *FS, - FormatStyle *Style, bool AllowUnknownOptions, + FormatStyle *Style, + const std::vector<std::string> &StyleSearchPaths, + bool AllowUnknownOptions, llvm::SourceMgr::DiagHandlerTy DiagHandler) { llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text = FS->getBufferForFile(ConfigFile.str()); if (auto EC = Text.getError()) return EC; - if (auto EC = parseConfiguration(*Text.get(), Style, AllowUnknownOptions, + if (auto EC = parseConfiguration(*Text.get(), Style, StyleSearchPaths, AllowUnknownOptions, DiagHandler)) { return EC; } @@ -4005,6 +4035,7 @@ loadAndParseConfigFile(StringRef ConfigFile, llvm::vfs::FileSystem *FS, } Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, + const std::vector<std::string> &StyleSearchPaths, StringRef FallbackStyleName, StringRef Code, llvm::vfs::FileSystem *FS, bool AllowUnknownOptions, @@ -4021,7 +4052,7 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, StringRef Source = "<command-line>"; if (std::error_code ec = parseConfiguration(llvm::MemoryBufferRef(StyleName, Source), &Style, - AllowUnknownOptions, DiagHandler)) { + StyleSearchPaths, AllowUnknownOptions, DiagHandler)) { return make_string_error("Error parsing -style: " + ec.message()); } @@ -4041,8 +4072,8 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, StyleName.starts_with_insensitive("file:")) { auto ConfigFile = StyleName.substr(5); llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text = - loadAndParseConfigFile(ConfigFile, FS, &Style, AllowUnknownOptions, - DiagHandler); + loadAndParseConfigFile(ConfigFile, FS, &Style, StyleSearchPaths, + AllowUnknownOptions, DiagHandler); if (auto EC = Text.getError()) { return make_string_error("Error reading " + ConfigFile + ": " + EC.message()); @@ -4082,7 +4113,7 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, auto applyChildFormatTexts = [&](FormatStyle *Style) { for (const auto &MemBuf : llvm::reverse(ChildFormatTextToApply)) { auto EC = - parseConfiguration(*MemBuf, Style, AllowUnknownOptions, + parseConfiguration(*MemBuf, Style, StyleSearchPaths, AllowUnknownOptions, DiagHandler ? DiagHandler : dropDiagnosticHandler); // It was already correctly parsed. assert(!EC); @@ -4117,7 +4148,7 @@ Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName, } llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text = - loadAndParseConfigFile(ConfigFile, FS, &Style, AllowUnknownOptions, + loadAndParseConfigFile(ConfigFile, FS, &Style, StyleSearchPaths, AllowUnknownOptions, DiagHandler); if (auto EC = Text.getError()) { if (EC != ParseError::Unsuitable) { diff --git a/clang/lib/Tooling/Refactoring.cpp b/clang/lib/Tooling/Refactoring.cpp index 961fc1c1801547..8221957648f88a 100644 --- a/clang/lib/Tooling/Refactoring.cpp +++ b/clang/lib/Tooling/Refactoring.cpp @@ -68,7 +68,7 @@ int RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) { bool formatAndApplyAllReplacements( const std::map<std::string, Replacements> &FileToReplaces, - Rewriter &Rewrite, StringRef Style) { + Rewriter &Rewrite, StringRef Style, const std::vector<std::string>& StyleSearchPaths) { SourceManager &SM = Rewrite.getSourceMgr(); FileManager &Files = SM.getFileManager(); @@ -82,7 +82,7 @@ bool formatAndApplyAllReplacements( FileID ID = SM.getOrCreateFileID(Entry, SrcMgr::C_User); StringRef Code = SM.getBufferData(ID); - auto CurStyle = format::getStyle(Style, FilePath, "LLVM"); + auto CurStyle = format::getStyle(Style, FilePath, StyleSearchPaths, "LLVM"); if (!CurStyle) { llvm::errs() << llvm::toString(CurStyle.takeError()) << "\n"; return false; diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp index 6aed46328f3469..0cca1e1c4c4d15 100644 --- a/clang/tools/clang-format/ClangFormat.cpp +++ b/clang/tools/clang-format/ClangFormat.cpp @@ -172,6 +172,24 @@ static cl::opt<bool> cl::desc("If set, changes formatting warnings to errors"), cl::cat(ClangFormatCategory)); +static cl::list<std::string> + StyleSearchPaths( + "style-search-path", + cl::desc("Directory to search for BasedOnStyle files, when the value of the\n" + "BasedOnStyle directive is not one of the predefined styles, nor\n" + "InheritFromParent. Multiple style search paths may be specified,\n" + "and will be searched in order, stopping at the first file found."), + cl::value_desc("directory"), + cl::cat(ClangFormatCategory)); + +static cl::alias + StyleSearchPathShort( + "S", + cl::desc("Alias for --style-search-path"), + cl::cat(ClangFormatCategory), + cl::aliasopt(StyleSearchPaths), + cl::NotHidden); + namespace { enum class WNoError { Unknown }; } @@ -452,7 +470,7 @@ static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) { } Expected<FormatStyle> FormatStyle = - getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(), + getStyle(Style, AssumedFileName, StyleSearchPaths, FallbackStyle, Code->getBuffer(), nullptr, WNoErrorList.isSet(WNoError::Unknown)); if (!FormatStyle) { llvm::errs() << toString(FormatStyle.takeError()) << "\n"; @@ -573,6 +591,7 @@ static int dumpConfig() { Expected<clang::format::FormatStyle> FormatStyle = clang::format::getStyle( Style, FileNames.empty() || FileNames[0] == "-" ? AssumeFileName : FileNames[0], + StyleSearchPaths, FallbackStyle, Code ? Code->getBuffer() : ""); if (!FormatStyle) { llvm::errs() << toString(FormatStyle.takeError()) << "\n"; diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index b8bdfaaa74e10e..31b99b640fb5b0 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -80,27 +80,27 @@ TEST(ConfigParseTest, GetsCorrectBasedOnStyle) { Styles[0] = getGoogleStyle(); Styles[1] = getLLVMStyle(); - EXPECT_EQ(0, parseConfiguration("BasedOnStyle: Google", &Styles[1]).value()); + EXPECT_EQ(0, parseConfiguration("BasedOnStyle: Google", &Styles[1], /*StyleSearchPaths*/{}).value()); EXPECT_ALL_STYLES_EQUAL(Styles); Styles.resize(5); Styles[0] = getGoogleStyle(FormatStyle::LK_JavaScript); Styles[1] = getLLVMStyle(); Styles[1].Language = FormatStyle::LK_JavaScript; - EXPECT_EQ(0, parseConfiguration("BasedOnStyle: Google", &Styles[1]).value()); + EXPECT_EQ(0, parseConfiguration("BasedOnStyle: Google", &Styles[1], /*StyleSearchPaths*/{}).value()); Styles[2] = getLLVMStyle(); Styles[2].Language = FormatStyle::LK_JavaScript; EXPECT_EQ(0, parseConfiguration("Language: JavaScript\n" "BasedOnStyle: Google", - &Styles[2]) + &Styles[2], /*StyleSearchPaths*/{}) .value()); Styles[3] = getLLVMStyle(); Styles[3].Language = FormatStyle::LK_JavaScript; EXPECT_EQ(0, parseConfiguration("BasedOnStyle: Google\n" "Language: JavaScript", - &Styles[3]) + &Styles[3], /*StyleSearchPaths*/{}) .value()); Styles[4] = getLLVMStyle(); @@ -111,16 +111,18 @@ TEST(ConfigParseTest, GetsCorrectBasedOnStyle) { "---\n" "BasedOnStyle: Google\n" "Language: JavaScript", - &Styles[4]) + &Styles[4], /*StyleSearchPaths*/{}) .value()); EXPECT_ALL_STYLES_EQUAL(Styles); } #define CHECK_PARSE_BOOL_FIELD(FIELD, CONFIG_NAME) \ Style.FIELD = false; \ - EXPECT_EQ(0, parseConfiguration(CONFIG_NAME ": true", &Style).value()); \ + EXPECT_EQ(0, parseConfiguration(CONFIG_NAME ": true", &Style, \ + /*StyleSearchPaths*/{}).value()); \ EXPECT_TRUE(Style.FIELD); \ - EXPECT_EQ(0, parseConfiguration(CONFIG_NAME ": false", &Style).value()); \ + EXPECT_EQ(0, parseConfiguration(CONFIG_NAME ": false", &Style, \ + /*StyleSearchPaths*/{}).value()); \ EXPECT_FALSE(Style.FIELD) #define CHECK_PARSE_BOOL(FIELD) CHECK_PARSE_BOOL_FIELD(FIELD, #FIELD) @@ -128,11 +130,13 @@ TEST(ConfigParseTest, GetsCorrectBasedOnStyle) { #define CHECK_PARSE_NESTED_BOOL_FIELD(STRUCT, FIELD, CONFIG_NAME) \ Style.STRUCT.FIELD = false; \ EXPECT_EQ(0, \ - parseConfiguration(#STRUCT ":\n " CONFIG_NAME ": true", &Style) \ + parseConfiguration(#STRUCT ":\n " CONFIG_NAME ": true", \ + &Style, /*StyleSearchPaths*/{}) \ .value()); \ EXPECT_TRUE(Style.STRUCT.FIELD); \ EXPECT_EQ(0, \ - parseConfiguration(#STRUCT ":\n " CONFIG_NAME ": false", &Style) \ + parseConfiguration(#STRUCT ":\n " CONFIG_NAME ": false", \ + &Style, /*StyleSearchPaths*/{}) \ .value()); \ EXPECT_FALSE(Style.STRUCT.FIELD) @@ -141,12 +145,13 @@ TEST(ConfigParseTest, GetsCorrectBasedOnStyle) { #define CHECK_PARSE(TEXT, FIELD, VALUE) \ EXPECT_NE(VALUE, Style.FIELD) << "Initial value already the same!"; \ - EXPECT_EQ(0, parseConfiguration(TEXT, &Style).value()); \ + EXPECT_EQ(0, parseConfiguration(TEXT, &Style, \ + /*StyleSearchPaths*/{}).value()); \ EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!" - #define CHECK_PARSE_NESTED_VALUE(TEXT, STRUCT, FIELD, VALUE) \ EXPECT_NE(VALUE, Style.STRUCT.FIELD) << "Initial value already the same!"; \ - EXPECT_EQ(0, parseConfiguration(#STRUCT ":\n " TEXT, &Style).value()); \ + EXPECT_EQ(0, parseConfiguration(#STRUCT ":\n " TEXT, &Style, \ + /*StyleSearchPaths*/{}).value()); \ EXPECT_EQ(VALUE, Style.STRUCT.FIELD) << "Unexpected value after parsing!" TEST(ConfigParseTest, ParsesConfigurationBools) { @@ -1065,13 +1070,13 @@ TEST(ConfigParseTest, ParsesConfigurationWithLanguages) { IndentWidth, 12u); EXPECT_EQ(parseConfiguration("Language: JavaScript\n" "IndentWidth: 34", - &Style), + &Style, /*StyleSearchPaths*/{}), ParseError::Unsuitable); FormatStyle BinPackedTCS = {}; BinPackedTCS.Language = FormatStyle::LK_JavaScript; EXPECT_EQ(parseConfiguration("BinPackArguments: true\n" "InsertTrailingCommas: Wrapped", - &BinPackedTCS), + &BinPackedTCS, /*StyleSearchPaths*/{}), ParseError::BinPackTrailingCommaConflict); EXPECT_EQ(12u, Style.IndentWidth); CHECK_PARSE("IndentWidth: 56", IndentWidth, 56u); @@ -1084,7 +1089,7 @@ TEST(ConfigParseTest, ParsesConfigurationWithLanguages) { CHECK_PARSE("IndentWidth: 23", IndentWidth, 23u); EXPECT_EQ(parseConfiguration("Language: Cpp\n" "IndentWidth: 34", - &Style), + &Style, /*StyleSearchPaths*/{}), ParseError::Unsuitable); EXPECT_EQ(23u, Style.IndentWidth); CHECK_PARSE("IndentWidth: 56", IndentWidth, 56u); @@ -1136,7 +1141,7 @@ TEST(ConfigParseTest, ParsesConfigurationWithLanguages) { "BreakBeforeBraces: Stroustrup\n" "TabWidth: 789\n" "...\n", - &Style)); + &Style, /*StyleSearchPaths*/{})); EXPECT_EQ(123u, Style.ColumnLimit); EXPECT_EQ(456u, Style.IndentWidth); EXPECT_EQ(FormatStyle::BS_Stroustrup, Style.BreakBeforeBraces); @@ -1148,7 +1153,7 @@ TEST(ConfigParseTest, ParsesConfigurationWithLanguages) { "---\n" "IndentWidth: 78\n" "...\n", - &Style), + &Style, /*StyleSearchPaths*/{}), ParseError::Error); EXPECT_EQ(parseConfiguration("---\n" "Language: JavaScript\n" @@ -1157,7 +1162,7 @@ TEST(ConfigParseTest, ParsesConfigurationWithLanguages) { "Language: JavaScript\n" "IndentWidth: 78\n" "...\n", - &Style), + &Style, /*StyleSearchPaths*/{}), ParseError::Error); EXPECT_EQ(FormatStyle::LK_Cpp, Style.Language); @@ -1184,7 +1189,7 @@ TEST(ConfigParseTest, UsesLanguageForBasedOnStyle) { FormatStyle Style = {}; Style.Language = FormatStyle::LK_JavaScript; Style.BreakBeforeTernaryOperators = true; - EXPECT_EQ(0, parseConfiguration("BasedOnStyle: Google", &Style).value()); + EXPECT_EQ(0, parseConfiguration("BasedOnStyle: Google", &Style, /*StyleSearchPaths*/{}).value()); EXPECT_FALSE(Style.BreakBeforeTernaryOperators); Style.BreakBeforeTernaryOperators = true; @@ -1194,7 +1199,7 @@ TEST(ConfigParseTest, UsesLanguageForBasedOnStyle) { "Language: JavaScript\n" "IndentWidth: 76\n" "...\n", - &Style) + &Style, /*StyleSearchPaths*/{}) .value()); EXPECT_FALSE(Style.BreakBeforeTernaryOperators); EXPECT_EQ(76u, Style.IndentWidth); @@ -1206,13 +1211,13 @@ TEST(ConfigParseTest, ConfigurationRoundTripTest) { std::string YAML = configurationAsText(Style); FormatStyle ParsedStyle = {}; ParsedStyle.Language = FormatStyle::LK_Cpp; - EXPECT_EQ(0, parseConfiguration(YAML, &ParsedStyle).value()); + EXPECT_EQ(0, parseConfiguration(YAML, &ParsedStyle, /*StyleSearchPaths*/{}).value()); EXPECT_EQ(Style, ParsedStyle); } TEST(ConfigParseTest, GetStyleWithEmptyFileName) { llvm::vfs::InMemoryFileSystem FS; - auto Style1 = getStyle("file", "", "Google", "", &FS); + auto Style1 = getStyle("file", "", /*StyleSearchPaths*/{}, "Google", "", &FS); ASSERT_TRUE((bool)Style1); ASSERT_EQ(*Style1, getGoogleStyle()); } @@ -1225,19 +1230,19 @@ TEST(ConfigParseTest, GetStyleOfFile) { llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: LLVM"))); ASSERT_TRUE( FS.addFile("/a/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;"))); - auto Style1 = getStyle("file", "/a/.clang-format", "Google", "", &FS); + auto Style1 = getStyle("file", "/a/.clang-format", /*StyleSearchPaths*/{}, "Google", "", &FS); ASSERT_TRUE((bool)Style1); ASSERT_EQ(*Style1, getLLVMStyle()); // Test 2.1: fallback to default. ASSERT_TRUE( FS.addFile("/b/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;"))); - auto Style2 = getStyle("file", "/b/test.cpp", "Mozilla", "", &FS); + auto Style2 = getStyle("file", "/b/test.cpp", /*StyleSearchPaths*/{}, "Mozilla", "", &FS); ASSERT_TRUE((bool)Style2); ASSERT_EQ(*Style2, getMozillaStyle()); // Test 2.2: no format on 'none' fallback style. - Style2 = getStyle("file", "/b/test.cpp", "none", "", &FS); + Style2 = getStyle("file", "/b/test.cpp", /*StyleSearchPaths*/{}, "none", "", &FS); ASSERT_TRUE((bool)Style2); ASSERT_EQ(*Style2, getNoStyle()); @@ -1245,12 +1250,12 @@ TEST(ConfigParseTest, GetStyleOfFile) { // 'none'. ASSERT_TRUE(FS.addFile("/b/.clang-format", 0, llvm::MemoryBuffer::getMemBuffer("IndentWidth: 2"))); - Style2 = getStyle("file", "/b/test.cpp", "none", "", &FS); + Style2 = getStyle("file", "/b/test.cpp", /*StyleSearchPaths*/{}, "none", "", &FS); ASSERT_TRUE((bool)Style2); ASSERT_EQ(*Style2, getLLVMStyle()); // Test 2.4: format if yaml with no based style, while fallback is 'none'. - Style2 = getStyle("{}", "a.h", "none", "", &FS); + Style2 = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "", &FS); ASSERT_TRUE((bool)Style2); ASSERT_EQ(*Style2, getLLVMStyle()); @@ -1260,23 +1265,24 @@ TEST(ConfigParseTest, GetStyleOfFile) { llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: Google"))); ASSERT_TRUE(FS.addFile("/c/sub/sub/sub/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;"))); - auto Style3 = getStyle("file", "/c/sub/sub/sub/test.cpp", "LLVM", "", &FS); + auto Style3 = getStyle("file", "/c/sub/sub/sub/test.cpp", /*StyleSearchPaths*/{}, "LLVM", "", &FS); ASSERT_TRUE((bool)Style3); ASSERT_EQ(*Style3, getGoogleStyle()); // Test 4: error on invalid fallback style - auto Style4 = getStyle("file", "a.h", "KungFu", "", &FS); + auto Style4 = getStyle("file", "a.h", /*StyleSearchPaths*/{}, "KungFu", "", &FS); ASSERT_FALSE((bool)Style4); llvm::consumeError(Style4.takeError()); // Test 5: error on invalid yaml on command line auto Style5 = getStyle("{invalid_key=invalid_value}", "a.h", "LLVM", "", &FS, - /*AllowUnknownOptions=*/false, dropDiagnosticHandler); + /*StyleSearchPaths*/{}, /*AllowUnknownOptions=*/false, + dropDiagnosticHandler); ASSERT_FALSE((bool)Style5); llvm::consumeError(Style5.takeError()); // Test 6: error on invalid style - auto Style6 = getStyle("KungFu", "a.h", "LLVM", "", &FS); + auto Style6 = getStyle("KungFu", "a.h", /*StyleSearchPaths*/{}, "LLVM", "", &FS); ASSERT_FALSE((bool)Style6); llvm::consumeError(Style6.takeError()); @@ -1288,16 +1294,18 @@ TEST(ConfigParseTest, GetStyleOfFile) { ASSERT_TRUE( FS.addFile("/d/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;"))); auto Style7a = getStyle("file", "/d/.clang-format", "LLVM", "", &FS, - /*AllowUnknownOptions=*/false, dropDiagnosticHandler); + /*StyleSearchPaths*/{}, /*AllowUnknownOptions=*/false, + dropDiagnosticHandler); ASSERT_FALSE((bool)Style7a); llvm::consumeError(Style7a.takeError()); auto Style7b = getStyle("file", "/d/.clang-format", "LLVM", "", &FS, - /*AllowUnknownOptions=*/true, dropDiagnosticHandler); + /*StyleSearchPaths*/{}, /*AllowUnknownOptions=*/true, + dropDiagnosticHandler); ASSERT_TRUE((bool)Style7b); // Test 8: inferred per-language defaults apply. - auto StyleTd = getStyle("file", "x.td", "llvm", "", &FS); + auto StyleTd = getStyle("file", "x.td", /*StyleSearchPaths*/{}, "llvm", "", &FS); ASSERT_TRUE((bool)StyleTd); ASSERT_EQ(*StyleTd, getLLVMStyle(FormatStyle::LK_TableGen)); @@ -1309,7 +1317,7 @@ TEST(ConfigParseTest, GetStyleOfFile) { "ColumnLimit: 20"))); ASSERT_TRUE(FS.addFile("/e/sub/code.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;"))); - auto Style9 = getStyle("file", "/e/sub/code.cpp", "none", "", &FS); + auto Style9 = getStyle("file", "/e/sub/code.cpp", /*StyleSearchPaths*/{}, "none", "", &FS); ASSERT_TRUE(static_cast<bool>(Style9)); ASSERT_EQ(*Style9, [] { auto Style = getNoStyle(); @@ -1330,7 +1338,7 @@ TEST(ConfigParseTest, GetStyleOfFile) { NonDefaultWhiteSpaceMacros[1] = "BAR"; ASSERT_NE(Style9->WhitespaceSensitiveMacros, NonDefaultWhiteSpaceMacros); - Style9 = getStyle("file", "/e/sub/sub/code.cpp", "none", "", &FS); + Style9 = getStyle("file", "/e/sub/sub/code.cpp", /*StyleSearchPaths*/{}, "none", "", &FS); ASSERT_TRUE(static_cast<bool>(Style9)); ASSERT_EQ(*Style9, [&NonDefaultWhiteSpaceMacros] { auto Style = getNoStyle(); @@ -1340,7 +1348,7 @@ TEST(ConfigParseTest, GetStyleOfFile) { }()); // Test 9.2: with LLVM fallback style - Style9 = getStyle("file", "/e/sub/code.cpp", "LLVM", "", &FS); + Style9 = getStyle("file", "/e/sub/code.cpp", /*StyleSearchPaths*/{}, "LLVM", "", &FS); ASSERT_TRUE(static_cast<bool>(Style9)); ASSERT_EQ(*Style9, [] { auto Style = getLLVMStyle(); @@ -1353,7 +1361,7 @@ TEST(ConfigParseTest, GetStyleOfFile) { FS.addFile("/e/.clang-format", 0, llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: Google\n" "UseTab: Always"))); - Style9 = getStyle("file", "/e/sub/code.cpp", "none", "", &FS); + Style9 = getStyle("file", "/e/sub/code.cpp", /*StyleSearchPaths*/{}, "none", "", &FS); ASSERT_TRUE(static_cast<bool>(Style9)); ASSERT_EQ(*Style9, [] { auto Style = getGoogleStyle(); @@ -1372,26 +1380,26 @@ TEST(ConfigParseTest, GetStyleOfFile) { }(); ASSERT_NE(Style9->WhitespaceSensitiveMacros, NonDefaultWhiteSpaceMacros); - Style9 = getStyle("file", "/e/sub/sub/code.cpp", "none", "", &FS); + Style9 = getStyle("file", "/e/sub/sub/code.cpp", /*StyleSearchPaths*/{}, "none", "", &FS); ASSERT_TRUE(static_cast<bool>(Style9)); ASSERT_EQ(*Style9, SubSubStyle); // Test 9.5: use InheritParentConfig as style name Style9 = - getStyle("inheritparentconfig", "/e/sub/sub/code.cpp", "none", "", &FS); + getStyle("inheritparentconfig", "/e/sub/sub/code.cpp", /*StyleSearchPaths*/{}, "none", "", &FS); ASSERT_TRUE(static_cast<bool>(Style9)); ASSERT_EQ(*Style9, SubSubStyle); // Test 9.6: use command line style with inheritance Style9 = getStyle("{BasedOnStyle: InheritParentConfig}", - "/e/sub/sub/code.cpp", "none", "", &FS); + "/e/sub/sub/code.cpp", /*StyleSearchPaths*/{}, "none", "", &FS); ASSERT_TRUE(static_cast<bool>(Style9)); ASSERT_EQ(*Style9, SubSubStyle); // Test 9.7: use command line style with inheritance and own config Style9 = getStyle("{BasedOnStyle: InheritParentConfig, " "WhitespaceSensitiveMacros: ['FOO', 'BAR']}", - "/e/sub/code.cpp", "none", "", &FS); + "/e/sub/code.cpp", /*StyleSearchPaths*/{}, "none", "", &FS); ASSERT_TRUE(static_cast<bool>(Style9)); ASSERT_EQ(*Style9, SubSubStyle); @@ -1403,7 +1411,7 @@ TEST(ConfigParseTest, GetStyleOfFile) { llvm::MemoryBuffer::getMemBuffer( "BasedOnStyle: InheritParentConfig\nIndentWidth: 7"))); // Make sure we do not use the fallback style - Style9 = getStyle("file", "/e/withoutbase/code.cpp", "google", "", &FS); + Style9 = getStyle("file", "/e/withoutbase/code.cpp", /*StyleSearchPaths*/{}, "google", "", &FS); ASSERT_TRUE(static_cast<bool>(Style9)); ASSERT_EQ(*Style9, [] { auto Style = getLLVMStyle(); @@ -1411,7 +1419,7 @@ TEST(ConfigParseTest, GetStyleOfFile) { return Style; }()); - Style9 = getStyle("file", "/e/withoutbase/sub/code.cpp", "google", "", &FS); + Style9 = getStyle("file", "/e/withoutbase/sub/code.cpp", /*StyleSearchPaths*/{}, "google", "", &FS); ASSERT_TRUE(static_cast<bool>(Style9)); ASSERT_EQ(*Style9, [] { auto Style = getLLVMStyle(); @@ -1421,7 +1429,7 @@ TEST(ConfigParseTest, GetStyleOfFile) { }()); // Test 9.9: use inheritance from a specific config file. - Style9 = getStyle("file:/e/sub/sub/.clang-format", "/e/sub/sub/code.cpp", + Style9 = getStyle("file:/e/sub/sub/.clang-format", "/e/sub/sub/code.cpp", /*StyleSearchPaths*/{}, "none", "", &FS); ASSERT_TRUE(static_cast<bool>(Style9)); ASSERT_EQ(*Style9, SubSubStyle); @@ -1439,7 +1447,7 @@ TEST(ConfigParseTest, GetStyleOfSpecificFile) { ASSERT_TRUE(FS.addFile("/e/sub/sub/sub/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;"))); auto Style = getStyle("file:/e/explicit.clang-format", - "/e/sub/sub/sub/test.cpp", "LLVM", "", &FS); + "/e/sub/sub/sub/test.cpp", /*StyleSearchPaths*/{}, "LLVM", "", &FS); ASSERT_TRUE(static_cast<bool>(Style)); ASSERT_EQ(*Style, getGoogleStyle()); @@ -1448,12 +1456,12 @@ TEST(ConfigParseTest, GetStyleOfSpecificFile) { FS.addFile("../../e/explicit.clang-format", 0, llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: Google"))); Style = getStyle("file:../../e/explicit.clang-format", - "/e/sub/sub/sub/test.cpp", "LLVM", "", &FS); + "/e/sub/sub/sub/test.cpp", /*StyleSearchPaths*/{}, "LLVM", "", &FS); ASSERT_TRUE(static_cast<bool>(Style)); ASSERT_EQ(*Style, getGoogleStyle()); // Specify path to a format file that does not exist. - Style = getStyle("file:/e/missing.clang-format", "/e/sub/sub/sub/test.cpp", + Style = getStyle("file:/e/missing.clang-format", "/e/sub/sub/sub/test.cpp", /*StyleSearchPaths*/{}, "LLVM", "", &FS); ASSERT_FALSE(static_cast<bool>(Style)); llvm::consumeError(Style.takeError()); @@ -1477,7 +1485,7 @@ TEST(ConfigParseTest, GetStyleOfSpecificFile) { CodeFileTest.close(); std::string format_file_arg = std::string("file:") + FormatFilePath.c_str(); - Style = getStyle(format_file_arg, TestFilePath, "LLVM", "", nullptr); + Style = getStyle(format_file_arg, TestFilePath, /*StyleSearchPaths*/{}, "LLVM", "", nullptr); llvm::sys::fs::remove(FormatFilePath.c_str()); llvm::sys::fs::remove(TestFilePath.c_str()); diff --git a/clang/unittests/Format/FormatTestObjC.cpp b/clang/unittests/Format/FormatTestObjC.cpp index 9b6f0c396d4dbf..c692dc0cd8ac87 100644 --- a/clang/unittests/Format/FormatTestObjC.cpp +++ b/clang/unittests/Format/FormatTestObjC.cpp @@ -32,7 +32,7 @@ class FormatTestObjC : public FormatTestBase { #define verifyFormat(...) _verifyFormat(__FILE__, __LINE__, __VA_ARGS__) TEST(FormatTestObjCStyle, DetectsObjCInStdin) { - auto Style = getStyle("LLVM", "<stdin>", "none", + auto Style = getStyle("LLVM", "<stdin>", /*StyleSearchPaths*/{}, "none", "@interface\n" "- (id)init;"); ASSERT_TRUE((bool)Style); @@ -40,67 +40,67 @@ TEST(FormatTestObjCStyle, DetectsObjCInStdin) { } TEST(FormatTestObjCStyle, DetectsObjCInHeaders) { - auto Style = getStyle("LLVM", "a.h", "none", + auto Style = getStyle("LLVM", "a.h", /*StyleSearchPaths*/{}, "none", "@interface\n" "- (id)init;"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("LLVM", "a.h", "none", + Style = getStyle("LLVM", "a.h", /*StyleSearchPaths*/{}, "none", "@interface\n" "+ (id)init;"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("LLVM", "a.h", "none", + Style = getStyle("LLVM", "a.h", /*StyleSearchPaths*/{}, "none", "@interface\n" "@end\n" "//comment"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("LLVM", "a.h", "none", + Style = getStyle("LLVM", "a.h", /*StyleSearchPaths*/{}, "none", "@interface\n" "@end //comment"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); // No recognizable ObjC. - Style = getStyle("LLVM", "a.h", "none", "void f() {}"); + Style = getStyle("LLVM", "a.h", /*StyleSearchPaths*/{}, "none", "void f() {}"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_Cpp, Style->Language); - Style = getStyle("{}", "a.h", "none", "@interface Foo\n@end"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "@interface Foo\n@end"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "const int interface = 1;\nconst int end = 2;"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_Cpp, Style->Language); - Style = getStyle("{}", "a.h", "none", "@protocol Foo\n@end"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "@protocol Foo\n@end"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "const int protocol = 1;\nconst int end = 2;"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_Cpp, Style->Language); - Style = getStyle("{}", "a.h", "none", "typedef NS_ENUM(int, Foo) {};"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "typedef NS_ENUM(int, Foo) {};"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", "typedef NS_CLOSED_ENUM(int, Foo) {};"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "typedef NS_CLOSED_ENUM(int, Foo) {};"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", "typedef NS_ERROR_ENUM(int, Foo) {};"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "typedef NS_ERROR_ENUM(int, Foo) {};"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", R"objc( + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", R"objc( NS_ASSUME_NONNULL_BEGIN extern int i; NS_ASSUME_NONNULL_END @@ -108,71 +108,71 @@ NS_ASSUME_NONNULL_END ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", R"objc( + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", R"objc( FOUNDATION_EXTERN void DoStuff(void); )objc"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", R"objc( + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", R"objc( FOUNDATION_EXPORT void DoStuff(void); )objc"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", "enum Foo {};"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "enum Foo {};"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_Cpp, Style->Language); - Style = getStyle("{}", "a.h", "none", "inline void Foo() { Log(@\"Foo\"); }"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "inline void Foo() { Log(@\"Foo\"); }"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", "inline void Foo() { Log(\"Foo\"); }"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "inline void Foo() { Log(\"Foo\"); }"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_Cpp, Style->Language); Style = - getStyle("{}", "a.h", "none", "inline void Foo() { id = @[1, 2, 3]; }"); + getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "inline void Foo() { id = @[1, 2, 3]; }"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "inline void Foo() { id foo = @{1: 2, 3: 4, 5: 6}; }"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "inline void Foo() { int foo[] = {1, 2, 3}; }"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_Cpp, Style->Language); // ObjC characteristic types. - Style = getStyle("{}", "a.h", "none", "extern NSString *kFoo;"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "extern NSString *kFoo;"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", "extern NSInteger Foo();"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "extern NSInteger Foo();"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", "NSObject *Foo();"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "NSObject *Foo();"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); - Style = getStyle("{}", "a.h", "none", "NSSet *Foo();"); + Style = getStyle("{}", "a.h", /*StyleSearchPaths*/{}, "none", "NSSet *Foo();"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); } TEST(FormatTestObjCStyle, AvoidDetectingDesignatedInitializersAsObjCInHeaders) { - auto Style = getStyle("LLVM", "a.h", "none", + auto Style = getStyle("LLVM", "a.h", /*StyleSearchPaths*/{}, "none", "static const char *names[] = {[0] = \"foo\",\n" "[kBar] = \"bar\"};"); ASSERT_TRUE((bool)Style); EXPECT_EQ(FormatStyle::LK_Cpp, Style->Language); - Style = getStyle("LLVM", "a.h", "none", + Style = getStyle("LLVM", "a.h", /*StyleSearchPaths*/{}, "none", "static const char *names[] = {[0] EQ \"foo\",\n" "[kBar] EQ \"bar\"};"); ASSERT_TRUE((bool)Style); diff --git a/clang/unittests/Format/FormatTestRawStrings.cpp b/clang/unittests/Format/FormatTestRawStrings.cpp index 0615fb1fad4c53..d2d4773df7736a 100644 --- a/clang/unittests/Format/FormatTestRawStrings.cpp +++ b/clang/unittests/Format/FormatTestRawStrings.cpp @@ -136,7 +136,7 @@ TEST_F(FormatTestRawStrings, UsesConfigurationOverBaseStyle) { EXPECT_EQ(0, parseConfiguration("---\n" "Language: Cpp\n" "BasedOnStyle: Google", - &Style) + &Style, /*StyleSearchPaths*/{}) .value()); Style.RawStringFormats = {{ FormatStyle::LK_Cpp, diff --git a/clang/unittests/Format/ObjCPropertyAttributeOrderFixerTest.cpp b/clang/unittests/Format/ObjCPropertyAttributeOrderFixerTest.cpp index 9f852e4768b12b..5fda3b981748d6 100644 --- a/clang/unittests/Format/ObjCPropertyAttributeOrderFixerTest.cpp +++ b/clang/unittests/Format/ObjCPropertyAttributeOrderFixerTest.cpp @@ -19,11 +19,13 @@ namespace { #define CHECK_PARSE(TEXT, FIELD, VALUE) \ EXPECT_NE(VALUE, Style.FIELD) << "Initial value already the same!"; \ - EXPECT_EQ(0, parseConfiguration(TEXT, &Style).value()); \ + EXPECT_EQ(0, parseConfiguration(TEXT, &Style, /*StyleSearchPaths*/{}) \ + .value()); \ EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!" #define FAIL_PARSE(TEXT, FIELD, VALUE) \ - EXPECT_NE(0, parseConfiguration(TEXT, &Style).value()); \ + EXPECT_NE(0, parseConfiguration(TEXT, &Style, /*StyleSearchPaths*/{}) \ + .value()); \ EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!" class ObjCPropertyAttributeOrderFixerTest : public FormatTestBase { diff --git a/clang/unittests/Format/QualifierFixerTest.cpp b/clang/unittests/Format/QualifierFixerTest.cpp index f9255c6e4c7088..3ed1e5579937f4 100644 --- a/clang/unittests/Format/QualifierFixerTest.cpp +++ b/clang/unittests/Format/QualifierFixerTest.cpp @@ -19,11 +19,13 @@ namespace { #define CHECK_PARSE(TEXT, FIELD, VALUE) \ EXPECT_NE(VALUE, Style.FIELD) << "Initial value already the same!"; \ - EXPECT_EQ(0, parseConfiguration(TEXT, &Style).value()); \ + EXPECT_EQ(0, parseConfiguration(TEXT, &Style, /*StyleSearchPaths*/{}) \ + .value()); \ EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!" #define FAIL_PARSE(TEXT, FIELD, VALUE) \ - EXPECT_NE(0, parseConfiguration(TEXT, &Style).value()); \ + EXPECT_NE(0, parseConfiguration(TEXT, &Style, /*StyleSearchPaths*/{}) \ + .value()); \ EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!" class QualifierFixerTest : public FormatTestBase { diff --git a/clang/unittests/Rename/ClangRenameTest.h b/clang/unittests/Rename/ClangRenameTest.h index 64033657b57963..7b242c26387095 100644 --- a/clang/unittests/Rename/ClangRenameTest.h +++ b/clang/unittests/Rename/ClangRenameTest.h @@ -81,7 +81,7 @@ class ClangRenameTest : public testing::Test, FileContents)) return ""; - formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite, "llvm"); + formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite, "llvm", /*StyleSearchPaths*/{}); return Context.getRewrittenText(InputFileID); } diff --git a/clang/unittests/Tooling/RefactoringTest.cpp b/clang/unittests/Tooling/RefactoringTest.cpp index 77d410f5d3b604..cf3c7c7fa1e137 100644 --- a/clang/unittests/Tooling/RefactoringTest.cpp +++ b/clang/unittests/Tooling/RefactoringTest.cpp @@ -541,7 +541,8 @@ TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) { "10")}); EXPECT_TRUE( formatAndApplyAllReplacements(FileToReplaces, Context.Rewrite, - "{BasedOnStyle: LLVM, ColumnLimit: 20}")); + "{BasedOnStyle: LLVM, ColumnLimit: 20}", + /*StyleSearchPaths*/{})); EXPECT_EQ(Expected1, Context.getRewrittenText(ID1)); EXPECT_EQ(Expected2, Context.getRewrittenText(ID2)); } diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h index 1d04783753d5cd..04dc9ccca36150 100644 --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -779,6 +779,8 @@ class IO { IO(void *Ctxt = nullptr); virtual ~IO(); + virtual SourceMgr& getSourceMgr(); + virtual bool outputting() const = 0; virtual unsigned beginSequence() = 0; @@ -819,6 +821,7 @@ class IO { virtual void setError(const Twine &) = 0; virtual void setAllowUnknownKeys(bool Allow); + virtual bool allowUnknownKeys() const; template <typename T> void enumCase(T &Val, const char* Str, const T ConstVal) { @@ -1449,6 +1452,8 @@ class Input : public IO { // Check if there was an syntax or semantic error during parsing. std::error_code error(); + SourceMgr& getSourceMgr() override; + private: bool outputting() const override; bool mapTag(StringRef, bool) override; @@ -1567,6 +1572,7 @@ class Input : public IO { const Node *getCurrentNode() const; void setAllowUnknownKeys(bool Allow) override; + bool allowUnknownKeys() const override; private: SourceMgr SrcMgr; // must be before Strm diff --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp index 56b557646100b1..d7bc0255fe4445 100644 --- a/llvm/lib/Support/YAMLTraits.cpp +++ b/llvm/lib/Support/YAMLTraits.cpp @@ -39,6 +39,10 @@ IO::IO(void *Context) : Ctxt(Context) {} IO::~IO() = default; +SourceMgr& IO::getSourceMgr() { + llvm_unreachable("Only supported for Input"); +} + void *IO::getContext() const { return Ctxt; } @@ -51,6 +55,10 @@ void IO::setAllowUnknownKeys(bool Allow) { llvm_unreachable("Only supported for Input"); } +bool IO::allowUnknownKeys() const { + llvm_unreachable("Only supported for Input"); +} + //===----------------------------------------------------------------------===// // Input //===----------------------------------------------------------------------===// @@ -75,6 +83,8 @@ Input::~Input() = default; std::error_code Input::error() { return EC; } +SourceMgr& Input::getSourceMgr() { return SrcMgr; } + bool Input::outputting() const { return false; } @@ -473,6 +483,8 @@ void Input::setError(const Twine &Message) { void Input::setAllowUnknownKeys(bool Allow) { AllowUnknownKeys = Allow; } +bool Input::allowUnknownKeys() const { return AllowUnknownKeys; } + bool Input::canElideEmptySequence() { return false; } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits