diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -65,6 +65,7 @@ FindTarget.cpp FileDistance.cpp Format.cpp + FormatProvider.cpp FS.cpp FuzzyMatch.cpp GlobalCompilationDatabase.cpp diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -12,6 +12,7 @@ #include "../clang-tidy/ClangTidyOptions.h" #include "CodeComplete.h" #include "ConfigProvider.h" +#include "FormatProvider.h" #include "GlobalCompilationDatabase.h" #include "Hover.h" #include "Protocol.h" @@ -118,6 +119,8 @@ /// checks will be disabled. TidyProviderRef ClangTidyProvider; + FormatProviderRef ClangFormatProvider; + /// If true, force -frecovery-ast flag. /// If false, respect the value in clang. bool BuildRecoveryAST = false; @@ -383,6 +386,9 @@ // When set, provides clang-tidy options for a specific file. TidyProviderRef ClangTidyProvider; + // When set, provides clang-format options for a specific file. + FormatProviderRef ClangFormatProvider; + // If this is true, suggest include insertion fixes for diagnostic errors that // can be caused by missing includes (e.g. member access in incomplete type). bool SuggestMissingIncludes = false; diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -145,6 +145,7 @@ Opts.CollectMainFileRefs) : nullptr), ClangTidyProvider(Opts.ClangTidyProvider), + ClangFormatProvider(Opts.ClangFormatProvider), SuggestMissingIncludes(Opts.SuggestMissingIncludes), BuildRecoveryAST(Opts.BuildRecoveryAST), PreserveRecoveryASTType(Opts.PreserveRecoveryASTType), @@ -213,6 +214,7 @@ Inputs.Opts = std::move(Opts); Inputs.Index = Index; Inputs.ClangTidyProvider = ClangTidyProvider; + Inputs.ClangFormatProvider = ClangFormatProvider; Inputs.Opts.BuildRecoveryAST = BuildRecoveryAST; Inputs.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType; bool NewFile = WorkScheduler.update(File, Inputs, WantDiags); @@ -258,6 +260,7 @@ ParseInput.Index = Index; ParseInput.Opts.BuildRecoveryAST = BuildRecoveryAST; ParseInput.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType; + ParseInput.ClangFormatProvider = ClangFormatProvider; // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. @@ -339,11 +342,8 @@ auto Action = [File = File.str(), Code = Code.str(), TriggerText = TriggerText.str(), CursorPos = *CursorPos, CB = std::move(CB), this]() mutable { - auto Style = format::getStyle(format::DefaultFormatStyle, File, - format::DefaultFallbackStyle, Code, - TFS.view(/*CWD=*/llvm::None).get()); - if (!Style) - return CB(Style.takeError()); + auto Style = ClangFormatProvider(File, Code); + assert(Style && "Provider should always return a valid style"); std::vector Result; for (const tooling::Replacement &R : @@ -410,12 +410,11 @@ return CB(R.takeError()); if (Opts.WantFormat) { - auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents, - *InpAST->Inputs.TFS); + auto Style = ClangFormatProvider(File, InpAST->Inputs.Contents); llvm::Error Err = llvm::Error::success(); for (auto &E : R->GlobalChanges) - Err = - llvm::joinErrors(reformatEdit(E.getValue(), Style), std::move(Err)); + Err = llvm::joinErrors(reformatEdit(E.getValue(), *Style), + std::move(Err)); if (Err) return CB(std::move(Err)); @@ -516,9 +515,8 @@ // Tweaks don't apply clang-format, do that centrally here. for (auto &It : (*Effect)->ApplyEdits) { Edit &E = It.second; - format::FormatStyle Style = - getFormatStyleForFile(File, E.InitialCode, TFS); - if (llvm::Error Err = reformatEdit(E, Style)) + auto Style = ClangFormatProvider(File, E.InitialCode); + if (llvm::Error Err = reformatEdit(E, *Style)) elog("Failed to format {0}: {1}", It.first(), std::move(Err)); } } else { @@ -567,15 +565,15 @@ // Call clang-format. auto Action = [File = File.str(), Code = Code.str(), Ranges = Ranges.vec(), CB = std::move(CB), this]() mutable { - format::FormatStyle Style = getFormatStyleForFile(File, Code, TFS); + auto Style = ClangFormatProvider(File, Code); tooling::Replacements IncludeReplaces = - format::sortIncludes(Style, Code, Ranges, File); + format::sortIncludes(*Style, Code, Ranges, File); auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces); if (!Changed) return CB(Changed.takeError()); CB(IncludeReplaces.merge(format::reformat( - Style, *Changed, + *Style, *Changed, tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges), File))); }; @@ -601,9 +599,8 @@ this](llvm::Expected InpAST) mutable { if (!InpAST) return CB(InpAST.takeError()); - format::FormatStyle Style = getFormatStyleForFile( - File, InpAST->Inputs.Contents, *InpAST->Inputs.TFS); - CB(clangd::getHover(InpAST->AST, Pos, std::move(Style), Index)); + auto Style = ClangFormatProvider(File, InpAST->Inputs.Contents); + CB(clangd::getHover(InpAST->AST, Pos, Style, Index)); }; WorkScheduler.runWithAST("Hover", File, std::move(Action), diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -24,6 +24,7 @@ #include "Diagnostics.h" #include "ExpectedTypes.h" #include "FileDistance.h" +#include "FormatProvider.h" #include "FuzzyMatch.h" #include "Headers.h" #include "Hover.h" @@ -1271,13 +1272,16 @@ /// set and contains a cached request. llvm::Optional SpecReq; + FormatProviderRef FormatProvider; + public: // A CodeCompleteFlow object is only useful for calling run() exactly once. CodeCompleteFlow(PathRef FileName, const IncludeStructure &Includes, SpeculativeFuzzyFind *SpecFuzzyFind, - const CodeCompleteOptions &Opts) + const CodeCompleteOptions &Opts, + FormatProviderRef FormatProvider) : FileName(FileName), Includes(Includes), SpecFuzzyFind(SpecFuzzyFind), - Opts(Opts) {} + Opts(Opts), FormatProvider(FormatProvider) {} CodeCompleteResult run(const SemaCompleteInput &SemaCCInput) && { trace::Span Tracer("CodeCompleteFlow"); @@ -1299,9 +1303,8 @@ assert(Recorder && "Recorder is not set"); CCContextKind = Recorder->CCContext.getKind(); IsUsingDeclaration = Recorder->CCContext.isUsingDeclaration(); - auto Style = getFormatStyleForFile(SemaCCInput.FileName, - SemaCCInput.ParseInput.Contents, - *SemaCCInput.ParseInput.TFS); + auto Style = + FormatProvider(SemaCCInput.FileName, SemaCCInput.ParseInput.Contents); const auto NextToken = Lexer::findNextToken( Recorder->CCSema->getPreprocessor().getCodeCompletionLoc(), Recorder->CCSema->getSourceManager(), Recorder->CCSema->LangOpts); @@ -1310,7 +1313,7 @@ // If preprocessor was run, inclusions from preprocessor callback should // already be added to Includes. Inserter.emplace( - SemaCCInput.FileName, SemaCCInput.ParseInput.Contents, Style, + SemaCCInput.FileName, SemaCCInput.ParseInput.Contents, *Style, SemaCCInput.ParseInput.CompileCommand.Directory, &Recorder->CCSema->getPreprocessor().getHeaderSearchInfo()); for (const auto &Inc : Includes.MainFileIncludes) @@ -1389,12 +1392,12 @@ ProxSources[FileName].Cost = 0; FileProximity.emplace(ProxSources); - auto Style = getFormatStyleForFile(FileName, Content, TFS); + auto Style = FormatProvider(FileName, Content); // This will only insert verbatim headers. - Inserter.emplace(FileName, Content, Style, + Inserter.emplace(FileName, Content, *Style, /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr); - auto Identifiers = collectIdentifiers(Content, Style); + auto Identifiers = collectIdentifiers(Content, *Style); std::vector IdentifierResults; for (const auto &IDAndCount : Identifiers) { RawIdentifier ID; @@ -1412,7 +1415,7 @@ // - all-scopes query if no qualifier was typed (and it's allowed). SpecifiedScope Scopes; Scopes.AccessibleScopes = visibleNamespaces( - Content.take_front(Offset), format::getFormattingLangOpts(Style)); + Content.take_front(Offset), format::getFormattingLangOpts(*Style)); for (std::string &S : Scopes.AccessibleScopes) if (!S.empty()) S.append("::"); // visibleNamespaces doesn't include trailing ::. @@ -1811,7 +1814,7 @@ } auto Flow = CodeCompleteFlow( FileName, Preamble ? Preamble->Includes : IncludeStructure(), - SpecFuzzyFind, Opts); + SpecFuzzyFind, Opts, ParseInput.ClangFormatProvider); return (!Preamble || Opts.RunParser == CodeCompleteOptions::NeverParse) ? std::move(Flow).runWithoutSema(ParseInput.Contents, *Offset, *ParseInput.TFS) diff --git a/clang-tools-extra/clangd/Compiler.h b/clang-tools-extra/clangd/Compiler.h --- a/clang-tools-extra/clangd/Compiler.h +++ b/clang-tools-extra/clangd/Compiler.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_COMPILER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_COMPILER_H +#include "FormatProvider.h" #include "GlobalCompilationDatabase.h" #include "TidyProvider.h" #include "index/Index.h" @@ -56,6 +57,7 @@ const SymbolIndex *Index = nullptr; ParseOptions Opts = ParseOptions(); TidyProviderRef ClangTidyProvider = {}; + FormatProviderRef ClangFormatProvider = {}; }; /// Builds compiler invocation that could be used to build AST or preamble. diff --git a/clang-tools-extra/clangd/FormatProvider.h b/clang-tools-extra/clangd/FormatProvider.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/FormatProvider.h @@ -0,0 +1,31 @@ +//===--- FormatProvider.h -----------------------------------*- C++-*------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FORMATPROVIDER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FORMATPROVIDER_H + +#include "support/Path.h" +#include "support/ThreadsafeFS.h" +#include "clang/Format/Format.h" +#include "llvm/ADT/FunctionExtras.h" + +namespace clang { +namespace clangd { +using FormatProvider = + llvm::unique_function( + PathRef, llvm::StringRef) const>; +using FormatProviderRef = + llvm::function_ref( + PathRef, llvm::StringRef)>; + +FormatProvider getClangFormatProvider(ThreadsafeFS &TFS, + StringRef FallbackStyle); + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_FORMATPROVIDER_H diff --git a/clang-tools-extra/clangd/FormatProvider.cpp b/clang-tools-extra/clangd/FormatProvider.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/FormatProvider.cpp @@ -0,0 +1,187 @@ +//===--- FormatProvider.cpp ---------------------------------*- C++-*------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FormatProvider.h" +#include "support/FileCache.h" +#include "support/Logger.h" +#include "clang/Format/Format.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBufferRef.h" +#include +#include +#include + +namespace clang { +namespace clangd { + +namespace { +// Access to config from a clang format configuration file, caching IO and +// parsing. +using LanguageKind = format::FormatStyle::LanguageKind; +class ClangFormatCache : private FileCache { + mutable std::shared_ptr Value; + const LanguageKind Lang; + +public: + ClangFormatCache(PathRef Path, LanguageKind Lang) + : FileCache(Path), Lang(Lang) {} + + std::shared_ptr + get(const ThreadsafeFS &TFS, + std::chrono::steady_clock::time_point FreshTime) const { + std::shared_ptr Result; + read( + TFS, FreshTime, + [this](llvm::Optional Data) { + // Clear any stale config if one exists. + Value.reset(); + if (!Data || !Data->empty()) + return; + format::FormatStyle Style = format::getLLVMStyle(Lang); + if (auto EC = format::parseConfiguration( + llvm::MemoryBufferRef(*Data, path()), &Style, + /*AllowUnknownOptions=*/true)) + elog("clang-format style parsing error: {0}", EC.message()); + else + Value = std::make_shared(std::move(Style)); + }, + [&]() { Result = Value; }); + return Result; + } +}; + +// Clang-Format checks for ".clang-format" then "_clang-format" in each +// directory, so this wraps those 2 caches into one. +struct ClangFormatCachePair { +private: + SmallString<256> appendDir(StringRef Dir, StringRef Tail) { + SmallString<256> Res(Dir); + llvm::sys::path::append(Res, Tail); + return Res; + } + +public: + ClangFormatCache Dot; + ClangFormatCache Underscore; + + explicit ClangFormatCachePair(StringRef Dir, LanguageKind Lang) + : Dot(appendDir(Dir, ".clang-format"), Lang), + Underscore(appendDir(Dir, "_clang-format"), Lang) {} +}; + +class ClangFormatConfigTree { + struct CacheMapAndFallback { + private: + static std::shared_ptr getFallback(StringRef Fallback, + LanguageKind Lang) { + auto Result = std::make_shared(format::getNoStyle()); + if (!format::getPredefinedStyle(Fallback, Lang, &*Result)) { + StringRef PrettyStyle; + if (Lang == LanguageKind::LK_Cpp) + PrettyStyle = "C/C++"; + else if (Lang == LanguageKind::LK_ObjC) + PrettyStyle = "Objective-C/Objective-C++"; + else + llvm_unreachable("Unsupported language kind"); + elog("error: Couldn't get fallback style for '{0}', using LLVM Style", + PrettyStyle); + *Result = format::getLLVMStyle(Lang); + } + return Result; + } + + public: + CacheMapAndFallback(StringRef FallbackStyle, LanguageKind Lang) + : Fallback(getFallback(FallbackStyle, Lang)) {} + + llvm::StringMap Map; + const std::shared_ptr Fallback; + }; + + const ThreadsafeFS &FS; + std::chrono::steady_clock::duration MaxStaleness; + mutable std::mutex Mu; + + CacheMapAndFallback CPPConfig, ObjCConfig; + +public: + ClangFormatConfigTree(const ThreadsafeFS &FS, StringRef FallbackStyle) + : FS(FS), MaxStaleness(std::chrono::seconds(5)), + CPPConfig(FallbackStyle, LanguageKind::LK_Cpp), + ObjCConfig(FallbackStyle, LanguageKind::LK_Cpp) {} + + std::shared_ptr get(PathRef AbsPath, + LanguageKind Lang) { + CacheMapAndFallback *MapFallback; + + switch (Lang) { + case LanguageKind::LK_Cpp: + MapFallback = &CPPConfig; + break; + case LanguageKind::LK_ObjC: + MapFallback = &ObjCConfig; + break; + default: + llvm_unreachable("Unsupported language kind"); + } + namespace path = llvm::sys::path; + assert(path::is_absolute(AbsPath)); + + // Compute absolute paths to all ancestors (substrings of P.Path). + // Ensure cache entries for each ancestor exist in the map. + llvm::StringRef Parent = path::parent_path(AbsPath); + llvm::SmallVector Caches; + { + std::lock_guard Lock(Mu); + for (auto I = path::begin(Parent), E = path::end(Parent); I != E; ++I) { + assert(I->end() >= Parent.begin() && I->end() <= Parent.end() && + "Canonical path components should be substrings"); + llvm::StringRef Ancestor(Parent.begin(), I->end() - Parent.begin()); +#ifdef _WIN32 + // C:\ is an ancestor, but skip its (relative!) parent C:. + if (Ancestor.size() == 2 && Ancestor.back() == ':') + continue; +#endif + assert(path::is_absolute(Ancestor)); + + auto It = MapFallback->Map.find(Ancestor); + + // Assemble the actual config file path only if needed. + if (It == MapFallback->Map.end()) { + It = MapFallback->Map.try_emplace(Ancestor, Ancestor, Lang).first; + } + Caches.push_back(&It->second); + } + } + // Finally query each individual file. + // This will take a (per-file) lock for each file that actually exists. + std::chrono::steady_clock::time_point FreshTime = + std::chrono::steady_clock::now() - MaxStaleness; + for (const ClangFormatCachePair *Cache : Caches) { + // Check the .clang-format file. + if (auto Config = Cache->Dot.get(FS, FreshTime)) + return Config; + // Check the _clang-format file. + if (auto Config = Cache->Underscore.get(FS, FreshTime)) + return Config; + } + return MapFallback->Fallback; + } +}; + +} // namespace +FormatProvider getClangFormatProvider(ThreadsafeFS &TFS, + StringRef FallbackStyle) { + return [Tree = std::make_unique(TFS, FallbackStyle)]( + PathRef Filename, StringRef Contents) { + LanguageKind Lang = format::guessLanguage(Filename, Contents); + return Tree->get(Filename, Lang); + }; +} +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h --- a/clang-tools-extra/clangd/Hover.h +++ b/clang-tools-extra/clangd/Hover.h @@ -114,9 +114,10 @@ } /// Get the hover information when hovering at \p Pos. -llvm::Optional getHover(ParsedAST &AST, Position Pos, - format::FormatStyle Style, - const SymbolIndex *Index); +llvm::Optional +getHover(ParsedAST &AST, Position Pos, + std::shared_ptr Style, + const SymbolIndex *Index); } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -828,9 +828,10 @@ } // namespace -llvm::Optional getHover(ParsedAST &AST, Position Pos, - format::FormatStyle Style, - const SymbolIndex *Index) { +llvm::Optional +getHover(ParsedAST &AST, Position Pos, + std::shared_ptr Style, + const SymbolIndex *Index) { PrintingPolicy PP = getPrintingPolicy(AST.getASTContext().getPrintingPolicy()); const SourceManager &SM = AST.getSourceManager(); @@ -909,7 +910,7 @@ return llvm::None; auto Replacements = format::reformat( - Style, HI->Definition, tooling::Range(0, HI->Definition.size())); + *Style, HI->Definition, tooling::Range(0, HI->Definition.size())); if (auto Formatted = tooling::applyAllReplacements(HI->Definition, Replacements)) HI->Definition = *Formatted; diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -353,9 +353,9 @@ auto BuildDir = VFS->getCurrentWorkingDirectory(); if (Inputs.Opts.SuggestMissingIncludes && Inputs.Index && !BuildDir.getError()) { - auto Style = getFormatStyleForFile(Filename, Inputs.Contents, *Inputs.TFS); + auto Style = Inputs.ClangFormatProvider(Filename, Inputs.Contents); auto Inserter = std::make_shared( - Filename, Inputs.Contents, Style, BuildDir.get(), + Filename, Inputs.Contents, *Style, BuildDir.get(), &Clang->getPreprocessor().getHeaderSearchInfo()); if (Preamble) { for (const auto &Inc : Preamble->Includes.MainFileIncludes) diff --git a/clang-tools-extra/clangd/SourceCode.h b/clang-tools-extra/clangd/SourceCode.h --- a/clang-tools-extra/clangd/SourceCode.h +++ b/clang-tools-extra/clangd/SourceCode.h @@ -161,15 +161,6 @@ llvm::Optional getCanonicalPath(const FileEntry *F, const SourceManager &SourceMgr); -/// Choose the clang-format style we should apply to a certain file. -/// This will usually use FS to look for .clang-format directories. -/// FIXME: should we be caching the .clang-format file search? -/// This uses format::DefaultFormatStyle and format::DefaultFallbackStyle, -/// though the latter may have been overridden in main()! -format::FormatStyle getFormatStyleForFile(llvm::StringRef File, - llvm::StringRef Content, - const ThreadsafeFS &TFS); - /// Cleanup and format the given replacements. llvm::Expected cleanupAndFormat(StringRef Code, const tooling::Replacements &Replaces, diff --git a/clang-tools-extra/clangd/SourceCode.cpp b/clang-tools-extra/clangd/SourceCode.cpp --- a/clang-tools-extra/clangd/SourceCode.cpp +++ b/clang-tools-extra/clangd/SourceCode.cpp @@ -570,20 +570,6 @@ return digest(Content); } -format::FormatStyle getFormatStyleForFile(llvm::StringRef File, - llvm::StringRef Content, - const ThreadsafeFS &TFS) { - auto Style = format::getStyle(format::DefaultFormatStyle, File, - format::DefaultFallbackStyle, Content, - TFS.view(/*CWD=*/llvm::None).get()); - if (!Style) { - log("getStyle() failed for file {0}: {1}. Fallback is LLVM style.", File, - Style.takeError()); - return format::getLLVMStyle(); - } - return *Style; -} - llvm::Expected cleanupAndFormat(StringRef Code, const tooling::Replacements &Replaces, const format::FormatStyle &Style) { diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp --- a/clang-tools-extra/clangd/tool/Check.cpp +++ b/clang-tools-extra/clangd/tool/Check.cpp @@ -76,7 +76,7 @@ // from buildInvocation ParseInputs Inputs; std::unique_ptr Invocation; - format::FormatStyle Style; + std::shared_ptr Style; // from buildAST std::shared_ptr Preamble; llvm::Optional AST; @@ -134,6 +134,7 @@ return false; } } + Inputs.ClangFormatProvider = Opts.ClangFormatProvider; log("Parsing command..."); Invocation = buildCompilerInvocation(Inputs, CaptureInvocationDiags, &CC1Args); @@ -147,7 +148,7 @@ // FIXME: Check that resource-dir/built-in-headers exist? - Style = getFormatStyleForFile(File, Inputs.Contents, TFS); + Style = Inputs.ClangFormatProvider(File, Inputs.Contents); return true; } diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -9,6 +9,7 @@ #include "ClangdLSPServer.h" #include "CodeComplete.h" #include "Features.inc" +#include "FormatProvider.h" #include "PathMapping.h" #include "Protocol.h" #include "TidyProvider.h" @@ -882,6 +883,9 @@ ClangTidyOptProvider = combine(std::move(Providers)); Opts.ClangTidyProvider = ClangTidyOptProvider; } + FormatProvider ClangFormatOptProvider = + getClangFormatProvider(TFS, FallbackStyle); + Opts.ClangFormatProvider = ClangFormatOptProvider; Opts.AsyncPreambleBuilds = AsyncPreamble; Opts.SuggestMissingIncludes = SuggestMissingIncludes; Opts.QueryDriverGlobs = std::move(QueryDriverGlobs); diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -11,6 +11,7 @@ #include "ClangdServer.h" #include "CodeComplete.h" #include "ConfigFragment.h" +#include "FormatProvider.h" #include "GlobalCompilationDatabase.h" #include "Matchers.h" #include "SyncAPI.h" @@ -616,8 +617,12 @@ MockFS FS; ErrorCheckingCallbacks DiagConsumer; MockCompilationDatabase CDB; + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + ClangdServer::Options ServerOpts = ClangdServer::optsForTest(); + ServerOpts.ClangFormatProvider = Format; - ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer); + ClangdServer Server(CDB, FS, ServerOpts, &DiagConsumer); auto FooCpp = testPath("foo.cpp"); // clang cannot create CompilerInvocation if we pass two files in the @@ -731,7 +736,12 @@ TestDiagConsumer DiagConsumer; { MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer); + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + ClangdServer::Options ServerOpts = ClangdServer::optsForTest(); + ServerOpts.ClangFormatProvider = Format; + + ClangdServer Server(CDB, FS, ServerOpts, &DiagConsumer); // Prepare some random distributions for the test. std::random_device RandGen; @@ -926,7 +936,12 @@ MockFS FS; ErrorCheckingCallbacks DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer); + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + ClangdServer::Options ServerOpts = ClangdServer::optsForTest(); + ServerOpts.ClangFormatProvider = Format; + + ClangdServer Server(CDB, FS, ServerOpts, &DiagConsumer); auto Path = testPath("foo.cpp"); std::string Code = R"cpp( @@ -955,7 +970,12 @@ MockFS FS; ErrorCheckingCallbacks DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer); + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + ClangdServer::Options ServerOpts = ClangdServer::optsForTest(); + ServerOpts.ClangFormatProvider = Format; + + ClangdServer Server(CDB, FS, ServerOpts, &DiagConsumer); auto SourcePath = testPath("source/foo.cpp"); auto HeaderPath = testPath("headers/foo.h"); @@ -1032,7 +1052,12 @@ StatRecordingFS FS(CountStats); ErrorCheckingCallbacks DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer); + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + ClangdServer::Options ServerOpts = ClangdServer::optsForTest(); + ServerOpts.ClangFormatProvider = Format; + + ClangdServer Server(CDB, FS, ServerOpts, &DiagConsumer); auto SourcePath = testPath("foo.cpp"); auto HeaderPath = testPath("foo.h"); @@ -1061,7 +1086,12 @@ MockFS FS; ErrorCheckingCallbacks DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer); + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + ClangdServer::Options ServerOpts = ClangdServer::optsForTest(); + ServerOpts.ClangFormatProvider = Format; + + ClangdServer Server(CDB, FS, ServerOpts, &DiagConsumer); auto FooCpp = testPath("foo.cpp"); Annotations Code(R"cpp( @@ -1132,7 +1162,12 @@ Notification CanReturnCommand; DelayedCompilationDatabase CDB(CanReturnCommand); - ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer); + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + ClangdServer::Options ServerOpts = ClangdServer::optsForTest(); + ServerOpts.ClangFormatProvider = Format; + + ClangdServer Server(CDB, FS, ServerOpts, &DiagConsumer); auto FooCpp = testPath("foo.cpp"); Annotations Code(R"cpp( diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -10,6 +10,7 @@ #include "ClangdServer.h" #include "CodeComplete.h" #include "Compiler.h" +#include "FormatProvider.h" #include "Matchers.h" #include "Protocol.h" #include "Quality.h" @@ -140,7 +141,7 @@ CodeCompleteResult completionsNoCompile(llvm::StringRef Text, std::vector IndexSymbols = {}, clangd::CodeCompleteOptions Opts = {}, - PathRef FilePath = "foo.cpp") { + PathRef FilePath = "/foo.cpp") { std::unique_ptr OverrideIndex; if (!IndexSymbols.empty()) { assert(!Opts.Index && "both Index and IndexSymbols given!"); @@ -149,8 +150,11 @@ } MockFS FS; + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); Annotations Test(Text); ParseInputs ParseInput{tooling::CompileCommand(), &FS, Test.code().str()}; + ParseInput.ClangFormatProvider = Format; return codeComplete(FilePath, Test.point(), /*Preamble=*/nullptr, ParseInput, Opts); } @@ -839,6 +843,9 @@ MockCompilationDatabase CDB; ClangdServer::Options Opts = ClangdServer::optsForTest(); Opts.BuildDynamicSymbolIndex = true; + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + Opts.ClangFormatProvider = Format; ClangdServer Server(CDB, FS, Opts); FS.Files[testPath("foo_header.h")] = R"cpp( @@ -874,6 +881,9 @@ MockCompilationDatabase CDB; auto Opts = ClangdServer::optsForTest(); Opts.BuildDynamicSymbolIndex = true; + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + Opts.ClangFormatProvider = Format; ClangdServer Server(CDB, FS, Opts); FS.Files[testPath("foo.h")] = R"cpp( @@ -935,6 +945,9 @@ auto Opts = ClangdServer::optsForTest(); Opts.BuildDynamicSymbolIndex = true; + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + Opts.ClangFormatProvider = Format; ClangdServer Server(CDB, FS, Opts); @@ -1667,7 +1680,11 @@ FS.Files[FooCpp] = ""; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, ClangdServer::optsForTest()); + ClangdServer::Options ServerOpts = ClangdServer::optsForTest(); + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + ServerOpts.ClangFormatProvider = Format; + ClangdServer Server(CDB, FS, ServerOpts); Annotations Source(R"cpp( #include "foo.h" @@ -1755,7 +1772,11 @@ MockFS FS; FS.Files[FooCpp] = "// empty file"; - ClangdServer Server(CDB, FS, ClangdServer::optsForTest()); + ClangdServer::Options ServerOpts = ClangdServer::optsForTest(); + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + ServerOpts.ClangFormatProvider = Format; + ClangdServer Server(CDB, FS, ServerOpts); // Run completion outside the file range. Position Pos; Pos.line = 100; @@ -2161,6 +2182,9 @@ MockCompilationDatabase CDB; ClangdServer::Options Opts = ClangdServer::optsForTest(); Opts.BuildDynamicSymbolIndex = true; + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + Opts.ClangFormatProvider = Format; ClangdServer Server(CDB, FS, Opts); FS.Files[testPath("foo.h")] = R"cpp( @@ -2331,7 +2355,11 @@ TEST(CompletionTest, EnableSpeculativeIndexRequest) { MockFS FS; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, ClangdServer::optsForTest()); + ClangdServer::Options ServerOpts = ClangdServer::optsForTest(); + FormatProvider Format = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + ServerOpts.ClangFormatProvider = Format; + ClangdServer Server(CDB, FS, ServerOpts); auto File = testPath("foo.cpp"); Annotations Test(R"cpp( diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -876,7 +876,9 @@ TU.ExtraArgs.push_back("--target=x86_64-pc-linux-gnu"); auto AST = TU.build(); - auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr); + auto H = getHover( + AST, T.point(), + std::make_shared(format::getLLVMStyle()), nullptr); ASSERT_TRUE(H); HoverInfo Expected; Expected.SymRange = T.range(); @@ -972,7 +974,9 @@ TestTU TU = TestTU::withCode(T.code()); TU.ExtraArgs.push_back("-std=c++17"); auto AST = TU.build(); - auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr); + auto H = getHover( + AST, T.point(), + std::make_shared(format::getLLVMStyle()), nullptr); ASSERT_TRUE(H); EXPECT_EQ(H->CallPassType->PassBy, Test.PassBy); EXPECT_EQ(H->CallPassType->Converted, Test.Converted); @@ -1024,7 +1028,9 @@ TestTU TU = TestTU::withCode(T.code()); TU.ExtraArgs.push_back("-std=c++17"); auto AST = TU.build(); - auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr); + auto H = getHover( + AST, T.point(), + std::make_shared(format::getLLVMStyle()), nullptr); ASSERT_FALSE(H); } } @@ -2284,7 +2290,10 @@ TU.ExtraArgs.push_back("--target=x86_64-pc-linux-gnu"); auto AST = TU.build(); - auto H = getHover(AST, T.point(), format::getLLVMStyle(), Index.get()); + auto H = + getHover(AST, T.point(), + std::make_shared(format::getLLVMStyle()), + Index.get()); ASSERT_TRUE(H); HoverInfo Expected; Expected.SymRange = T.range(); @@ -2326,7 +2335,9 @@ MemIndex::build(std::move(Symbols).build(), RefSlab(), RelationSlab()); for (const auto &P : T.points()) { - auto H = getHover(AST, P, format::getLLVMStyle(), Index.get()); + auto H = getHover( + AST, P, std::make_shared(format::getLLVMStyle()), + Index.get()); ASSERT_TRUE(H); EXPECT_EQ(H->Documentation, IndexSym.Documentation); } @@ -2351,7 +2362,9 @@ TestTU TU = TestTU::withCode(T.code()); auto AST = TU.build(); for (const auto &P : T.points()) { - auto H = getHover(AST, P, format::getLLVMStyle(), nullptr); + auto H = getHover( + AST, P, std::make_shared(format::getLLVMStyle()), + nullptr); ASSERT_TRUE(H); EXPECT_EQ(H->Documentation, "doc"); } @@ -2375,7 +2388,9 @@ auto AST = TU.build(); for (auto Comment : {"doc1", "doc2", "doc3"}) { for (const auto &P : T.points(Comment)) { - auto H = getHover(AST, P, format::getLLVMStyle(), nullptr); + auto H = getHover( + AST, P, std::make_shared(format::getLLVMStyle()), + nullptr); ASSERT_TRUE(H); EXPECT_EQ(H->Documentation, Comment); } diff --git a/clang-tools-extra/clangd/unittests/PreambleTests.cpp b/clang-tools-extra/clangd/unittests/PreambleTests.cpp --- a/clang-tools-extra/clangd/unittests/PreambleTests.cpp +++ b/clang-tools-extra/clangd/unittests/PreambleTests.cpp @@ -393,9 +393,9 @@ auto FoundMacro = locateMacroAt(*MacroTok, AST->getPreprocessor()); ASSERT_TRUE(FoundMacro); EXPECT_THAT(FoundMacro->Name, "FOO"); - auto HI = - getHover(*AST, offsetToPosition(Modified.code(), Modified.point()), - format::getLLVMStyle(), nullptr); + auto HI = getHover( + *AST, offsetToPosition(Modified.code(), Modified.point()), + std::make_shared(format::getLLVMStyle()), nullptr); ASSERT_TRUE(HI); EXPECT_THAT(HI->Definition, testing::IsEmpty()); } @@ -409,7 +409,9 @@ auto AST = createPatchedAST(Baseline, Modified.code()); ASSERT_TRUE(AST); - auto HI = getHover(*AST, Modified.point(), format::getLLVMStyle(), nullptr); + auto HI = getHover( + *AST, Modified.point(), + std::make_shared(format::getLLVMStyle()), nullptr); ASSERT_TRUE(HI); EXPECT_THAT(HI->Definition, "#define BAR"); } diff --git a/clang-tools-extra/clangd/unittests/TestTU.h b/clang-tools-extra/clangd/unittests/TestTU.h --- a/clang-tools-extra/clangd/unittests/TestTU.h +++ b/clang-tools-extra/clangd/unittests/TestTU.h @@ -19,6 +19,7 @@ #include "../TidyProvider.h" #include "Compiler.h" +#include "FormatProvider.h" #include "ParsedAST.h" #include "TestFS.h" #include "index/Index.h" @@ -60,6 +61,8 @@ std::vector ExtraArgs; TidyProvider ClangTidyProvider = {}; + + mutable FormatProvider ClangFormatProvider = {}; // Index to use when building AST. const SymbolIndex *ExternalIndex = nullptr; diff --git a/clang-tools-extra/clangd/unittests/TestTU.cpp b/clang-tools-extra/clangd/unittests/TestTU.cpp --- a/clang-tools-extra/clangd/unittests/TestTU.cpp +++ b/clang-tools-extra/clangd/unittests/TestTU.cpp @@ -9,6 +9,7 @@ #include "TestTU.h" #include "Compiler.h" #include "Diagnostics.h" +#include "FormatProvider.h" #include "TestFS.h" #include "index/FileIndex.h" #include "index/MemIndex.h" @@ -61,6 +62,9 @@ Inputs.Opts = ParseOptions(); if (ClangTidyProvider) Inputs.ClangTidyProvider = ClangTidyProvider; + ClangFormatProvider = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + Inputs.ClangFormatProvider = ClangFormatProvider; Inputs.Index = ExternalIndex; if (Inputs.Index) Inputs.Opts.SuggestMissingIncludes = true;