diff options
author | Sam McCall <sam.mccall@gmail.com> | 2018-07-03 08:09:29 +0000 |
---|---|---|
committer | Sam McCall <sam.mccall@gmail.com> | 2018-07-03 08:09:29 +0000 |
commit | 3f0243fdafaa7207fa6ade486e1bb9b99d2c4140 (patch) | |
tree | 370b8271f4c8701b309c077f419ecc57e2623f31 /clang-tools-extra/clangd/CodeComplete.cpp | |
parent | a0a52bf195821a49a6920084a6ddb5139d9fa3fe (diff) | |
download | bcm5719-llvm-3f0243fdafaa7207fa6ade486e1bb9b99d2c4140.tar.gz bcm5719-llvm-3f0243fdafaa7207fa6ade486e1bb9b99d2c4140.zip |
[clangd] Incorporate transitive #includes into code complete proximity scoring.
Summary:
We now compute a distance from the main file to the symbol header, which
is a weighted count of:
- some number of #include traversals from source file --> included file
- some number of FS traversals from file --> parent directory
- some number of FS traversals from parent directory --> child file/dir
This calculation is performed in the appropriate URI scheme.
This means we'll get some proximity boost from header files in main-file
contexts, even when these are in different directory trees.
This extended file proximity model is not yet incorporated in the index
interface/implementation.
Reviewers: ioeric
Subscribers: mgorny, ilya-biryukov, MaskRay, jkorous, cfe-commits
Differential Revision: https://reviews.llvm.org/D48441
llvm-svn: 336177
Diffstat (limited to 'clang-tools-extra/clangd/CodeComplete.cpp')
-rw-r--r-- | clang-tools-extra/clangd/CodeComplete.cpp | 120 |
1 files changed, 67 insertions, 53 deletions
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index 33fdd853dd1..248c96b36db 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -22,6 +22,7 @@ #include "AST.h" #include "CodeCompletionStrings.h" #include "Compiler.h" +#include "FileDistance.h" #include "FuzzyMatch.h" #include "Headers.h" #include "Logger.h" @@ -763,7 +764,6 @@ struct SemaCompleteInput { PathRef FileName; const tooling::CompileCommand &Command; PrecompiledPreamble const *Preamble; - const std::vector<Inclusion> &PreambleInclusions; StringRef Contents; Position Pos; IntrusiveRefCntPtr<vfs::FileSystem> VFS; @@ -771,12 +771,11 @@ struct SemaCompleteInput { }; // Invokes Sema code completion on a file. -// If \p Includes is set, it will be initialized after a compiler instance has -// been set up. +// If \p Includes is set, it will be updated based on the compiler invocation. bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer, const clang::CodeCompleteOptions &Options, const SemaCompleteInput &Input, - std::unique_ptr<IncludeInserter> *Includes = nullptr) { + IncludeStructure *Includes = nullptr) { trace::Span Tracer("Sema completion"); std::vector<const char *> ArgStrs; for (const auto &S : Input.Command.CommandLine) @@ -837,29 +836,9 @@ bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer, Input.FileName); return false; } - if (Includes) { - // Initialize Includes if provided. - - // FIXME(ioeric): needs more consistent style support in clangd server. - auto Style = format::getStyle(format::DefaultFormatStyle, Input.FileName, - format::DefaultFallbackStyle, Input.Contents, - Input.VFS.get()); - if (!Style) { - log("ERROR: failed to get FormatStyle for file " + Input.FileName + - ". Fall back to use LLVM style. Error: " + - llvm::toString(Style.takeError())); - Style = format::getLLVMStyle(); - } - *Includes = llvm::make_unique<IncludeInserter>( - Input.FileName, Input.Contents, *Style, Input.Command.Directory, - Clang->getPreprocessor().getHeaderSearchInfo()); - for (const auto &Inc : Input.PreambleInclusions) - Includes->get()->addExisting(Inc); - Clang->getPreprocessor().addPPCallbacks(collectInclusionsInMainFileCallback( - Clang->getSourceManager(), [Includes](Inclusion Inc) { - Includes->get()->addExisting(std::move(Inc)); - })); - } + if (Includes) + Clang->getPreprocessor().addPPCallbacks( + collectIncludeStructureCallback(Clang->getSourceManager(), Includes)); if (!Action.Execute()) { log("Execute() failed when running codeComplete for " + Input.FileName); return false; @@ -949,24 +928,23 @@ clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const { // - TopN determines the results with the best score. class CodeCompleteFlow { PathRef FileName; + IncludeStructure Includes; // Complete once the compiler runs. const CodeCompleteOptions &Opts; // Sema takes ownership of Recorder. Recorder is valid until Sema cleanup. CompletionRecorder *Recorder = nullptr; int NSema = 0, NIndex = 0, NBoth = 0; // Counters for logging. bool Incomplete = false; // Would more be available with a higher limit? llvm::Optional<FuzzyMatcher> Filter; // Initialized once Sema runs. - std::unique_ptr<IncludeInserter> Includes; // Initialized once compiler runs. - FileProximityMatcher FileProximityMatch; + // Include-insertion and proximity scoring rely on the include structure. + // This is available after Sema has run. + llvm::Optional<IncludeInserter> Inserter; // Available during runWithSema. + llvm::Optional<URIDistance> FileProximity; // Initialized once Sema runs. public: // A CodeCompleteFlow object is only useful for calling run() exactly once. - CodeCompleteFlow(PathRef FileName, const CodeCompleteOptions &Opts) - : FileName(FileName), Opts(Opts), - // FIXME: also use path of the main header corresponding to FileName to - // calculate the file proximity, which would capture include/ and src/ - // project setup where headers and implementations are not in the same - // directory. - FileProximityMatch(ArrayRef<StringRef>({FileName})) {} + CodeCompleteFlow(PathRef FileName, const IncludeStructure &Includes, + const CodeCompleteOptions &Opts) + : FileName(FileName), Includes(Includes), Opts(Opts) {} CodeCompleteResult run(const SemaCompleteInput &SemaCCInput) && { trace::Span Tracer("CodeCompleteFlow"); @@ -977,11 +955,45 @@ public: CodeCompleteResult Output; auto RecorderOwner = llvm::make_unique<CompletionRecorder>(Opts, [&]() { assert(Recorder && "Recorder is not set"); - assert(Includes && "Includes is not set"); + // FIXME(ioeric): needs more consistent style support in clangd server. + auto Style = + format::getStyle("file", SemaCCInput.FileName, "LLVM", + SemaCCInput.Contents, SemaCCInput.VFS.get()); + if (!Style) { + log("Failed to get FormatStyle for file" + SemaCCInput.FileName + ": " + + llvm::toString(Style.takeError()) + ". Fallback is LLVM style."); + Style = format::getLLVMStyle(); + } // If preprocessor was run, inclusions from preprocessor callback should - // already be added to Inclusions. + // already be added to Includes. + Inserter.emplace( + SemaCCInput.FileName, SemaCCInput.Contents, *Style, + SemaCCInput.Command.Directory, + Recorder->CCSema->getPreprocessor().getHeaderSearchInfo()); + for (const auto &Inc : Includes.MainFileIncludes) + Inserter->addExisting(Inc); + + // Most of the cost of file proximity is in initializing the FileDistance + // structures based on the observed includes, once per query. Conceptually + // that happens here (though the per-URI-scheme initialization is lazy). + // The per-result proximity scoring is (amortized) very cheap. + FileDistanceOptions ProxOpts{}; // Use defaults. + const auto &SM = Recorder->CCSema->getSourceManager(); + llvm::StringMap<SourceParams> ProxSources; + for (auto &Entry : Includes.includeDepth( + SM.getFileEntryForID(SM.getMainFileID())->getName())) { + auto &Source = ProxSources[Entry.getKey()]; + Source.Cost = Entry.getValue() * ProxOpts.IncludeCost; + // Symbols near our transitive includes are good, but only consider + // things in the same directory or below it. Otherwise there can be + // many false positives. + if (Entry.getValue() > 0) + Source.MaxUpTraversals = 1; + } + FileProximity.emplace(ProxSources, ProxOpts); + Output = runWithSema(); - Includes.reset(); // Make sure this doesn't out-live Clang. + Inserter.reset(); // Make sure this doesn't out-live Clang. SPAN_ATTACH(Tracer, "sema_completion_kind", getCompletionKindString(Recorder->CCContext.getKind())); }); @@ -1044,6 +1056,7 @@ private: Req.RestrictForCodeCompletion = true; Req.Scopes = getQueryScopes(Recorder->CCContext, Recorder->CCSema->getSourceManager()); + // FIXME: we should send multiple weighted paths here. Req.ProximityPaths.push_back(FileName); log(llvm::formatv("Code complete: fuzzyFind(\"{0}\", scopes=[{1}])", Req.Query, @@ -1124,7 +1137,7 @@ private: SymbolQualitySignals Quality; SymbolRelevanceSignals Relevance; Relevance.Query = SymbolRelevanceSignals::CodeComplete; - Relevance.FileProximityMatch = &FileProximityMatch; + Relevance.FileProximityMatch = FileProximity.getPointer(); auto &First = Bundle.front(); if (auto FuzzyScore = fuzzyScore(First)) Relevance.NameMatch = *FuzzyScore; @@ -1174,7 +1187,7 @@ private: : nullptr; if (!Builder) Builder.emplace(Recorder->CCSema->getASTContext(), Item, SemaCCS, - *Includes, FileName, Opts); + *Inserter, FileName, Opts); else Builder->add(Item, SemaCCS); } @@ -1182,15 +1195,16 @@ private: } }; -CodeCompleteResult codeComplete( - PathRef FileName, const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, - const std::vector<Inclusion> &PreambleInclusions, StringRef Contents, - Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS, - std::shared_ptr<PCHContainerOperations> PCHs, CodeCompleteOptions Opts) { - return CodeCompleteFlow(FileName, Opts) - .run({FileName, Command, Preamble, PreambleInclusions, Contents, Pos, VFS, - PCHs}); +CodeCompleteResult codeComplete(PathRef FileName, + const tooling::CompileCommand &Command, + PrecompiledPreamble const *Preamble, + const IncludeStructure &PreambleInclusions, + StringRef Contents, Position Pos, + IntrusiveRefCntPtr<vfs::FileSystem> VFS, + std::shared_ptr<PCHContainerOperations> PCHs, + CodeCompleteOptions Opts) { + return CodeCompleteFlow(FileName, PreambleInclusions, Opts) + .run({FileName, Command, Preamble, Contents, Pos, VFS, PCHs}); } SignatureHelp signatureHelp(PathRef FileName, @@ -1205,11 +1219,11 @@ SignatureHelp signatureHelp(PathRef FileName, Options.IncludeMacros = false; Options.IncludeCodePatterns = false; Options.IncludeBriefComments = false; - std::vector<Inclusion> PreambleInclusions = {}; // Unused for signatureHelp + IncludeStructure PreambleInclusions; // Unused for signatureHelp semaCodeComplete(llvm::make_unique<SignatureHelpCollector>(Options, Result), Options, - {FileName, Command, Preamble, PreambleInclusions, Contents, - Pos, std::move(VFS), std::move(PCHs)}); + {FileName, Command, Preamble, Contents, Pos, std::move(VFS), + std::move(PCHs)}); return Result; } |