Author: ioeric Date: Fri Feb 16 06:15:55 2018 New Revision: 325343 URL: http://llvm.org/viewvc/llvm-project?rev=325343&view=rev Log: [clangd] collect symbol #include & insert #include in global code completion.
Summary: o Collect suitable #include paths for index symbols. This also does smart mapping for STL symbols and IWYU pragma (code borrowed from include-fixer). o For global code completion, add a command for inserting new #include in each code completion item. Reviewers: sammccall Reviewed By: sammccall Subscribers: klimek, mgorny, ilya-biryukov, jkorous-apple, hintonda, cfe-commits Differential Revision: https://reviews.llvm.org/D42640 Added: clang-tools-extra/trunk/clangd/Headers.cpp clang-tools-extra/trunk/clangd/Headers.h clang-tools-extra/trunk/clangd/index/CanonicalIncludes.cpp clang-tools-extra/trunk/clangd/index/CanonicalIncludes.h clang-tools-extra/trunk/test/clangd/insert-include.test clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp Modified: clang-tools-extra/trunk/clangd/CMakeLists.txt clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp clang-tools-extra/trunk/clangd/ClangdServer.cpp clang-tools-extra/trunk/clangd/ClangdServer.h clang-tools-extra/trunk/clangd/CodeComplete.cpp clang-tools-extra/trunk/clangd/Protocol.cpp clang-tools-extra/trunk/clangd/Protocol.h clang-tools-extra/trunk/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp clang-tools-extra/trunk/clangd/index/Index.cpp clang-tools-extra/trunk/clangd/index/Index.h clang-tools-extra/trunk/clangd/index/Merge.cpp clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp clang-tools-extra/trunk/clangd/index/SymbolCollector.h clang-tools-extra/trunk/clangd/index/SymbolYAML.cpp clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp clang-tools-extra/trunk/test/clangd/completion-snippets.test clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test clang-tools-extra/trunk/test/clangd/initialize-params.test clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp Modified: clang-tools-extra/trunk/clangd/CMakeLists.txt URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CMakeLists.txt?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/CMakeLists.txt (original) +++ clang-tools-extra/trunk/clangd/CMakeLists.txt Fri Feb 16 06:15:55 2018 @@ -14,6 +14,7 @@ add_clang_library(clangDaemon DraftStore.cpp FuzzyMatch.cpp GlobalCompilationDatabase.cpp + Headers.cpp JSONExpr.cpp JSONRPCDispatcher.cpp Logger.cpp @@ -25,6 +26,7 @@ add_clang_library(clangDaemon TUScheduler.cpp URI.cpp XRefs.cpp + index/CanonicalIncludes.cpp index/FileIndex.cpp index/Index.cpp index/MemIndex.cpp Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original) +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Fri Feb 16 06:15:55 2018 @@ -121,7 +121,9 @@ void ClangdLSPServer::onInitialize(Initi {"renameProvider", true}, {"executeCommandProvider", json::obj{ - {"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}}, + {"commands", + {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND, + ExecuteCommandParams::CLANGD_INSERT_HEADER_INCLUDE}}, }}, }}}}); } @@ -155,6 +157,14 @@ void ClangdLSPServer::onFileEvent(DidCha } void ClangdLSPServer::onCommand(ExecuteCommandParams &Params) { + auto ApplyEdit = [](WorkspaceEdit WE) { + ApplyWorkspaceEditParams Edit; + Edit.edit = std::move(WE); + // We don't need the response so id == 1 is OK. + // Ideally, we would wait for the response and if there is no error, we + // would reply success/failure to the original RPC. + call("workspace/applyEdit", Edit); + }; if (Params.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND && Params.workspaceEdit) { // The flow for "apply-fix" : @@ -166,13 +176,35 @@ void ClangdLSPServer::onCommand(ExecuteC // 6. The editor applies the changes (applyEdit), and sends us a reply (but // we ignore it) - ApplyWorkspaceEditParams ApplyEdit; - ApplyEdit.edit = *Params.workspaceEdit; reply("Fix applied."); - // We don't need the response so id == 1 is OK. - // Ideally, we would wait for the response and if there is no error, we - // would reply success/failure to the original RPC. - call("workspace/applyEdit", ApplyEdit); + ApplyEdit(*Params.workspaceEdit); + } else if (Params.command == + ExecuteCommandParams::CLANGD_INSERT_HEADER_INCLUDE) { + auto &FileURI = Params.includeInsertion->textDocument.uri; + auto Code = Server.getDocument(FileURI.file()); + if (!Code) + return replyError(ErrorCode::InvalidParams, + ("command " + + ExecuteCommandParams::CLANGD_INSERT_HEADER_INCLUDE + + " called on non-added file " + FileURI.file()) + .str()); + auto Replaces = Server.insertInclude(FileURI.file(), *Code, + Params.includeInsertion->header); + if (!Replaces) { + std::string ErrMsg = + ("Failed to generate include insertion edits for adding " + + Params.includeInsertion->header + " into " + FileURI.file()) + .str(); + log(ErrMsg + ":" + llvm::toString(Replaces.takeError())); + replyError(ErrorCode::InternalError, ErrMsg); + return; + } + auto Edits = replacementsToEdits(*Code, *Replaces); + WorkspaceEdit WE; + WE.changes = {{FileURI.uri(), Edits}}; + + reply("Inserted header " + Params.includeInsertion->header); + ApplyEdit(std::move(WE)); } else { // We should not get here because ExecuteCommandParams would not have // parsed in the first place and this handler should not be called. But if Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original) +++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Fri Feb 16 06:15:55 2018 @@ -9,6 +9,7 @@ #include "ClangdServer.h" #include "CodeComplete.h" +#include "Headers.h" #include "SourceCode.h" #include "XRefs.h" #include "index/Merge.h" @@ -310,6 +311,47 @@ void ClangdServer::rename( BindWithForward(Action, File.str(), NewName.str(), std::move(Callback))); } +Expected<tooling::Replacements> +ClangdServer::insertInclude(PathRef File, StringRef Code, + llvm::StringRef Header) { + std::string ToInclude; + if (Header.startswith("<") || Header.startswith("\"")) { + ToInclude = Header; + } else { + auto U = URI::parse(Header); + if (!U) + return U.takeError(); + auto Resolved = URI::resolve(*U, /*HintPath=*/File); + if (!Resolved) + return Resolved.takeError(); + + auto FS = FSProvider.getTaggedFileSystem(File).Value; + tooling::CompileCommand CompileCommand = + CompileArgs.getCompileCommand(File); + FS->setCurrentWorkingDirectory(CompileCommand.Directory); + + auto Include = + shortenIncludePath(File, Code, *Resolved, CompileCommand, FS); + if (!Include) + return Include.takeError(); + if (Include->empty()) + return tooling::Replacements(); + ToInclude = std::move(*Include); + } + + auto Style = format::getStyle("file", File, "llvm"); + if (!Style) { + llvm::consumeError(Style.takeError()); + // FIXME(ioeric): needs more consistent style support in clangd server. + Style = format::getLLVMStyle(); + } + // Replacement with offset UINT_MAX and length 0 will be treated as include + // insertion. + tooling::Replacement R(File, /*Offset=*/UINT_MAX, 0, "#include " + ToInclude); + return format::cleanupAroundReplacements(Code, tooling::Replacements(R), + *Style); +} + llvm::Optional<std::string> ClangdServer::getDocument(PathRef File) { auto Latest = DraftMgr.getDraft(File); if (!Latest.Draft) Modified: clang-tools-extra/trunk/clangd/ClangdServer.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/ClangdServer.h (original) +++ clang-tools-extra/trunk/clangd/ClangdServer.h Fri Feb 16 06:15:55 2018 @@ -231,6 +231,13 @@ public: UniqueFunction<void(Expected<std::vector<tooling::Replacement>>)> Callback); + /// Inserts a new #include of \p Header into \p File, if it's not present. + /// \p Header is either an URI that can be resolved to an #include path that + /// is suitable to be inserted or a literal string quoted with <> or "" that + /// can be #included directly. + Expected<tooling::Replacements> insertInclude(PathRef File, StringRef Code, + StringRef Header); + /// Gets current document contents for \p File. Returns None if \p File is not /// currently tracked. /// FIXME(ibiryukov): This function is here to allow offset-to-Position Modified: clang-tools-extra/trunk/clangd/CodeComplete.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/CodeComplete.cpp (original) +++ clang-tools-extra/trunk/clangd/CodeComplete.cpp Fri Feb 16 06:15:55 2018 @@ -19,13 +19,16 @@ #include "Compiler.h" #include "FuzzyMatch.h" #include "Logger.h" +#include "SourceCode.h" #include "Trace.h" #include "index/Index.h" +#include "clang/Format/Format.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Index/USRGeneration.h" #include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Sema/Sema.h" +#include "clang/Tooling/Core/Replacement.h" #include "llvm/Support/Format.h" #include <queue> @@ -249,7 +252,8 @@ struct CompletionCandidate { } // Builds an LSP completion item. - CompletionItem build(const CompletionItemScores &Scores, + CompletionItem build(llvm::StringRef FileName, + const CompletionItemScores &Scores, const CodeCompleteOptions &Opts, CodeCompletionString *SemaCCS) const { assert(bool(SemaResult) == bool(SemaCCS)); @@ -282,6 +286,28 @@ struct CompletionCandidate { I.documentation = D->Documentation; if (I.detail.empty()) I.detail = D->CompletionDetail; + // We only insert #include for items with details, since we can't tell + // whether the file URI of the canonical declaration would be the + // canonical #include without checking IncludeHeader in the detail. + // FIXME: delay creating include insertion command to + // "completionItem/resolve", when it is supported + if (!D->IncludeHeader.empty() || + !IndexResult->CanonicalDeclaration.FileURI.empty()) { + // LSP favors additionalTextEdits over command. But we are still using + // command here because it would be expensive to calculate #include + // insertion edits for all candidates, and the include insertion edit + // is unlikely to conflict with the code completion edits. + Command Cmd; + // Command title is not added since this is not a user-facing command. + Cmd.command = ExecuteCommandParams::CLANGD_INSERT_HEADER_INCLUDE; + IncludeInsertion Insertion; + Insertion.header = D->IncludeHeader.empty() + ? IndexResult->CanonicalDeclaration.FileURI + : D->IncludeHeader; + Insertion.textDocument.uri = URIForFile(FileName); + Cmd.includeInsertion = std::move(Insertion); + I.command = std::move(Cmd); + } } } I.scoreInfo = Scores; @@ -806,6 +832,7 @@ clang::CodeCompleteOptions CodeCompleteO // This score is combined with the result quality score for the final score. // - TopN determines the results with the best score. class CodeCompleteFlow { + PathRef FileName; const CodeCompleteOptions &Opts; // Sema takes ownership of Recorder. Recorder is valid until Sema cleanup. std::unique_ptr<CompletionRecorder> RecorderOwner; @@ -816,9 +843,9 @@ class CodeCompleteFlow { public: // A CodeCompleteFlow object is only useful for calling run() exactly once. - CodeCompleteFlow(const CodeCompleteOptions &Opts) - : Opts(Opts), RecorderOwner(new CompletionRecorder(Opts)), - Recorder(*RecorderOwner) {} + CodeCompleteFlow(PathRef FileName, const CodeCompleteOptions &Opts) + : FileName(FileName), Opts(Opts), + RecorderOwner(new CompletionRecorder(Opts)), Recorder(*RecorderOwner) {} CompletionList run(const SemaCompleteInput &SemaCCInput) && { trace::Span Tracer("CodeCompleteFlow"); @@ -956,7 +983,7 @@ private: CodeCompletionString *SemaCCS = nullptr; if (auto *SR = Candidate.SemaResult) SemaCCS = Recorder.codeCompletionString(*SR, Opts.IncludeBriefComments); - return Candidate.build(Scores, Opts, SemaCCS); + return Candidate.build(FileName, Scores, Opts, SemaCCS); } }; @@ -967,8 +994,8 @@ CompletionList codeComplete(PathRef File IntrusiveRefCntPtr<vfs::FileSystem> VFS, std::shared_ptr<PCHContainerOperations> PCHs, CodeCompleteOptions Opts) { - return CodeCompleteFlow(Opts).run( - {FileName, Command, Preamble, Contents, Pos, VFS, PCHs}); + return CodeCompleteFlow(FileName, Opts) + .run({FileName, Command, Preamble, Contents, Pos, VFS, PCHs}); } SignatureHelp signatureHelp(PathRef FileName, Added: clang-tools-extra/trunk/clangd/Headers.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Headers.cpp?rev=325343&view=auto ============================================================================== --- clang-tools-extra/trunk/clangd/Headers.cpp (added) +++ clang-tools-extra/trunk/clangd/Headers.cpp Fri Feb 16 06:15:55 2018 @@ -0,0 +1,117 @@ +//===--- Headers.cpp - Include headers ---------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Headers.h" +#include "Compiler.h" +#include "Logger.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Tooling/CompilationDatabase.h" + +namespace clang { +namespace clangd { +namespace { + +class RecordHeaders : public PPCallbacks { +public: + RecordHeaders(std::set<std::string> &Headers) : Headers(Headers) {} + + void InclusionDirective(SourceLocation /*HashLoc*/, + const Token & /*IncludeTok*/, + llvm::StringRef /*FileName*/, bool /*IsAngled*/, + CharSourceRange /*FilenameRange*/, + const FileEntry *File, llvm::StringRef /*SearchPath*/, + llvm::StringRef /*RelativePath*/, + const Module * /*Imported*/) override { + if (File != nullptr && !File->tryGetRealPathName().empty()) + Headers.insert(File->tryGetRealPathName()); + } + +private: + std::set<std::string> &Headers; +}; + +} // namespace + +/// FIXME(ioeric): we might not want to insert an absolute include path if the +/// path is not shortened. +llvm::Expected<std::string> +shortenIncludePath(llvm::StringRef File, llvm::StringRef Code, + llvm::StringRef Header, + const tooling::CompileCommand &CompileCommand, + IntrusiveRefCntPtr<vfs::FileSystem> FS) { + // Set up a CompilerInstance and create a preprocessor to collect existing + // #include headers in \p Code. Preprocesor also provides HeaderSearch with + // which we can calculate the shortest include path for \p Header. + std::vector<const char *> Argv; + for (const auto &S : CompileCommand.CommandLine) + Argv.push_back(S.c_str()); + IgnoringDiagConsumer IgnoreDiags; + auto CI = clang::createInvocationFromCommandLine( + Argv, + CompilerInstance::createDiagnostics(new DiagnosticOptions(), &IgnoreDiags, + false), + FS); + if (!CI) + return llvm::make_error<llvm::StringError>( + "Failed to create a compiler instance for " + File, + llvm::inconvertibleErrorCode()); + CI->getFrontendOpts().DisableFree = false; + // Parse the main file to get all existing #includes in the file, and then we + // can make sure the same header (even with different include path) is not + // added more than once. + CI->getPreprocessorOpts().SingleFileParseMode = true; + + auto Clang = prepareCompilerInstance( + std::move(CI), /*Preamble=*/nullptr, + llvm::MemoryBuffer::getMemBuffer(Code, File), + std::make_shared<PCHContainerOperations>(), FS, IgnoreDiags); + auto &DiagOpts = Clang->getDiagnosticOpts(); + DiagOpts.IgnoreWarnings = true; + + if (Clang->getFrontendOpts().Inputs.empty()) + return llvm::make_error<llvm::StringError>( + "Empty frontend action inputs empty for file " + File, + llvm::inconvertibleErrorCode()); + PreprocessOnlyAction Action; + if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) + return llvm::make_error<llvm::StringError>( + "Failed to begin preprocessor only action for file " + File, + llvm::inconvertibleErrorCode()); + std::set<std::string> ExistingHeaders; + Clang->getPreprocessor().addPPCallbacks( + llvm::make_unique<RecordHeaders>(ExistingHeaders)); + if (!Action.Execute()) + return llvm::make_error<llvm::StringError>( + "Failed to execute preprocessor only action for file " + File, + llvm::inconvertibleErrorCode()); + if (ExistingHeaders.find(Header) != ExistingHeaders.end()) { + return llvm::make_error<llvm::StringError>( + Header + " is already included in " + File, + llvm::inconvertibleErrorCode()); + } + + auto &HeaderSearchInfo = Clang->getPreprocessor().getHeaderSearchInfo(); + bool IsSystem = false; + std::string Suggested = HeaderSearchInfo.suggestPathToFileForDiagnostics( + Header, CompileCommand.Directory, &IsSystem); + if (IsSystem) + Suggested = "<" + Suggested + ">"; + else + Suggested = "\"" + Suggested + "\""; + + log("Suggested #include for " + Header + " is: " + Suggested); + return Suggested; +} + +} // namespace clangd +} // namespace clang Added: clang-tools-extra/trunk/clangd/Headers.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Headers.h?rev=325343&view=auto ============================================================================== --- clang-tools-extra/trunk/clangd/Headers.h (added) +++ clang-tools-extra/trunk/clangd/Headers.h Fri Feb 16 06:15:55 2018 @@ -0,0 +1,37 @@ +//===--- Headers.h - Include headers -----------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEADERS_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEADERS_H + +#include "Path.h" +#include "clang/Basic/VirtualFileSystem.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace clangd { +/// Determines the preferred way to #include a file, taking into account the +/// search path. Usually this will prefer a shorter representation like +/// 'Foo/Bar.h' over a longer one like 'Baz/include/Foo/Bar.h'. +/// +/// \param Header is an absolute file path. +/// \return A quoted "path" or <path>. If \p Header is already (directly) +/// included in the file (including those included via different paths), this +/// returns an empty string. +llvm::Expected<std::string> +shortenIncludePath(PathRef File, llvm::StringRef Code, llvm::StringRef Header, + const tooling::CompileCommand &CompileCommand, + IntrusiveRefCntPtr<vfs::FileSystem> FS); + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEADERS_H Modified: clang-tools-extra/trunk/clangd/Protocol.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/Protocol.cpp (original) +++ clang-tools-extra/trunk/clangd/Protocol.cpp Fri Feb 16 06:15:55 2018 @@ -57,6 +57,10 @@ llvm::raw_ostream &operator<<(llvm::raw_ return OS << U.uri(); } +json::Expr toJSON(const TextDocumentIdentifier &R) { + return json::obj{{"uri", R.uri}}; +} + bool fromJSON(const json::Expr &Params, TextDocumentIdentifier &R) { json::ObjectMapper O(Params); return O && O.map("uri", R.uri); @@ -326,6 +330,8 @@ bool fromJSON(const json::Expr &Params, const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND = "clangd.applyFix"; +const llvm::StringLiteral ExecuteCommandParams::CLANGD_INSERT_HEADER_INCLUDE = + "clangd.insertInclude"; bool fromJSON(const json::Expr &Params, ExecuteCommandParams &R) { json::ObjectMapper O(Params); @@ -336,10 +342,22 @@ bool fromJSON(const json::Expr &Params, if (R.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) { return Args && Args->size() == 1 && fromJSON(Args->front(), R.workspaceEdit); + } else if (R.command == ExecuteCommandParams::CLANGD_INSERT_HEADER_INCLUDE) { + return Args && Args->size() == 1 && + fromJSON(Args->front(), R.includeInsertion); } return false; // Unrecognized command. } +json::Expr toJSON(const Command &C) { + auto Cmd = json::obj{{"title", C.title}, {"command", C.command}}; + if (C.workspaceEdit) + Cmd["arguments"] = {*C.workspaceEdit}; + else if (C.includeInsertion) + Cmd["arguments"] = {*C.includeInsertion}; + return std::move(Cmd); +} + json::Expr toJSON(const WorkspaceEdit &WE) { if (!WE.changes) return json::obj{}; @@ -349,6 +367,15 @@ json::Expr toJSON(const WorkspaceEdit &W return json::obj{{"changes", std::move(FileChanges)}}; } +bool fromJSON(const json::Expr &II, IncludeInsertion &R) { + json::ObjectMapper O(II); + return O && O.map("textDocument", R.textDocument) && + O.map("header", R.header); +} +json::Expr toJSON(const IncludeInsertion &II) { + return json::obj{{"textDocument", II.textDocument}, {"header", II.header}}; +} + json::Expr toJSON(const ApplyWorkspaceEditParams &Params) { return json::obj{{"edit", Params.edit}}; } @@ -380,6 +407,8 @@ json::Expr toJSON(const CompletionItem & Result["textEdit"] = *CI.textEdit; if (!CI.additionalTextEdits.empty()) Result["additionalTextEdits"] = json::ary(CI.additionalTextEdits); + if (CI.command) + Result["command"] = *CI.command; return std::move(Result); } Modified: clang-tools-extra/trunk/clangd/Protocol.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/Protocol.h (original) +++ clang-tools-extra/trunk/clangd/Protocol.h Fri Feb 16 06:15:55 2018 @@ -82,6 +82,7 @@ struct TextDocumentIdentifier { /// The text document's URI. URIForFile uri; }; +json::Expr toJSON(const TextDocumentIdentifier &); bool fromJSON(const json::Expr &, TextDocumentIdentifier &); struct Position { @@ -424,6 +425,17 @@ struct WorkspaceEdit { bool fromJSON(const json::Expr &, WorkspaceEdit &); json::Expr toJSON(const WorkspaceEdit &WE); +struct IncludeInsertion { + /// The document in which the command was invoked. + TextDocumentIdentifier textDocument; + + /// The header to be inserted. This could be either a URI ir a literal string + /// quoted with <> or "" that can be #included directly. + std::string header; +}; +bool fromJSON(const json::Expr &, IncludeInsertion &); +json::Expr toJSON(const IncludeInsertion &II); + /// Exact commands are not specified in the protocol so we define the /// ones supported by Clangd here. The protocol specifies the command arguments /// to be "any[]" but to make this safer and more manageable, each command we @@ -435,15 +447,25 @@ json::Expr toJSON(const WorkspaceEdit &W struct ExecuteCommandParams { // Command to apply fix-its. Uses WorkspaceEdit as argument. const static llvm::StringLiteral CLANGD_APPLY_FIX_COMMAND; + // Command to insert an #include into code. + const static llvm::StringLiteral CLANGD_INSERT_HEADER_INCLUDE; /// The command identifier, e.g. CLANGD_APPLY_FIX_COMMAND std::string command; // Arguments llvm::Optional<WorkspaceEdit> workspaceEdit; + + llvm::Optional<IncludeInsertion> includeInsertion; }; bool fromJSON(const json::Expr &, ExecuteCommandParams &); +struct Command : public ExecuteCommandParams { + std::string title; +}; + +json::Expr toJSON(const Command &C); + struct ApplyWorkspaceEditParams { WorkspaceEdit edit; }; @@ -560,12 +582,10 @@ struct CompletionItem { /// themselves. std::vector<TextEdit> additionalTextEdits; + llvm::Optional<Command> command; // TODO(krasimir): The following optional fields defined by the language // server protocol are unsupported: // - // command?: Command - An optional command that is executed *after* inserting - // this completion. - // // data?: any - A data entry field that is preserved on a completion item // between a completion and a completion resolve request. }; Modified: clang-tools-extra/trunk/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp (original) +++ clang-tools-extra/trunk/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp Fri Feb 16 06:15:55 2018 @@ -13,10 +13,12 @@ // //===---------------------------------------------------------------------===// +#include "index/CanonicalIncludes.h" #include "index/Index.h" #include "index/Merge.h" #include "index/SymbolCollector.h" #include "index/SymbolYAML.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Index/IndexDataConsumer.h" @@ -57,11 +59,19 @@ public: class WrappedIndexAction : public WrapperFrontendAction { public: WrappedIndexAction(std::shared_ptr<SymbolCollector> C, + std::unique_ptr<CanonicalIncludes> Includes, const index::IndexingOptions &Opts, tooling::ExecutionContext *Ctx) : WrapperFrontendAction( index::createIndexingAction(C, Opts, nullptr)), - Ctx(Ctx), Collector(C) {} + Ctx(Ctx), Collector(C), Includes(std::move(Includes)), + PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {} + + std::unique_ptr<ASTConsumer> + CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { + CI.getPreprocessor().addCommentHandler(PragmaHandler.get()); + return WrapperFrontendAction::CreateASTConsumer(CI, InFile); + } void EndSourceFileAction() override { WrapperFrontendAction::EndSourceFileAction(); @@ -78,6 +88,8 @@ public: private: tooling::ExecutionContext *Ctx; std::shared_ptr<SymbolCollector> Collector; + std::unique_ptr<CanonicalIncludes> Includes; + std::unique_ptr<CommentHandler> PragmaHandler; }; index::IndexingOptions IndexOpts; @@ -86,9 +98,13 @@ public: IndexOpts.IndexFunctionLocals = false; auto CollectorOpts = SymbolCollector::Options(); CollectorOpts.FallbackDir = AssumedHeaderDir; + CollectorOpts.CollectIncludePath = true; + auto Includes = llvm::make_unique<CanonicalIncludes>(); + addSystemHeadersMapping(Includes.get()); + CollectorOpts.Includes = Includes.get(); return new WrappedIndexAction( - std::make_shared<SymbolCollector>(std::move(CollectorOpts)), IndexOpts, - Ctx); + std::make_shared<SymbolCollector>(std::move(CollectorOpts)), + std::move(Includes), IndexOpts, Ctx); } tooling::ExecutionContext *Ctx; Added: clang-tools-extra/trunk/clangd/index/CanonicalIncludes.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/CanonicalIncludes.cpp?rev=325343&view=auto ============================================================================== --- clang-tools-extra/trunk/clangd/index/CanonicalIncludes.cpp (added) +++ clang-tools-extra/trunk/clangd/index/CanonicalIncludes.cpp Fri Feb 16 06:15:55 2018 @@ -0,0 +1,704 @@ +//===-- CanonicalIncludes.h - remap #inclue headers--------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CanonicalIncludes.h" +#include "llvm/Support/Regex.h" + +namespace clang { +namespace clangd { +namespace { +const char IWYUPragma[] = "// IWYU pragma: private, include "; +} // namespace + +void CanonicalIncludes::addMapping(llvm::StringRef Path, + llvm::StringRef CanonicalPath) { + addRegexMapping((llvm::Twine("^") + Path + "$").str(), CanonicalPath); +}; + +void CanonicalIncludes::addRegexMapping(llvm::StringRef RE, + llvm::StringRef CanonicalPath) { + this->RegexHeaderMappingTable.emplace_back(llvm::Regex(RE), CanonicalPath); +} + +llvm::StringRef CanonicalIncludes::mapHeader(llvm::StringRef Header) const { + for (auto &Entry : RegexHeaderMappingTable) { +#ifndef NDEBUG + std::string Dummy; + assert(Entry.first.isValid(Dummy) && "Regex should never be invalid!"); +#endif + if (Entry.first.match(Header)) + return Entry.second; + } + return Header; +} + +std::unique_ptr<CommentHandler> +collectIWYUHeaderMaps(CanonicalIncludes *Includes) { + class PragmaCommentHandler : public clang::CommentHandler { + public: + PragmaCommentHandler(CanonicalIncludes *Includes) : Includes(Includes) {} + + bool HandleComment(Preprocessor &PP, SourceRange Range) override { + StringRef Text = + Lexer::getSourceText(CharSourceRange::getCharRange(Range), + PP.getSourceManager(), PP.getLangOpts()); + if (!Text.consume_front(IWYUPragma)) + return false; + // FIXME(ioeric): resolve the header and store actual file path. For now, + // we simply assume the written header is suitable to be #included. + Includes->addMapping(PP.getSourceManager().getFilename(Range.getBegin()), + Text.startswith("<") ? Text.str() + : ("\"" + Text + "\"").str()); + return false; + } + + private: + CanonicalIncludes *const Includes; + }; + return llvm::make_unique<PragmaCommentHandler>(Includes); +} + +void addSystemHeadersMapping(CanonicalIncludes *Includes) { + static const std::vector<std::pair<const char *, const char *>> + SystemHeaderMap = { + {"include/__stddef_max_align_t.h$", "<cstddef>"}, + {"include/__wmmintrin_aes.h$", "<wmmintrin.h>"}, + {"include/__wmmintrin_pclmul.h$", "<wmmintrin.h>"}, + {"include/adxintrin.h$", "<immintrin.h>"}, + {"include/ammintrin.h$", "<ammintrin.h>"}, + {"include/avx2intrin.h$", "<immintrin.h>"}, + {"include/avx512bwintrin.h$", "<immintrin.h>"}, + {"include/avx512cdintrin.h$", "<immintrin.h>"}, + {"include/avx512dqintrin.h$", "<immintrin.h>"}, + {"include/avx512erintrin.h$", "<immintrin.h>"}, + {"include/avx512fintrin.h$", "<immintrin.h>"}, + {"include/avx512ifmaintrin.h$", "<immintrin.h>"}, + {"include/avx512ifmavlintrin.h$", "<immintrin.h>"}, + {"include/avx512pfintrin.h$", "<immintrin.h>"}, + {"include/avx512vbmiintrin.h$", "<immintrin.h>"}, + {"include/avx512vbmivlintrin.h$", "<immintrin.h>"}, + {"include/avx512vlbwintrin.h$", "<immintrin.h>"}, + {"include/avx512vlcdintrin.h$", "<immintrin.h>"}, + {"include/avx512vldqintrin.h$", "<immintrin.h>"}, + {"include/avx512vlintrin.h$", "<immintrin.h>"}, + {"include/avxintrin.h$", "<immintrin.h>"}, + {"include/bmi2intrin.h$", "<x86intrin.h>"}, + {"include/bmiintrin.h$", "<x86intrin.h>"}, + {"include/emmintrin.h$", "<emmintrin.h>"}, + {"include/f16cintrin.h$", "<emmintrin.h>"}, + {"include/float.h$", "<cfloat>"}, + {"include/fma4intrin.h$", "<x86intrin.h>"}, + {"include/fmaintrin.h$", "<immintrin.h>"}, + {"include/fxsrintrin.h$", "<immintrin.h>"}, + {"include/ia32intrin.h$", "<x86intrin.h>"}, + {"include/immintrin.h$", "<immintrin.h>"}, + {"include/inttypes.h$", "<cinttypes>"}, + {"include/limits.h$", "<climits>"}, + {"include/lzcntintrin.h$", "<x86intrin.h>"}, + {"include/mm3dnow.h$", "<mm3dnow.h>"}, + {"include/mm_malloc.h$", "<mm_malloc.h>"}, + {"include/mmintrin.h$", "<mmintrin>"}, + {"include/mwaitxintrin.h$", "<x86intrin.h>"}, + {"include/pkuintrin.h$", "<immintrin.h>"}, + {"include/pmmintrin.h$", "<pmmintrin.h>"}, + {"include/popcntintrin.h$", "<popcntintrin.h>"}, + {"include/prfchwintrin.h$", "<x86intrin.h>"}, + {"include/rdseedintrin.h$", "<x86intrin.h>"}, + {"include/rtmintrin.h$", "<immintrin.h>"}, + {"include/shaintrin.h$", "<immintrin.h>"}, + {"include/smmintrin.h$", "<smmintrin.h>"}, + {"include/stdalign.h$", "<cstdalign>"}, + {"include/stdarg.h$", "<cstdarg>"}, + {"include/stdbool.h$", "<cstdbool>"}, + {"include/stddef.h$", "<cstddef>"}, + {"include/stdint.h$", "<cstdint>"}, + {"include/tbmintrin.h$", "<x86intrin.h>"}, + {"include/tmmintrin.h$", "<tmmintrin.h>"}, + {"include/wmmintrin.h$", "<wmmintrin.h>"}, + {"include/x86intrin.h$", "<x86intrin.h>"}, + {"include/xmmintrin.h$", "<xmmintrin.h>"}, + {"include/xopintrin.h$", "<x86intrin.h>"}, + {"include/xsavecintrin.h$", "<immintrin.h>"}, + {"include/xsaveintrin.h$", "<immintrin.h>"}, + {"include/xsaveoptintrin.h$", "<immintrin.h>"}, + {"include/xsavesintrin.h$", "<immintrin.h>"}, + {"include/xtestintrin.h$", "<immintrin.h>"}, + {"include/_G_config.h$", "<cstdio>"}, + {"include/assert.h$", "<cassert>"}, + {"algorithm$", "<algorithm>"}, + {"array$", "<array>"}, + {"atomic$", "<atomic>"}, + {"backward/auto_ptr.h$", "<memory>"}, + {"backward/binders.h$", "<string>"}, + {"bits/algorithmfwd.h$", "<algorithm>"}, + {"bits/alloc_traits.h$", "<unordered_set>"}, + {"bits/allocator.h$", "<string>"}, + {"bits/atomic_base.h$", "<atomic>"}, + {"bits/atomic_lockfree_defines.h$", "<exception>"}, + {"bits/basic_ios.h$", "<ios>"}, + {"bits/basic_ios.tcc$", "<ios>"}, + {"bits/basic_string.h$", "<string>"}, + {"bits/basic_string.tcc$", "<string>"}, + {"bits/char_traits.h$", "<string>"}, + {"bits/codecvt.h$", "<locale>"}, + {"bits/concept_check.h$", "<numeric>"}, + {"bits/cpp_type_traits.h$", "<cmath>"}, + {"bits/cxxabi_forced.h$", "<cxxabi.h>"}, + {"bits/deque.tcc$", "<deque>"}, + {"bits/exception_defines.h$", "<exception>"}, + {"bits/exception_ptr.h$", "<exception>"}, + {"bits/forward_list.h$", "<forward_list>"}, + {"bits/forward_list.tcc$", "<forward_list>"}, + {"bits/fstream.tcc$", "<fstream>"}, + {"bits/functexcept.h$", "<list>"}, + {"bits/functional_hash.h$", "<string>"}, + {"bits/gslice.h$", "<valarray>"}, + {"bits/gslice_array.h$", "<valarray>"}, + {"bits/hash_bytes.h$", "<typeinfo>"}, + {"bits/hashtable.h$", "<unordered_set>"}, + {"bits/hashtable_policy.h$", "<unordered_set>"}, + {"bits/indirect_array.h$", "<valarray>"}, + {"bits/ios_base.h$", "<ios>"}, + {"bits/istream.tcc$", "<istream>"}, + {"bits/list.tcc$", "<list>"}, + {"bits/locale_classes.h$", "<locale>"}, + {"bits/locale_classes.tcc$", "<locale>"}, + {"bits/locale_facets.h$", "<locale>"}, + {"bits/locale_facets.tcc$", "<locale>"}, + {"bits/locale_facets_nonio.h$", "<locale>"}, + {"bits/locale_facets_nonio.tcc$", "<locale>"}, + {"bits/localefwd.h$", "<locale>"}, + {"bits/mask_array.h$", "<valarray>"}, + {"bits/memoryfwd.h$", "<memory>"}, + {"bits/move.h$", "<utility>"}, + {"bits/nested_exception.h$", "<exception>"}, + {"bits/ostream.tcc$", "<ostream>"}, + {"bits/ostream_insert.h$", "<ostream>"}, + {"bits/postypes.h$", "<iosfwd>"}, + {"bits/ptr_traits.h$", "<memory>"}, + {"bits/random.h$", "<random>"}, + {"bits/random.tcc$", "<random>"}, + {"bits/range_access.h$", "<iterator>"}, + {"bits/regex.h$", "<regex>"}, + {"bits/regex_compiler.h$", "<regex>"}, + {"bits/regex_constants.h$", "<regex>"}, + {"bits/regex_cursor.h$", "<regex>"}, + {"bits/regex_error.h$", "<regex>"}, + {"bits/regex_grep_matcher.h$", "<regex>"}, + {"bits/regex_grep_matcher.tcc$", "<regex>"}, + {"bits/regex_nfa.h$", "<regex>"}, + {"bits/shared_ptr.h$", "<memory>"}, + {"bits/shared_ptr_base.h$", "<memory>"}, + {"bits/slice_array.h$", "<valarray>"}, + {"bits/sstream.tcc$", "<sstream>"}, + {"bits/stl_algo.h$", "<algorithm>"}, + {"bits/stl_algobase.h$", "<list>"}, + {"bits/stl_bvector.h$", "<vector>"}, + {"bits/stl_construct.h$", "<deque>"}, + {"bits/stl_deque.h$", "<deque>"}, + {"bits/stl_function.h$", "<string>"}, + {"bits/stl_heap.h$", "<queue>"}, + {"bits/stl_iterator.h$", "<iterator>"}, + {"bits/stl_iterator_base_funcs.h$", "<iterator>"}, + {"bits/stl_iterator_base_types.h$", "<numeric>"}, + {"bits/stl_list.h$", "<list>"}, + {"bits/stl_map.h$", "<map>"}, + {"bits/stl_multimap.h$", "<map>"}, + {"bits/stl_multiset.h$", "<set>"}, + {"bits/stl_numeric.h$", "<numeric>"}, + {"bits/stl_pair.h$", "<utility>"}, + {"bits/stl_queue.h$", "<queue>"}, + {"bits/stl_raw_storage_iter.h$", "<memory>"}, + {"bits/stl_relops.h$", "<utility>"}, + {"bits/stl_set.h$", "<set>"}, + {"bits/stl_stack.h$", "<stack>"}, + {"bits/stl_tempbuf.h$", "<memory>"}, + {"bits/stl_tree.h$", "<map>"}, + {"bits/stl_uninitialized.h$", "<deque>"}, + {"bits/stl_vector.h$", "<vector>"}, + {"bits/stream_iterator.h$", "<iterator>"}, + {"bits/streambuf.tcc$", "<streambuf>"}, + {"bits/streambuf_iterator.h$", "<iterator>"}, + {"bits/stringfwd.h$", "<string>"}, + {"bits/unique_ptr.h$", "<memory>"}, + {"bits/unordered_map.h$", "<unordered_map>"}, + {"bits/unordered_set.h$", "<unordered_set>"}, + {"bits/uses_allocator.h$", "<tuple>"}, + {"bits/valarray_after.h$", "<valarray>"}, + {"bits/valarray_array.h$", "<valarray>"}, + {"bits/valarray_array.tcc$", "<valarray>"}, + {"bits/valarray_before.h$", "<valarray>"}, + {"bits/vector.tcc$", "<vector>"}, + {"bitset$", "<bitset>"}, + {"ccomplex$", "<ccomplex>"}, + {"cctype$", "<cctype>"}, + {"cerrno$", "<cerrno>"}, + {"cfenv$", "<cfenv>"}, + {"cfloat$", "<cfloat>"}, + {"chrono$", "<chrono>"}, + {"cinttypes$", "<cinttypes>"}, + {"climits$", "<climits>"}, + {"clocale$", "<clocale>"}, + {"cmath$", "<cmath>"}, + {"complex$", "<complex>"}, + {"complex.h$", "<complex.h>"}, + {"condition_variable$", "<condition_variable>"}, + {"csetjmp$", "<csetjmp>"}, + {"csignal$", "<csignal>"}, + {"cstdalign$", "<cstdalign>"}, + {"cstdarg$", "<cstdarg>"}, + {"cstdbool$", "<cstdbool>"}, + {"cstdint$", "<cstdint>"}, + {"cstdio$", "<cstdio>"}, + {"cstdlib$", "<cstdlib>"}, + {"cstring$", "<cstring>"}, + {"ctgmath$", "<ctgmath>"}, + {"ctime$", "<ctime>"}, + {"cwchar$", "<cwchar>"}, + {"cwctype$", "<cwctype>"}, + {"cxxabi.h$", "<cxxabi.h>"}, + {"debug/debug.h$", "<numeric>"}, + {"deque$", "<deque>"}, + {"exception$", "<exception>"}, + {"ext/alloc_traits.h$", "<deque>"}, + {"ext/atomicity.h$", "<memory>"}, + {"ext/concurrence.h$", "<memory>"}, + {"ext/new_allocator.h$", "<string>"}, + {"ext/numeric_traits.h$", "<list>"}, + {"ext/string_conversions.h$", "<string>"}, + {"ext/type_traits.h$", "<cmath>"}, + {"fenv.h$", "<fenv.h>"}, + {"forward_list$", "<forward_list>"}, + {"fstream$", "<fstream>"}, + {"functional$", "<functional>"}, + {"future$", "<future>"}, + {"initializer_list$", "<initializer_list>"}, + {"iomanip$", "<iomanip>"}, + {"ios$", "<ios>"}, + {"iosfwd$", "<iosfwd>"}, + {"iostream$", "<iostream>"}, + {"istream$", "<istream>"}, + {"iterator$", "<iterator>"}, + {"limits$", "<limits>"}, + {"list$", "<list>"}, + {"locale$", "<locale>"}, + {"map$", "<map>"}, + {"memory$", "<memory>"}, + {"mutex$", "<mutex>"}, + {"new$", "<new>"}, + {"numeric$", "<numeric>"}, + {"ostream$", "<ostream>"}, + {"queue$", "<queue>"}, + {"random$", "<random>"}, + {"ratio$", "<ratio>"}, + {"regex$", "<regex>"}, + {"scoped_allocator$", "<scoped_allocator>"}, + {"set$", "<set>"}, + {"sstream$", "<sstream>"}, + {"stack$", "<stack>"}, + {"stdexcept$", "<stdexcept>"}, + {"streambuf$", "<streambuf>"}, + {"string$", "<string>"}, + {"system_error$", "<system_error>"}, + {"tgmath.h$", "<tgmath.h>"}, + {"thread$", "<thread>"}, + {"tuple$", "<tuple>"}, + {"type_traits$", "<type_traits>"}, + {"typeindex$", "<typeindex>"}, + {"typeinfo$", "<typeinfo>"}, + {"unordered_map$", "<unordered_map>"}, + {"unordered_set$", "<unordered_set>"}, + {"utility$", "<utility>"}, + {"valarray$", "<valarray>"}, + {"vector$", "<vector>"}, + {"include/complex.h$", "<complex.h>"}, + {"include/ctype.h$", "<cctype>"}, + {"include/errno.h$", "<cerrno>"}, + {"include/fenv.h$", "<fenv.h>"}, + {"include/inttypes.h$", "<cinttypes>"}, + {"include/libio.h$", "<cstdio>"}, + {"include/limits.h$", "<climits>"}, + {"include/locale.h$", "<clocale>"}, + {"include/math.h$", "<cmath>"}, + {"include/setjmp.h$", "<csetjmp>"}, + {"include/signal.h$", "<csignal>"}, + {"include/stdint.h$", "<cstdint>"}, + {"include/stdio.h$", "<cstdio>"}, + {"include/stdlib.h$", "<cstdlib>"}, + {"include/string.h$", "<cstring>"}, + {"include/time.h$", "<ctime>"}, + {"include/wchar.h$", "<cwchar>"}, + {"include/wctype.h$", "<cwctype>"}, + {"bits/cmathcalls.h$", "<complex.h>"}, + {"bits/errno.h$", "<cerrno>"}, + {"bits/fenv.h$", "<fenv.h>"}, + {"bits/huge_val.h$", "<cmath>"}, + {"bits/huge_valf.h$", "<cmath>"}, + {"bits/huge_vall.h$", "<cmath>"}, + {"bits/inf.h$", "<cmath>"}, + {"bits/local_lim.h$", "<climits>"}, + {"bits/locale.h$", "<clocale>"}, + {"bits/mathcalls.h$", "<math.h>"}, + {"bits/mathdef.h$", "<cmath>"}, + {"bits/nan.h$", "<cmath>"}, + {"bits/posix1_lim.h$", "<climits>"}, + {"bits/posix2_lim.h$", "<climits>"}, + {"bits/setjmp.h$", "<csetjmp>"}, + {"bits/sigaction.h$", "<csignal>"}, + {"bits/sigcontext.h$", "<csignal>"}, + {"bits/siginfo.h$", "<csignal>"}, + {"bits/signum.h$", "<csignal>"}, + {"bits/sigset.h$", "<csignal>"}, + {"bits/sigstack.h$", "<csignal>"}, + {"bits/stdio_lim.h$", "<cstdio>"}, + {"bits/sys_errlist.h$", "<cstdio>"}, + {"bits/time.h$", "<ctime>"}, + {"bits/timex.h$", "<ctime>"}, + {"bits/typesizes.h$", "<cstdio>"}, + {"bits/wchar.h$", "<cwchar>"}, + {"bits/wordsize.h$", "<csetjmp>"}, + {"bits/xopen_lim.h$", "<climits>"}, + {"include/xlocale.h$", "<cstring>"}, + {"bits/atomic_word.h$", "<memory>"}, + {"bits/basic_file.h$", "<fstream>"}, + {"bits/c\\+\\+allocator.h$", "<string>"}, + {"bits/c\\+\\+config.h$", "<iosfwd>"}, + {"bits/c\\+\\+io.h$", "<ios>"}, + {"bits/c\\+\\+locale.h$", "<locale>"}, + {"bits/cpu_defines.h$", "<iosfwd>"}, + {"bits/ctype_base.h$", "<locale>"}, + {"bits/cxxabi_tweaks.h$", "<cxxabi.h>"}, + {"bits/error_constants.h$", "<system_error>"}, + {"bits/gthr-default.h$", "<memory>"}, + {"bits/gthr.h$", "<memory>"}, + {"bits/opt_random.h$", "<random>"}, + {"bits/os_defines.h$", "<iosfwd>"}, + // GNU C headers + {"include/aio.h$", "<aio.h>"}, + {"include/aliases.h$", "<aliases.h>"}, + {"include/alloca.h$", "<alloca.h>"}, + {"include/ar.h$", "<ar.h>"}, + {"include/argp.h$", "<argp.h>"}, + {"include/argz.h$", "<argz.h>"}, + {"include/arpa/nameser.h$", "<resolv.h>"}, + {"include/arpa/nameser_compat.h$", "<resolv.h>"}, + {"include/byteswap.h$", "<byteswap.h>"}, + {"include/cpio.h$", "<cpio.h>"}, + {"include/crypt.h$", "<crypt.h>"}, + {"include/dirent.h$", "<dirent.h>"}, + {"include/dlfcn.h$", "<dlfcn.h>"}, + {"include/elf.h$", "<elf.h>"}, + {"include/endian.h$", "<endian.h>"}, + {"include/envz.h$", "<envz.h>"}, + {"include/err.h$", "<err.h>"}, + {"include/error.h$", "<error.h>"}, + {"include/execinfo.h$", "<execinfo.h>"}, + {"include/fcntl.h$", "<fcntl.h>"}, + {"include/features.h$", "<features.h>"}, + {"include/fenv.h$", "<fenv.h>"}, + {"include/fmtmsg.h$", "<fmtmsg.h>"}, + {"include/fnmatch.h$", "<fnmatch.h>"}, + {"include/fstab.h$", "<fstab.h>"}, + {"include/fts.h$", "<fts.h>"}, + {"include/ftw.h$", "<ftw.h>"}, + {"include/gconv.h$", "<gconv.h>"}, + {"include/getopt.h$", "<getopt.h>"}, + {"include/glob.h$", "<glob.h>"}, + {"include/grp.h$", "<grp.h>"}, + {"include/gshadow.h$", "<gshadow.h>"}, + {"include/iconv.h$", "<iconv.h>"}, + {"include/ifaddrs.h$", "<ifaddrs.h>"}, + {"include/kdb.h$", "<kdb.h>"}, + {"include/langinfo.h$", "<langinfo.h>"}, + {"include/libgen.h$", "<libgen.h>"}, + {"include/libintl.h$", "<libintl.h>"}, + {"include/link.h$", "<link.h>"}, + {"include/malloc.h$", "<malloc.h>"}, + {"include/mcheck.h$", "<mcheck.h>"}, + {"include/memory.h$", "<memory.h>"}, + {"include/mntent.h$", "<mntent.h>"}, + {"include/monetary.h$", "<monetary.h>"}, + {"include/mqueue.h$", "<mqueue.h>"}, + {"include/netdb.h$", "<netdb.h>"}, + {"include/netinet/in.h$", "<netinet/in.h>"}, + {"include/nl_types.h$", "<nl_types.h>"}, + {"include/nss.h$", "<nss.h>"}, + {"include/obstack.h$", "<obstack.h>"}, + {"include/panel.h$", "<panel.h>"}, + {"include/paths.h$", "<paths.h>"}, + {"include/printf.h$", "<printf.h>"}, + {"include/profile.h$", "<profile.h>"}, + {"include/pthread.h$", "<pthread.h>"}, + {"include/pty.h$", "<pty.h>"}, + {"include/pwd.h$", "<pwd.h>"}, + {"include/re_comp.h$", "<re_comp.h>"}, + {"include/regex.h$", "<regex.h>"}, + {"include/regexp.h$", "<regexp.h>"}, + {"include/resolv.h$", "<resolv.h>"}, + {"include/rpc/netdb.h$", "<netdb.h>"}, + {"include/sched.h$", "<sched.h>"}, + {"include/search.h$", "<search.h>"}, + {"include/semaphore.h$", "<semaphore.h>"}, + {"include/sgtty.h$", "<sgtty.h>"}, + {"include/shadow.h$", "<shadow.h>"}, + {"include/spawn.h$", "<spawn.h>"}, + {"include/stab.h$", "<stab.h>"}, + {"include/stdc-predef.h$", "<stdc-predef.h>"}, + {"include/stdio_ext.h$", "<stdio_ext.h>"}, + {"include/strings.h$", "<strings.h>"}, + {"include/stropts.h$", "<stropts.h>"}, + {"include/sudo_plugin.h$", "<sudo_plugin.h>"}, + {"include/sysexits.h$", "<sysexits.h>"}, + {"include/tar.h$", "<tar.h>"}, + {"include/tcpd.h$", "<tcpd.h>"}, + {"include/term.h$", "<term.h>"}, + {"include/term_entry.h$", "<term_entry.h>"}, + {"include/termcap.h$", "<termcap.h>"}, + {"include/termios.h$", "<termios.h>"}, + {"include/thread_db.h$", "<thread_db.h>"}, + {"include/tic.h$", "<tic.h>"}, + {"include/ttyent.h$", "<ttyent.h>"}, + {"include/uchar.h$", "<uchar.h>"}, + {"include/ucontext.h$", "<ucontext.h>"}, + {"include/ulimit.h$", "<ulimit.h>"}, + {"include/unctrl.h$", "<unctrl.h>"}, + {"include/unistd.h$", "<unistd.h>"}, + {"include/utime.h$", "<utime.h>"}, + {"include/utmp.h$", "<utmp.h>"}, + {"include/utmpx.h$", "<utmpx.h>"}, + {"include/values.h$", "<values.h>"}, + {"include/wordexp.h$", "<wordexp.h>"}, + {"fpu_control.h$", "<fpu_control.h>"}, + {"ieee754.h$", "<ieee754.h>"}, + {"include/xlocale.h$", "<xlocale.h>"}, + {"gnu/lib-names.h$", "<gnu/lib-names.h>"}, + {"gnu/libc-version.h$", "<gnu/libc-version.h>"}, + {"gnu/option-groups.h$", "<gnu/option-groups.h>"}, + {"gnu/stubs-32.h$", "<gnu/stubs-32.h>"}, + {"gnu/stubs-64.h$", "<gnu/stubs-64.h>"}, + {"gnu/stubs-x32.h$", "<gnu/stubs-x32.h>"}, + {"include/rpc/auth_des.h$", "<rpc/auth_des.h>"}, + {"include/rpc/rpc_msg.h$", "<rpc/rpc_msg.h>"}, + {"include/rpc/pmap_clnt.h$", "<rpc/pmap_clnt.h>"}, + {"include/rpc/rpc.h$", "<rpc/rpc.h>"}, + {"include/rpc/types.h$", "<rpc/types.h>"}, + {"include/rpc/auth_unix.h$", "<rpc/auth_unix.h>"}, + {"include/rpc/key_prot.h$", "<rpc/key_prot.h>"}, + {"include/rpc/pmap_prot.h$", "<rpc/pmap_prot.h>"}, + {"include/rpc/auth.h$", "<rpc/auth.h>"}, + {"include/rpc/svc_auth.h$", "<rpc/svc_auth.h>"}, + {"include/rpc/xdr.h$", "<rpc/xdr.h>"}, + {"include/rpc/pmap_rmt.h$", "<rpc/pmap_rmt.h>"}, + {"include/rpc/des_crypt.h$", "<rpc/des_crypt.h>"}, + {"include/rpc/svc.h$", "<rpc/svc.h>"}, + {"include/rpc/rpc_des.h$", "<rpc/rpc_des.h>"}, + {"include/rpc/clnt.h$", "<rpc/clnt.h>"}, + {"include/scsi/scsi.h$", "<scsi/scsi.h>"}, + {"include/scsi/sg.h$", "<scsi/sg.h>"}, + {"include/scsi/scsi_ioctl.h$", "<scsi/scsi_ioctl>"}, + {"include/netrose/rose.h$", "<netrose/rose.h>"}, + {"include/nfs/nfs.h$", "<nfs/nfs.h>"}, + {"include/netatalk/at.h$", "<netatalk/at.h>"}, + {"include/netinet/ether.h$", "<netinet/ether.h>"}, + {"include/netinet/icmp6.h$", "<netinet/icmp6.h>"}, + {"include/netinet/if_ether.h$", "<netinet/if_ether.h>"}, + {"include/netinet/if_fddi.h$", "<netinet/if_fddi.h>"}, + {"include/netinet/if_tr.h$", "<netinet/if_tr.h>"}, + {"include/netinet/igmp.h$", "<netinet/igmp.h>"}, + {"include/netinet/in.h$", "<netinet/in.h>"}, + {"include/netinet/in_systm.h$", "<netinet/in_systm.h>"}, + {"include/netinet/ip.h$", "<netinet/ip.h>"}, + {"include/netinet/ip6.h$", "<netinet/ip6.h>"}, + {"include/netinet/ip_icmp.h$", "<netinet/ip_icmp.h>"}, + {"include/netinet/tcp.h$", "<netinet/tcp.h>"}, + {"include/netinet/udp.h$", "<netinet/udp.h>"}, + {"include/netrom/netrom.h$", "<netrom/netrom.h>"}, + {"include/protocols/routed.h$", "<protocols/routed.h>"}, + {"include/protocols/rwhod.h$", "<protocols/rwhod.h>"}, + {"include/protocols/talkd.h$", "<protocols/talkd.h>"}, + {"include/protocols/timed.h$", "<protocols/timed.h>"}, + {"include/rpcsvc/klm_prot.x$", "<rpcsvc/klm_prot.x>"}, + {"include/rpcsvc/rstat.h$", "<rpcsvc/rstat.h>"}, + {"include/rpcsvc/spray.x$", "<rpcsvc/spray.x>"}, + {"include/rpcsvc/nlm_prot.x$", "<rpcsvc/nlm_prot.x>"}, + {"include/rpcsvc/nis_callback.x$", "<rpcsvc/nis_callback.x>"}, + {"include/rpcsvc/yp.h$", "<rpcsvc/yp.h>"}, + {"include/rpcsvc/yp.x$", "<rpcsvc/yp.x>"}, + {"include/rpcsvc/nfs_prot.h$", "<rpcsvc/nfs_prot.h>"}, + {"include/rpcsvc/rex.h$", "<rpcsvc/rex.h>"}, + {"include/rpcsvc/yppasswd.h$", "<rpcsvc/yppasswd.h>"}, + {"include/rpcsvc/rex.x$", "<rpcsvc/rex.x>"}, + {"include/rpcsvc/nis_tags.h$", "<rpcsvc/nis_tags.h>"}, + {"include/rpcsvc/nis_callback.h$", "<rpcsvc/nis_callback.h>"}, + {"include/rpcsvc/nfs_prot.x$", "<rpcsvc/nfs_prot.x>"}, + {"include/rpcsvc/bootparam_prot.x$", "<rpcsvc/bootparam_prot.x>"}, + {"include/rpcsvc/rusers.x$", "<rpcsvc/rusers.x>"}, + {"include/rpcsvc/rquota.x$", "<rpcsvc/rquota.x>"}, + {"include/rpcsvc/nis.h$", "<rpcsvc/nis.h>"}, + {"include/rpcsvc/nislib.h$", "<rpcsvc/nislib.h>"}, + {"include/rpcsvc/ypupd.h$", "<rpcsvc/ypupd.h>"}, + {"include/rpcsvc/bootparam.h$", "<rpcsvc/bootparam.h>"}, + {"include/rpcsvc/spray.h$", "<rpcsvc/spray.h>"}, + {"include/rpcsvc/key_prot.h$", "<rpcsvc/key_prot.h>"}, + {"include/rpcsvc/klm_prot.h$", "<rpcsvc/klm_prot.h>"}, + {"include/rpcsvc/sm_inter.h$", "<rpcsvc/sm_inter.h>"}, + {"include/rpcsvc/nlm_prot.h$", "<rpcsvc/nlm_prot.h>"}, + {"include/rpcsvc/yp_prot.h$", "<rpcsvc/yp_prot.h>"}, + {"include/rpcsvc/ypclnt.h$", "<rpcsvc/ypclnt.h>"}, + {"include/rpcsvc/rstat.x$", "<rpcsvc/rstat.x>"}, + {"include/rpcsvc/rusers.h$", "<rpcsvc/rusers.h>"}, + {"include/rpcsvc/key_prot.x$", "<rpcsvc/key_prot.x>"}, + {"include/rpcsvc/sm_inter.x$", "<rpcsvc/sm_inter.x>"}, + {"include/rpcsvc/rquota.h$", "<rpcsvc/rquota.h>"}, + {"include/rpcsvc/nis.x$", "<rpcsvc/nis.x>"}, + {"include/rpcsvc/bootparam_prot.h$", "<rpcsvc/bootparam_prot.h>"}, + {"include/rpcsvc/mount.h$", "<rpcsvc/mount.h>"}, + {"include/rpcsvc/mount.x$", "<rpcsvc/mount.x>"}, + {"include/rpcsvc/nis_object.x$", "<rpcsvc/nis_object.x>"}, + {"include/rpcsvc/yppasswd.x$", "<rpcsvc/yppasswd.x>"}, + {"sys/acct.h$", "<sys/acct.h>"}, + {"sys/auxv.h$", "<sys/auxv.h>"}, + {"sys/cdefs.h$", "<sys/cdefs.h>"}, + {"sys/debugreg.h$", "<sys/debugreg.h>"}, + {"sys/dir.h$", "<sys/dir.h>"}, + {"sys/elf.h$", "<sys/elf.h>"}, + {"sys/epoll.h$", "<sys/epoll.h>"}, + {"sys/eventfd.h$", "<sys/eventfd.h>"}, + {"sys/fanotify.h$", "<sys/fanotify.h>"}, + {"sys/file.h$", "<sys/file.h>"}, + {"sys/fsuid.h$", "<sys/fsuid.h>"}, + {"sys/gmon.h$", "<sys/gmon.h>"}, + {"sys/gmon_out.h$", "<sys/gmon_out.h>"}, + {"sys/inotify.h$", "<sys/inotify.h>"}, + {"sys/io.h$", "<sys/io.h>"}, + {"sys/ioctl.h$", "<sys/ioctl.h>"}, + {"sys/ipc.h$", "<sys/ipc.h>"}, + {"sys/kd.h$", "<sys/kd.h>"}, + {"sys/kdaemon.h$", "<sys/kdaemon.h>"}, + {"sys/klog.h$", "<sys/klog.h>"}, + {"sys/mman.h$", "<sys/mman.h>"}, + {"sys/mount.h$", "<sys/mount.h>"}, + {"sys/msg.h$", "<sys/msg.h>"}, + {"sys/mtio.h$", "<sys/mtio.h>"}, + {"sys/param.h$", "<sys/param.h>"}, + {"sys/pci.h$", "<sys/pci.h>"}, + {"sys/perm.h$", "<sys/perm.h>"}, + {"sys/personality.h$", "<sys/personality.h>"}, + {"sys/poll.h$", "<sys/poll.h>"}, + {"sys/prctl.h$", "<sys/prctl.h>"}, + {"sys/procfs.h$", "<sys/procfs.h>"}, + {"sys/profil.h$", "<sys/profil.h>"}, + {"sys/ptrace.h$", "<sys/ptrace.h>"}, + {"sys/queue.h$", "<sys/queue.h>"}, + {"sys/quota.h$", "<sys/quota.h>"}, + {"sys/raw.h$", "<sys/raw.h>"}, + {"sys/reboot.h$", "<sys/reboot.h>"}, + {"sys/reg.h$", "<sys/reg.h>"}, + {"sys/resource.h$", "<sys/resource.h>"}, + {"sys/select.h$", "<sys/select.h>"}, + {"sys/sem.h$", "<sys/sem.h>"}, + {"sys/sendfile.h$", "<sys/sendfile.h>"}, + {"sys/shm.h$", "<sys/shm.h>"}, + {"sys/signalfd.h$", "<sys/signalfd.h>"}, + {"sys/socket.h$", "<sys/socket.h>"}, + {"sys/stat.h$", "<sys/stat.h>"}, + {"sys/statfs.h$", "<sys/statfs.h>"}, + {"sys/statvfs.h$", "<sys/statvfs.h>"}, + {"sys/swap.h$", "<sys/swap.h>"}, + {"sys/syscall.h$", "<sys/syscall.h>"}, + {"sys/sysctl.h$", "<sys/sysctl.h>"}, + {"sys/sysinfo.h$", "<sys/sysinfo.h>"}, + {"sys/syslog.h$", "<sys/syslog.h>"}, + {"sys/sysmacros.h$", "<sys/sysmacros.h>"}, + {"sys/termios.h$", "<sys/termios.h>"}, + {"sys/time.h$", "<sys/select.h>"}, + {"sys/timeb.h$", "<sys/timeb.h>"}, + {"sys/timerfd.h$", "<sys/timerfd.h>"}, + {"sys/times.h$", "<sys/times.h>"}, + {"sys/timex.h$", "<sys/timex.h>"}, + {"sys/ttychars.h$", "<sys/ttychars.h>"}, + {"sys/ttydefaults.h$", "<sys/ttydefaults.h>"}, + {"sys/types.h$", "<sys/types.h>"}, + {"sys/ucontext.h$", "<sys/ucontext.h>"}, + {"sys/uio.h$", "<sys/uio.h>"}, + {"sys/un.h$", "<sys/un.h>"}, + {"sys/user.h$", "<sys/user.h>"}, + {"sys/ustat.h$", "<sys/ustat.h>"}, + {"sys/utsname.h$", "<sys/utsname.h>"}, + {"sys/vlimit.h$", "<sys/vlimit.h>"}, + {"sys/vm86.h$", "<sys/vm86.h>"}, + {"sys/vtimes.h$", "<sys/vtimes.h>"}, + {"sys/wait.h$", "<sys/wait.h>"}, + {"sys/xattr.h$", "<sys/xattr.h>"}, + {"bits/epoll.h$", "<sys/epoll.h>"}, + {"bits/eventfd.h$", "<sys/eventfd.h>"}, + {"bits/inotify.h$", "<sys/inotify.h>"}, + {"bits/ipc.h$", "<sys/ipc.h>"}, + {"bits/ipctypes.h$", "<sys/ipc.h>"}, + {"bits/mman-linux.h$", "<sys/mman.h>"}, + {"bits/mman.h$", "<sys/mman.h>"}, + {"bits/msq.h$", "<sys/msg.h>"}, + {"bits/resource.h$", "<sys/resource.h>"}, + {"bits/sem.h$", "<sys/sem.h>"}, + {"bits/shm.h$", "<sys/shm.h>"}, + {"bits/signalfd.h$", "<sys/signalfd.h>"}, + {"bits/statfs.h$", "<sys/statfs.h>"}, + {"bits/statvfs.h$", "<sys/statvfs.h>"}, + {"bits/timerfd.h$", "<sys/timerfd.h>"}, + {"bits/utsname.h$", "<sys/utsname.h>"}, + {"bits/auxv.h$", "<sys/auxv.h>"}, + {"bits/byteswap-16.h$", "<byteswap.h>"}, + {"bits/byteswap.h$", "<byteswap.h>"}, + {"bits/confname.h$", "<unistd.h>"}, + {"bits/dirent.h$", "<dirent.h>"}, + {"bits/dlfcn.h$", "<dlfcn.h>"}, + {"bits/elfclass.h$", "<link.h>"}, + {"bits/endian.h$", "<endian.h>"}, + {"bits/environments.h$", "<unistd.h>"}, + {"bits/fcntl-linux.h$", "<fcntl.h>"}, + {"bits/fcntl.h$", "<fcntl.h>"}, + {"bits/in.h$", "<netinet/in.h>"}, + {"bits/ioctl-types.h$", "<sys/ioctl.h>"}, + {"bits/ioctls.h$", "<sys/ioctl.h>"}, + {"bits/link.h$", "<link.h>"}, + {"bits/mqueue.h$", "<mqueue.h>"}, + {"bits/netdb.h$", "<netdb.h>"}, + {"bits/param.h$", "<sys/param.h>"}, + {"bits/poll.h$", "<sys/poll.h>"}, + {"bits/posix_opt.h$", "<bits/posix_opt.h>"}, + {"bits/pthreadtypes.h$", "<pthread.h>"}, + {"bits/sched.h$", "<sched.h>"}, + {"bits/select.h$", "<sys/select.h>"}, + {"bits/semaphore.h$", "<semaphore.h>"}, + {"bits/sigthread.h$", "<pthread.h>"}, + {"bits/sockaddr.h$", "<sys/socket.h>"}, + {"bits/socket.h$", "<sys/socket.h>"}, + {"bits/socket_type.h$", "<sys/socket.h>"}, + {"bits/stab.def$", "<stab.h>"}, + {"bits/stat.h$", "<sys/stat.h>"}, + {"bits/stropts.h$", "<stropts.h>"}, + {"bits/syscall.h$", "<sys/syscall.h>"}, + {"bits/syslog-path.h$", "<sys/syslog.h>"}, + {"bits/termios.h$", "<termios.h>"}, + {"bits/types.h$", "<sys/types.h>"}, + {"bits/typesizes.h$", "<sys/types.h>"}, + {"bits/uio.h$", "<sys/uio.h>"}, + {"bits/ustat.h$", "<sys/ustat.h>"}, + {"bits/utmp.h$", "<utmp.h>"}, + {"bits/utmpx.h$", "<utmpx.h>"}, + {"bits/waitflags.h$", "<sys/wait.h>"}, + {"bits/waitstatus.h$", "<sys/wait.h>"}, + {"bits/xtitypes.h$", "<stropts.h>"}, + }; + for (const auto &Pair : SystemHeaderMap) + Includes->addRegexMapping(Pair.first, Pair.second); +} + +} // namespace clangd +} // namespace clang Added: clang-tools-extra/trunk/clangd/index/CanonicalIncludes.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/CanonicalIncludes.h?rev=325343&view=auto ============================================================================== --- clang-tools-extra/trunk/clangd/index/CanonicalIncludes.h (added) +++ clang-tools-extra/trunk/clangd/index/CanonicalIncludes.h Fri Feb 16 06:15:55 2018 @@ -0,0 +1,79 @@ +//===-- CanonicalIncludes.h - remap #include header -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// At indexing time, we decide which file to #included for a symbol. +// Usually this is the file with the canonical decl, but there are exceptions: +// - private headers may have pragmas pointing to the matching public header. +// (These are "IWYU" pragmas, named after the include-what-you-use tool). +// - the standard library is implemented in many files, without any pragmas. +// We have a lookup table for common standard library implementations. +// libstdc++ puts char_traits in bits/char_traits.h, but we #include <string>. +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_CANONICALINCLUDES_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_CANONICALINCLUDES_H + +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Regex.h" +#include <string> +#include <vector> + +namespace clang { +namespace clangd { + +/// Maps a definition location onto an #include file, based on a set of filename +/// rules. +class CanonicalIncludes { +public: + CanonicalIncludes() = default; + + /// Adds a string-to-string mapping from \p Path to \p CanonicalPath. + void addMapping(llvm::StringRef Path, llvm::StringRef CanonicalPath); + + /// Maps all files matching \p RE to \p CanonicalPath + void addRegexMapping(llvm::StringRef RE, llvm::StringRef CanonicalPath); + + /// \return \p Header itself if there is no mapping for it; otherwise, return + /// a canonical header name. + llvm::StringRef mapHeader(llvm::StringRef Header) const; + +private: + // A map from header patterns to header names. This needs to be mutable so + // that we can match again a Regex in a const function member. + // FIXME(ioeric): All the regexes we have so far are suffix matches. The + // performance could be improved by allowing only suffix matches instead of + // arbitrary regexes. + mutable std::vector<std::pair<llvm::Regex, std::string>> + RegexHeaderMappingTable; +}; + +/// Returns a CommentHandler that parses pragma comment on include files to +/// determine when we should include a different header from the header that +/// directly defines a symbol. Mappinps are registered with \p Includes. +/// +/// Currently it only supports IWYU private pragma: +/// https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUPragmas.md#iwyu-pragma-private +std::unique_ptr<CommentHandler> +collectIWYUHeaderMaps(CanonicalIncludes *Includes); + +/// Adds mapping for system headers. Approximately, the following system headers +/// are handled: +/// - C++ standard library e.g. bits/basic_string.h$ -> <string> +/// - Posix library e.g. bits/pthreadtypes.h$ -> <pthread.h> +/// - Compiler extensions, e.g. include/avx512bwintrin.h$ -> <immintrin.h> +/// The mapping is hardcoded and hand-maintained, so it might not cover all +/// headers. +void addSystemHeadersMapping(CanonicalIncludes *Includes); + +} // namespace clangd +} // namespace clang + +#endif //LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_HEADERMAPCOLLECTOR_H Modified: clang-tools-extra/trunk/clangd/index/Index.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/Index.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/index/Index.cpp (original) +++ clang-tools-extra/trunk/clangd/index/Index.cpp Fri Feb 16 06:15:55 2018 @@ -80,6 +80,7 @@ static void own(Symbol &S, DenseSet<Stri // Intern the actual strings. Intern(Detail->Documentation); Intern(Detail->CompletionDetail); + Intern(Detail->IncludeHeader); // Replace the detail pointer with our copy. S.Detail = Detail; } Modified: clang-tools-extra/trunk/clangd/index/Index.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/Index.h?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/index/Index.h (original) +++ clang-tools-extra/trunk/clangd/index/Index.h Fri Feb 16 06:15:55 2018 @@ -152,11 +152,19 @@ struct Symbol { /// and have clients resolve full symbol information for a specific candidate /// if needed. struct Details { - // Documentation including comment for the symbol declaration. + /// Documentation including comment for the symbol declaration. llvm::StringRef Documentation; - // This is what goes into the LSP detail field in a completion item. For - // example, the result type of a function. + /// This is what goes into the LSP detail field in a completion item. For + /// example, the result type of a function. llvm::StringRef CompletionDetail; + /// This can be either a URI of the header to be #include'd for this symbol, + /// or a literal header quoted with <> or "" that is suitable to be included + /// directly. When this is a URI, the exact #include path needs to be + /// calculated according to the URI scheme. + /// + /// If empty, FileURI in CanonicalDeclaration should be used to calculate + /// the #include path. + llvm::StringRef IncludeHeader; }; // Optional details of the symbol. Modified: clang-tools-extra/trunk/clangd/index/Merge.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/Merge.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/index/Merge.cpp (original) +++ clang-tools-extra/trunk/clangd/index/Merge.cpp Fri Feb 16 06:15:55 2018 @@ -90,6 +90,8 @@ mergeSymbol(const Symbol &L, const Symbo Scratch->Documentation = O.Detail->Documentation; if (Scratch->CompletionDetail == "") Scratch->CompletionDetail = O.Detail->CompletionDetail; + if (Scratch->IncludeHeader == "") + Scratch->IncludeHeader = O.Detail->IncludeHeader; S.Detail = Scratch; } else S.Detail = O.Detail; Modified: clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp (original) +++ clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp Fri Feb 16 06:15:55 2018 @@ -11,6 +11,7 @@ #include "../CodeCompletionStrings.h" #include "../Logger.h" #include "../URI.h" +#include "CanonicalIncludes.h" #include "clang/AST/DeclCXX.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/SourceManager.h" @@ -127,6 +128,51 @@ bool shouldFilterDecl(const NamedDecl *N return false; } +// We only collect #include paths for symbols that are suitable for global code +// completion, except for namespaces since #include path for a namespace is hard +// to define. +bool shouldCollectIncludePath(index::SymbolKind Kind) { + using SK = index::SymbolKind; + switch (Kind) { + case SK::Macro: + case SK::Enum: + case SK::Struct: + case SK::Class: + case SK::Union: + case SK::TypeAlias: + case SK::Using: + case SK::Function: + case SK::Variable: + case SK::EnumConstant: + return true; + default: + return false; + } +} + +/// Gets a canonical include (<header> or "header") for header of \p Loc. +/// Returns None if the header has no canonical include. +/// FIXME: we should handle .inc files whose symbols are expected be exported by +/// their containing headers. +llvm::Optional<std::string> +getIncludeHeader(const SourceManager &SM, SourceLocation Loc, + const SymbolCollector::Options &Opts) { + llvm::StringRef FilePath = SM.getFilename(Loc); + if (FilePath.empty()) + return llvm::None; + if (Opts.Includes) { + llvm::StringRef Mapped = Opts.Includes->mapHeader(FilePath); + if (Mapped != FilePath) + return (Mapped.startswith("<") || Mapped.startswith("\"")) + ? Mapped.str() + : ("\"" + Mapped + "\"").str(); + } + // If the header path is the same as the file path of the declaration, we skip + // storing the #include path; users can use the URI in declaration location to + // calculate the #include path. + return llvm::None; +} + // Return the symbol location of the given declaration `D`. // // For symbols defined inside macros: @@ -252,6 +298,14 @@ const Symbol *SymbolCollector::addDeclar std::string Documentation = getDocumentation(*CCS); std::string CompletionDetail = getDetail(*CCS); + std::string Include; + if (Opts.CollectIncludePath && shouldCollectIncludePath(S.SymInfo.Kind)) { + // Use the expansion location to get the #include header since this is + // where the symbol is exposed. + if (auto Header = + getIncludeHeader(SM, SM.getExpansionLoc(ND.getLocation()), Opts)) + Include = std::move(*Header); + } S.CompletionFilterText = FilterText; S.CompletionLabel = Label; S.CompletionPlainInsertText = PlainInsertText; @@ -259,6 +313,7 @@ const Symbol *SymbolCollector::addDeclar Symbol::Details Detail; Detail.Documentation = Documentation; Detail.CompletionDetail = CompletionDetail; + Detail.IncludeHeader = Include; S.Detail = &Detail; Symbols.insert(S); Modified: clang-tools-extra/trunk/clangd/index/SymbolCollector.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/SymbolCollector.h?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/index/SymbolCollector.h (original) +++ clang-tools-extra/trunk/clangd/index/SymbolCollector.h Fri Feb 16 06:15:55 2018 @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "CanonicalIncludes.h" #include "Index.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -39,6 +40,10 @@ public: /// in symbols. The list of schemes will be tried in order until a working /// scheme is found. If no scheme works, symbol location will be dropped. std::vector<std::string> URISchemes = {"file"}; + bool CollectIncludePath = false; + /// If set, this is used to map symbol #include path to a potentially + /// different #include path. + const CanonicalIncludes *Includes = nullptr; }; SymbolCollector(Options Opts); Modified: clang-tools-extra/trunk/clangd/index/SymbolYAML.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/SymbolYAML.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/index/SymbolYAML.cpp (original) +++ clang-tools-extra/trunk/clangd/index/SymbolYAML.cpp Fri Feb 16 06:15:55 2018 @@ -64,6 +64,7 @@ template <> struct MappingTraits<Symbol: static void mapping(IO &io, Symbol::Details &Detail) { io.mapOptional("Documentation", Detail.Documentation); io.mapOptional("CompletionDetail", Detail.CompletionDetail); + io.mapOptional("IncludeHeader", Detail.IncludeHeader); } }; Modified: clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp (original) +++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp Fri Feb 16 06:15:55 2018 @@ -16,6 +16,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" +#include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include <cstdlib> #include <iostream> Modified: clang-tools-extra/trunk/test/clangd/completion-snippets.test URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/completion-snippets.test?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/test/clangd/completion-snippets.test (original) +++ clang-tools-extra/trunk/test/clangd/completion-snippets.test Fri Feb 16 06:15:55 2018 @@ -28,9 +28,7 @@ # CHECK-NEXT: "result": { # CHECK-NEXT: "isIncomplete": {{.*}} # CHECK-NEXT: "items": [ -# CHECK-NEXT: { -# CHECK-NEXT: "detail": "int", -# CHECK-NEXT: "filterText": "func_with_args", +# CHECK: "filterText": "func_with_args", # CHECK-NEXT: "insertText": "func_with_args(${1:int a}, ${2:int b})", # CHECK-NEXT: "insertTextFormat": 2, # CHECK-NEXT: "kind": 3, Modified: clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test (original) +++ clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test Fri Feb 16 06:15:55 2018 @@ -24,7 +24,8 @@ # CHECK-NEXT: "documentRangeFormattingProvider": true, # CHECK-NEXT: "executeCommandProvider": { # CHECK-NEXT: "commands": [ -# CHECK-NEXT: "clangd.applyFix" +# CHECK-NEXT: "clangd.applyFix", +# CHECK-NEXT: "clangd.insertInclude" # CHECK-NEXT: ] # CHECK-NEXT: }, # CHECK-NEXT: "renameProvider": true, Modified: clang-tools-extra/trunk/test/clangd/initialize-params.test URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/initialize-params.test?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/test/clangd/initialize-params.test (original) +++ clang-tools-extra/trunk/test/clangd/initialize-params.test Fri Feb 16 06:15:55 2018 @@ -24,7 +24,8 @@ # CHECK-NEXT: "documentRangeFormattingProvider": true, # CHECK-NEXT: "executeCommandProvider": { # CHECK-NEXT: "commands": [ -# CHECK-NEXT: "clangd.applyFix" +# CHECK-NEXT: "clangd.applyFix", +# CHECK-NEXT: "clangd.insertInclude" # CHECK-NEXT: ] # CHECK-NEXT: }, # CHECK-NEXT: "renameProvider": true, Added: clang-tools-extra/trunk/test/clangd/insert-include.test URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/insert-include.test?rev=325343&view=auto ============================================================================== --- clang-tools-extra/trunk/test/clangd/insert-include.test (added) +++ clang-tools-extra/trunk/test/clangd/insert-include.test Fri Feb 16 06:15:55 2018 @@ -0,0 +1,36 @@ +# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s +# RUN: clangd -lit-test -pch-storage=memory < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"void f() {}"}}} +--- +{"jsonrpc":"2.0","id":3,"method":"workspace/executeCommand","params":{"arguments":[{"header":"<vector>","textDocument":{"uri":"test:///main.cpp"}}],"command":"clangd.insertInclude"}} +# CHECK: "id": 3, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": "Inserted header <vector>" +# CHECK-NEXT: } +# CHECK: "method": "workspace/applyEdit", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "edit": { +# CHECK-NEXT: "changes": { +# CHECK-NEXT: "file:///clangd-test/main.cpp": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "#include <vector>\n", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":4,"method":"shutdown"} Modified: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt (original) +++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Fri Feb 16 06:15:55 2018 @@ -17,6 +17,7 @@ add_extra_unittest(ClangdTests ContextTests.cpp FileIndexTests.cpp FuzzyMatchTests.cpp + HeadersTests.cpp IndexTests.cpp JSONExprTests.cpp SourceCodeTests.cpp Modified: clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp (original) +++ clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp Fri Feb 16 06:15:55 2018 @@ -12,6 +12,7 @@ #include "Matchers.h" #include "SyncAPI.h" #include "TestFS.h" +#include "URI.h" #include "clang/Config/config.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" @@ -100,8 +101,6 @@ std::string dumpASTWithoutMemoryLocs(Cla return replacePtrsInDump(DumpWithMemLocs); } -} // namespace - class ClangdVFSTest : public ::testing::Test { protected: std::string parseSourceAndDumpAST( @@ -819,5 +818,38 @@ int d; ASSERT_EQ(DiagConsumer.Count, 2); // Sanity check - we actually ran both? } +TEST_F(ClangdVFSTest, InsertIncludes) { + MockFSProvider FS; + ErrorCheckingDiagConsumer DiagConsumer; + MockCompilationDatabase CDB; + ClangdServer Server(CDB, DiagConsumer, FS, + /*AsyncThreadsCount=*/0, + /*StorePreamblesInMemory=*/true); + + // No need to sync reparses, because reparses are performed on the calling + // thread. + auto FooCpp = testPath("foo.cpp"); + const auto Code = R"cpp( +#include "x.h" + +void f() {} +)cpp"; + FS.Files[FooCpp] = Code; + Server.addDocument(FooCpp, Code); + + auto Inserted = [&](llvm::StringRef Header, llvm::StringRef Expected) { + auto Replaces = Server.insertInclude(FooCpp, Code, Header); + EXPECT_TRUE(static_cast<bool>(Replaces)); + auto Changed = tooling::applyAllReplacements(Code, *Replaces); + EXPECT_TRUE(static_cast<bool>(Changed)); + return llvm::StringRef(*Changed).contains( + (llvm::Twine("#include ") + Expected + "").str()); + }; + + EXPECT_TRUE(Inserted("\"y.h\"", "\"y.h\"")); + EXPECT_TRUE(Inserted("<string>", "<string>")); +} + +} // namespace } // namespace clangd } // namespace clang Added: clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp?rev=325343&view=auto ============================================================================== --- clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp (added) +++ clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp Fri Feb 16 06:15:55 2018 @@ -0,0 +1,98 @@ +//===-- HeadersTests.cpp - Include headers unit tests -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Headers.h" +#include "TestFS.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +class HeadersTest : public ::testing::Test { +public: + HeadersTest() { + CDB.ExtraClangFlags = {SearchDirArg.c_str()}; + FS.Files[MainFile] = ""; + } + +protected: + // Adds \p File with content \p Code to the sub directory and returns the + // absolute path. +// std::string addToSubdir(PathRef File, llvm::StringRef Code = "") { +// assert(llvm::sys::path::is_relative(File) && "FileName should be relative"); +// llvm::SmallString<32> Path; +// llvm::sys::path::append(Path, Subdir, File); +// FS.Files[Path] = Code; +// return Path.str(); +// }; + + // Shortens the header and returns "" on error. + std::string shorten(PathRef Header) { + auto VFS = FS.getTaggedFileSystem(MainFile).Value; + auto Cmd = CDB.getCompileCommand(MainFile); + assert(static_cast<bool>(Cmd)); + VFS->setCurrentWorkingDirectory(Cmd->Directory); + auto Path = + shortenIncludePath(MainFile, FS.Files[MainFile], Header, *Cmd, VFS); + if (!Path) { + llvm::consumeError(Path.takeError()); + return std::string(); + } + return std::move(*Path); + } + MockFSProvider FS; + MockCompilationDatabase CDB; + std::string MainFile = testPath("main.cpp"); + std::string Subdir = testPath("sub"); + std::string SearchDirArg = (llvm::Twine("-I") + Subdir).str(); +}; + +TEST_F(HeadersTest, InsertInclude) { + std::string Path = testPath("sub/bar.h"); + FS.Files[Path] = ""; + EXPECT_EQ(shorten(Path), "\"bar.h\""); +} + +TEST_F(HeadersTest, DontInsertDuplicateSameName) { + FS.Files[MainFile] = R"cpp( +#include "bar.h" +)cpp"; + std::string BarHeader = testPath("sub/bar.h"); + FS.Files[BarHeader] = ""; + EXPECT_EQ(shorten(BarHeader), ""); +} + +TEST_F(HeadersTest, DontInsertDuplicateDifferentName) { + std::string BarHeader = testPath("sub/bar.h"); + FS.Files[BarHeader] = ""; + FS.Files[MainFile] = R"cpp( +#include "sub/bar.h" // not shortest +)cpp"; + EXPECT_EQ(shorten(BarHeader), ""); +} + +TEST_F(HeadersTest, StillInsertIfTrasitivelyIncluded) { + std::string BazHeader = testPath("sub/baz.h"); + FS.Files[BazHeader] = ""; + std::string BarHeader = testPath("sub/bar.h"); + FS.Files[BarHeader] = R"cpp( +#include "baz.h" +)cpp"; + FS.Files[MainFile] = R"cpp( +#include "bar.h" +)cpp"; + EXPECT_EQ(shorten(BazHeader), "\"baz.h\""); +} + +} // namespace +} // namespace clangd +} // namespace clang + Modified: clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp?rev=325343&r1=325342&r2=325343&view=diff ============================================================================== --- clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp (original) +++ clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp Fri Feb 16 06:15:55 2018 @@ -47,6 +47,9 @@ MATCHER_P(Snippet, S, "") { } MATCHER_P(QName, Name, "") { return (arg.Scope + arg.Name).str() == Name; } MATCHER_P(DeclURI, P, "") { return arg.CanonicalDeclaration.FileURI == P; } +MATCHER_P(IncludeHeader, P, "") { + return arg.Detail && arg.Detail->IncludeHeader == P; +} MATCHER_P(DeclRange, Offsets, "") { return arg.CanonicalDeclaration.StartOffset == Offsets.first && arg.CanonicalDeclaration.EndOffset == Offsets.second; @@ -62,28 +65,50 @@ namespace clangd { namespace { class SymbolIndexActionFactory : public tooling::FrontendActionFactory { public: - SymbolIndexActionFactory(SymbolCollector::Options COpts) - : COpts(std::move(COpts)) {} + SymbolIndexActionFactory(SymbolCollector::Options COpts, + CommentHandler *PragmaHandler) + : COpts(std::move(COpts)), PragmaHandler(PragmaHandler) {} clang::FrontendAction *create() override { + class WrappedIndexAction : public WrapperFrontendAction { + public: + WrappedIndexAction(std::shared_ptr<SymbolCollector> C, + const index::IndexingOptions &Opts, + CommentHandler *PragmaHandler) + : WrapperFrontendAction( + index::createIndexingAction(C, Opts, nullptr)), + PragmaHandler(PragmaHandler) {} + + std::unique_ptr<ASTConsumer> + CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { + if (PragmaHandler) + CI.getPreprocessor().addCommentHandler(PragmaHandler); + return WrapperFrontendAction::CreateASTConsumer(CI, InFile); + } + + private: + index::IndexingOptions IndexOpts; + CommentHandler *PragmaHandler; + }; index::IndexingOptions IndexOpts; IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; IndexOpts.IndexFunctionLocals = false; Collector = std::make_shared<SymbolCollector>(COpts); - FrontendAction *Action = - index::createIndexingAction(Collector, IndexOpts, nullptr).release(); - return Action; + return new WrappedIndexAction(Collector, std::move(IndexOpts), + PragmaHandler); } std::shared_ptr<SymbolCollector> Collector; SymbolCollector::Options COpts; + CommentHandler *PragmaHandler; }; class SymbolCollectorTest : public ::testing::Test { public: SymbolCollectorTest() - : TestHeaderName(testPath("symbol.h")), + : InMemoryFileSystem(new vfs::InMemoryFileSystem), + TestHeaderName(testPath("symbol.h")), TestFileName(testPath("symbol.cc")) { TestHeaderURI = URI::createFile(TestHeaderName).toString(); TestFileURI = URI::createFile(TestFileName).toString(); @@ -91,12 +116,11 @@ public: bool runSymbolCollector(StringRef HeaderCode, StringRef MainCode, const std::vector<std::string> &ExtraArgs = {}) { - llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( - new vfs::InMemoryFileSystem); llvm::IntrusiveRefCntPtr<FileManager> Files( new FileManager(FileSystemOptions(), InMemoryFileSystem)); - auto Factory = llvm::make_unique<SymbolIndexActionFactory>(CollectorOpts); + auto Factory = llvm::make_unique<SymbolIndexActionFactory>( + CollectorOpts, PragmaHandler.get()); std::vector<std::string> Args = {"symbol_collector", "-fsyntax-only", "-std=c++11", "-include", @@ -117,12 +141,14 @@ public: } protected: + llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem; std::string TestHeaderName; std::string TestHeaderURI; std::string TestFileName; std::string TestFileURI; SymbolSlab Symbols; SymbolCollector::Options CollectorOpts; + std::unique_ptr<CommentHandler> PragmaHandler; }; TEST_F(SymbolCollectorTest, CollectSymbols) { @@ -554,6 +580,46 @@ CompletionSnippetInsertText: 'snippet QName("clang::Foo2"))); } +TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) { + CollectorOpts.CollectIncludePath = true; + runSymbolCollector("class Foo {};", /*Main=*/""); + EXPECT_THAT(Symbols, + UnorderedElementsAre(AllOf(QName("Foo"), DeclURI(TestHeaderURI), + IncludeHeader("")))); +} + +#ifndef LLVM_ON_WIN32 +TEST_F(SymbolCollectorTest, CanonicalSTLHeader) { + CollectorOpts.CollectIncludePath = true; + CanonicalIncludes Includes; + addSystemHeadersMapping(&Includes); + CollectorOpts.Includes = &Includes; + // bits/basic_string.h$ should be mapped to <string> + TestHeaderName = "/nasty/bits/basic_string.h"; + TestFileName = "/nasty/bits/basic_string.cpp"; + TestHeaderURI = URI::createFile(TestHeaderName).toString(); + runSymbolCollector("class string {};", /*Main=*/""); + EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("string"), + DeclURI(TestHeaderURI), + IncludeHeader("<string>")))); +} +#endif + +TEST_F(SymbolCollectorTest, IWYUPragma) { + CollectorOpts.CollectIncludePath = true; + CanonicalIncludes Includes; + PragmaHandler = collectIWYUHeaderMaps(&Includes); + CollectorOpts.Includes = &Includes; + const std::string Header = R"( + // IWYU pragma: private, include the/good/header.h + class Foo {}; + )"; + runSymbolCollector(Header, /*Main=*/""); + EXPECT_THAT(Symbols, UnorderedElementsAre( + AllOf(QName("Foo"), DeclURI(TestHeaderURI), + IncludeHeader("\"the/good/header.h\"")))); +} + } // namespace } // namespace clangd } // namespace clang _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits