r332520 fixed the broken build in clangd. On Wed, May 16, 2018 at 9:48 PM Eric Liu <ioe...@google.com> wrote:
> Looking... sorry that I missed the email from this bot. > > On Wed, May 16, 2018 at 9:43 PM Galina Kistanova <gkistan...@gmail.com> > wrote: > >> Hello Eric, >> >> This commit broke one of our builders: >> http://lab.llvm.org:8011/builders/clang-x86_64-linux-abi-test/builds/26399 >> >> . . . >> 387.403 [725/64/4163] Building CXX object >> tools/clang/tools/extra/clangd/CMakeFiles/clangDaemon.dir/CodeComplete.cpp.o >> FAILED: >> tools/clang/tools/extra/clangd/CMakeFiles/clangDaemon.dir/CodeComplete.cpp.o >> >> /usr/bin/c++ -DGTEST_HAS_RTTI=0 -D_DEBUG -D_GNU_SOURCE >> -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS >> -Itools/clang/tools/extra/clangd >> -I/home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/tools/extra/clangd >> -I/home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/include >> -Itools/clang/include -Iinclude >> -I/home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/include >> -fPIC -fvisibility-inlines-hidden -std=c++11 -Wall -W -Wno-unused-parameter >> -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic >> -Wno-long-long -Wno-maybe-uninitialized -Wdelete-non-virtual-dtor >> -Wno-comment -ffunction-sections -fdata-sections -fno-common >> -Woverloaded-virtual -fno-strict-aliasing -O3 -UNDEBUG -fno-exceptions >> -fno-rtti -MD -MT >> tools/clang/tools/extra/clangd/CMakeFiles/clangDaemon.dir/CodeComplete.cpp.o >> -MF >> tools/clang/tools/extra/clangd/CMakeFiles/clangDaemon.dir/CodeComplete.cpp.o.d >> -o >> tools/clang/tools/extra/clangd/CMakeFiles/clangDaemon.dir/CodeComplete.cpp.o >> -c >> /home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/tools/extra/clangd/CodeComplete.cpp >> /home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/tools/extra/clangd/CodeComplete.cpp: >> In function ‘clang::clangd::SignatureHelp >> clang::clangd::signatureHelp(clang::clangd::PathRef, const >> clang::tooling::CompileCommand&, const clang::PrecompiledPreamble*, >> llvm::StringRef, clang::clangd::Position, >> llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem>, >> std::shared_ptr<clang::PCHContainerOperations>)’: >> /home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/tools/extra/clangd/CodeComplete.cpp:1127:37: >> error: invalid initialization of reference of type ‘const >> clang::clangd::{anonymous}::SemaCompleteInput&’ from expression of type >> ‘<brace-enclosed initializer list>’ >> std::move(PCHs)}); >> ^ >> /home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/tools/extra/clangd/CodeComplete.cpp:710:6: >> error: in passing argument 3 of ‘bool >> clang::clangd::{anonymous}::semaCodeComplete(std::unique_ptr<clang::CodeCompleteConsumer>, >> const clang::CodeCompleteOptions&, const >> clang::clangd::{anonymous}::SemaCompleteInput&, >> std::unique_ptr<clang::clangd::IncludeInserter>*)’ >> >> bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer, >> ^ >> . . . >> >> Please have a look? >> >> It is not good idea to keep the bot red for too long. This hides new >> problem which later hard to track down. >> >> Thanks >> >> >> Galina >> >> >> >> On Tue, May 15, 2018 at 8:29 AM, Eric Liu via cfe-commits < >> cfe-commits@lists.llvm.org> wrote: >> >>> Author: ioeric >>> Date: Tue May 15 08:29:32 2018 >>> New Revision: 332363 >>> >>> URL: http://llvm.org/viewvc/llvm-project?rev=332363&view=rev >>> Log: >>> [clangd] Populate #include insertions as additional edits in completion >>> items. >>> >>> Summary: >>> o Remove IncludeInsertion LSP command. >>> o Populate include insertion edits synchromously in completion items. >>> o Share the code completion compiler instance and precompiled preamble >>> to get existing inclusions in main file. >>> o Include insertion logic lives only in CodeComplete now. >>> o Use tooling::HeaderIncludes for inserting new includes. >>> o Refactored tests. >>> >>> Reviewers: sammccall >>> >>> Reviewed By: sammccall >>> >>> Subscribers: klimek, ilya-biryukov, MaskRay, jkorous, cfe-commits >>> >>> Differential Revision: https://reviews.llvm.org/D46497 >>> >>> Modified: >>> clang-tools-extra/trunk/clangd/ClangdServer.cpp >>> clang-tools-extra/trunk/clangd/CodeComplete.cpp >>> clang-tools-extra/trunk/clangd/CodeComplete.h >>> clang-tools-extra/trunk/clangd/Headers.cpp >>> clang-tools-extra/trunk/clangd/Headers.h >>> clang-tools-extra/trunk/clangd/Protocol.h >>> clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp >>> clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp >>> >>> Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp >>> URL: >>> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=332363&r1=332362&r2=332363&view=diff >>> >>> ============================================================================== >>> --- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original) >>> +++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Tue May 15 08:29:32 >>> 2018 >>> @@ -161,6 +161,7 @@ void ClangdServer::codeComplete(PathRef >>> // both the old and the new version in case only one of them >>> matches. >>> CompletionList Result = clangd::codeComplete( >>> File, IP->Command, PreambleData ? &PreambleData->Preamble : >>> nullptr, >>> + PreambleData ? PreambleData->Inclusions : >>> std::vector<Inclusion>(), >>> IP->Contents, Pos, FS, PCHs, CodeCompleteOpts); >>> CB(std::move(Result)); >>> }; >>> >>> Modified: clang-tools-extra/trunk/clangd/CodeComplete.cpp >>> URL: >>> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.cpp?rev=332363&r1=332362&r2=332363&view=diff >>> >>> ============================================================================== >>> --- clang-tools-extra/trunk/clangd/CodeComplete.cpp (original) >>> +++ clang-tools-extra/trunk/clangd/CodeComplete.cpp Tue May 15 08:29:32 >>> 2018 >>> @@ -18,9 +18,11 @@ >>> #include "CodeCompletionStrings.h" >>> #include "Compiler.h" >>> #include "FuzzyMatch.h" >>> +#include "Headers.h" >>> #include "Logger.h" >>> #include "SourceCode.h" >>> #include "Trace.h" >>> +#include "URI.h" >>> #include "index/Index.h" >>> #include "clang/Format/Format.h" >>> #include "clang/Frontend/CompilerInstance.h" >>> @@ -219,6 +221,28 @@ std::string sortText(float Score, llvm:: >>> return S; >>> } >>> >>> +/// Creates a `HeaderFile` from \p Header which can be either a URI or >>> a literal >>> +/// include. >>> +static llvm::Expected<HeaderFile> toHeaderFile(StringRef Header, >>> + llvm::StringRef >>> HintPath) { >>> + if (isLiteralInclude(Header)) >>> + return HeaderFile{Header.str(), /*Verbatim=*/true}; >>> + auto U = URI::parse(Header); >>> + if (!U) >>> + return U.takeError(); >>> + >>> + auto IncludePath = URI::includeSpelling(*U); >>> + if (!IncludePath) >>> + return IncludePath.takeError(); >>> + if (!IncludePath->empty()) >>> + return HeaderFile{std::move(*IncludePath), /*Verbatim=*/true}; >>> + >>> + auto Resolved = URI::resolve(*U, HintPath); >>> + if (!Resolved) >>> + return Resolved.takeError(); >>> + return HeaderFile{std::move(*Resolved), /*Verbatim=*/false}; >>> +} >>> + >>> /// A code completion result, in clang-native form. >>> /// It may be promoted to a CompletionItem if it's among the top-ranked >>> results. >>> struct CompletionCandidate { >>> @@ -255,10 +279,10 @@ struct CompletionCandidate { >>> } >>> >>> // Builds an LSP completion item. >>> - CompletionItem build(llvm::StringRef FileName, >>> - const CompletionItemScores &Scores, >>> + CompletionItem build(StringRef FileName, const CompletionItemScores >>> &Scores, >>> const CodeCompleteOptions &Opts, >>> - CodeCompletionString *SemaCCS) const { >>> + CodeCompletionString *SemaCCS, >>> + const IncludeInserter *Includes) const { >>> assert(bool(SemaResult) == bool(SemaCCS)); >>> CompletionItem I; >>> if (SemaResult) { >>> @@ -289,6 +313,30 @@ struct CompletionCandidate { >>> I.documentation = D->Documentation; >>> if (I.detail.empty()) >>> I.detail = D->CompletionDetail; >>> + if (Includes && !D->IncludeHeader.empty()) { >>> + auto Edit = [&]() -> Expected<Optional<TextEdit>> { >>> + auto ResolvedDeclaring = toHeaderFile( >>> + IndexResult->CanonicalDeclaration.FileURI, FileName); >>> + if (!ResolvedDeclaring) >>> + return ResolvedDeclaring.takeError(); >>> + auto ResolvedInserted = toHeaderFile(D->IncludeHeader, >>> FileName); >>> + if (!ResolvedInserted) >>> + return ResolvedInserted.takeError(); >>> + return Includes->insert(*ResolvedDeclaring, >>> *ResolvedInserted); >>> + }(); >>> + if (!Edit) { >>> + std::string ErrMsg = >>> + ("Failed to generate include insertion edits for adding >>> header " >>> + "(FileURI=\"" + >>> + IndexResult->CanonicalDeclaration.FileURI + >>> + "\", IncludeHeader=\"" + D->IncludeHeader + "\") into >>> " + >>> + FileName) >>> + .str(); >>> + log(ErrMsg + ":" + llvm::toString(Edit.takeError())); >>> + } else if (*Edit) { >>> + I.additionalTextEdits = {std::move(**Edit)}; >>> + } >>> + } >>> } >>> } >>> I.scoreInfo = Scores; >>> @@ -649,6 +697,7 @@ struct SemaCompleteInput { >>> PathRef FileName; >>> const tooling::CompileCommand &Command; >>> PrecompiledPreamble const *Preamble; >>> + const std::vector<Inclusion> &PreambleInclusions; >>> StringRef Contents; >>> Position Pos; >>> IntrusiveRefCntPtr<vfs::FileSystem> VFS; >>> @@ -656,9 +705,12 @@ 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. >>> bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer, >>> const clang::CodeCompleteOptions &Options, >>> - const SemaCompleteInput &Input) { >>> + const SemaCompleteInput &Input, >>> + std::unique_ptr<IncludeInserter> *Includes = >>> nullptr) { >>> trace::Span Tracer("Sema completion"); >>> std::vector<const char *> ArgStrs; >>> for (const auto &S : Input.Command.CommandLine) >>> @@ -729,6 +781,28 @@ bool semaCodeComplete(std::unique_ptr<Co >>> Input.FileName); >>> return false; >>> } >>> + if (Includes) { >>> + // Initialize Includes if provided. >>> + >>> + // FIXME(ioeric): needs more consistent style support in clangd >>> server. >>> + auto Style = format::getStyle("file", Input.FileName, "LLVM", >>> + Input.Contents, Input.VFS.get()); >>> + if (!Style) { >>> + log("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 (!Action.Execute()) { >>> log("Execute() failed when running codeComplete for " + >>> Input.FileName); >>> return false; >>> @@ -863,7 +937,8 @@ class CodeCompleteFlow { >>> 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. >>> + llvm::Optional<FuzzyMatcher> Filter; // Initialized once Sema >>> runs. >>> + std::unique_ptr<IncludeInserter> Includes; // Initialized once >>> compiler runs. >>> >>> public: >>> // A CodeCompleteFlow object is only useful for calling run() exactly >>> once. >>> @@ -872,20 +947,25 @@ public: >>> >>> CompletionList run(const SemaCompleteInput &SemaCCInput) && { >>> trace::Span Tracer("CodeCompleteFlow"); >>> + >>> // We run Sema code completion first. It builds an AST and >>> calculates: >>> // - completion results based on the AST. >>> // - partial identifier and context. We need these for the index >>> query. >>> CompletionList Output; >>> auto RecorderOwner = llvm::make_unique<CompletionRecorder>(Opts, >>> [&]() { >>> assert(Recorder && "Recorder is not set"); >>> + assert(Includes && "Includes is not set"); >>> + // If preprocessor was run, inclusions from preprocessor callback >>> should >>> + // already be added to Inclusions. >>> Output = runWithSema(); >>> + Includes.reset(); // Make sure this doesn't out-live Clang. >>> SPAN_ATTACH(Tracer, "sema_completion_kind", >>> >>> getCompletionKindString(Recorder->CCContext.getKind())); >>> }); >>> >>> Recorder = RecorderOwner.get(); >>> semaCodeComplete(std::move(RecorderOwner), >>> Opts.getClangCompleteOpts(), >>> - SemaCCInput); >>> + SemaCCInput, &Includes); >>> >>> SPAN_ATTACH(Tracer, "sema_results", NSema); >>> SPAN_ATTACH(Tracer, "index_results", NIndex); >>> @@ -1011,19 +1091,21 @@ private: >>> CodeCompletionString *SemaCCS = nullptr; >>> if (auto *SR = Candidate.SemaResult) >>> SemaCCS = Recorder->codeCompletionString(*SR, >>> Opts.IncludeBriefComments); >>> - return Candidate.build(FileName, Scores, Opts, SemaCCS); >>> + return Candidate.build(FileName, Scores, Opts, SemaCCS, >>> Includes.get()); >>> } >>> }; >>> >>> CompletionList 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, Contents, Pos, VFS, PCHs}); >>> + .run({FileName, Command, Preamble, PreambleInclusions, Contents, >>> Pos, VFS, >>> + PCHs}); >>> } >>> >>> SignatureHelp signatureHelp(PathRef FileName, >>> @@ -1040,7 +1122,8 @@ SignatureHelp signatureHelp(PathRef File >>> Options.IncludeBriefComments = true; >>> semaCodeComplete(llvm::make_unique<SignatureHelpCollector>(Options, >>> Result), >>> Options, >>> - {FileName, Command, Preamble, Contents, Pos, >>> std::move(VFS), >>> + {FileName, Command, Preamble, >>> + /*PreambleInclusions=*/{}, Contents, Pos, >>> std::move(VFS), >>> std::move(PCHs)}); >>> return Result; >>> } >>> >>> Modified: clang-tools-extra/trunk/clangd/CodeComplete.h >>> URL: >>> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.h?rev=332363&r1=332362&r2=332363&view=diff >>> >>> ============================================================================== >>> --- clang-tools-extra/trunk/clangd/CodeComplete.h (original) >>> +++ clang-tools-extra/trunk/clangd/CodeComplete.h Tue May 15 08:29:32 >>> 2018 >>> @@ -15,6 +15,7 @@ >>> #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H >>> #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H >>> >>> +#include "Headers.h" >>> #include "Logger.h" >>> #include "Path.h" >>> #include "Protocol.h" >>> @@ -69,6 +70,7 @@ struct CodeCompleteOptions { >>> CompletionList 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, >>> >>> Modified: clang-tools-extra/trunk/clangd/Headers.cpp >>> URL: >>> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Headers.cpp?rev=332363&r1=332362&r2=332363&view=diff >>> >>> ============================================================================== >>> --- clang-tools-extra/trunk/clangd/Headers.cpp (original) >>> +++ clang-tools-extra/trunk/clangd/Headers.cpp Tue May 15 08:29:32 2018 >>> @@ -15,8 +15,6 @@ >>> #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" >>> #include "llvm/Support/Path.h" >>> >>> namespace clang { >>> @@ -74,64 +72,13 @@ collectInclusionsInMainFileCallback(cons >>> >>> /// FIXME(ioeric): we might not want to insert an absolute include path >>> if the >>> /// path is not shortened. >>> -llvm::Expected<std::string> >>> -calculateIncludePath(llvm::StringRef File, llvm::StringRef Code, >>> - const HeaderFile &DeclaringHeader, >>> - const HeaderFile &InsertedHeader, >>> - const tooling::CompileCommand &CompileCommand, >>> - IntrusiveRefCntPtr<vfs::FileSystem> FS) { >>> - assert(llvm::sys::path::is_absolute(File)); >>> +llvm::Expected<std::string> calculateIncludePath( >>> + PathRef File, StringRef BuildDir, HeaderSearch &HeaderSearchInfo, >>> + const std::vector<Inclusion> &Inclusions, const HeaderFile >>> &DeclaringHeader, >>> + const HeaderFile &InsertedHeader) { >>> assert(DeclaringHeader.valid() && InsertedHeader.valid()); >>> if (File == DeclaringHeader.File || File == InsertedHeader.File) >>> return ""; >>> - FS->setCurrentWorkingDirectory(CompileCommand.Directory); >>> - >>> - // 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; >>> - >>> - // The diagnostic options must be set before creating a >>> CompilerInstance. >>> - CI->getDiagnosticOpts().IgnoreWarnings = true; >>> - auto Clang = prepareCompilerInstance( >>> - std::move(CI), /*Preamble=*/nullptr, >>> - llvm::MemoryBuffer::getMemBuffer(Code, File), >>> - std::make_shared<PCHContainerOperations>(), FS, IgnoreDiags); >>> - >>> - 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::vector<Inclusion> Inclusions; >>> - >>> Clang->getPreprocessor().addPPCallbacks(collectInclusionsInMainFileCallback( >>> - Clang->getSourceManager(), >>> - [&Inclusions](Inclusion Inc) { >>> Inclusions.push_back(std::move(Inc)); })); >>> - if (!Action.Execute()) >>> - return llvm::make_error<llvm::StringError>( >>> - "Failed to execute preprocessor only action for file " + File, >>> - llvm::inconvertibleErrorCode()); >>> llvm::StringSet<> IncludedHeaders; >>> for (const auto &Inc : Inclusions) { >>> IncludedHeaders.insert(Inc.Written); >>> @@ -144,14 +91,13 @@ calculateIncludePath(llvm::StringRef Fil >>> if (Included(DeclaringHeader.File) || Included(InsertedHeader.File)) >>> return ""; >>> >>> - auto &HeaderSearchInfo = >>> Clang->getPreprocessor().getHeaderSearchInfo(); >>> bool IsSystem = false; >>> >>> if (InsertedHeader.Verbatim) >>> return InsertedHeader.File; >>> >>> std::string Suggested = >>> HeaderSearchInfo.suggestPathToFileForDiagnostics( >>> - InsertedHeader.File, CompileCommand.Directory, &IsSystem); >>> + InsertedHeader.File, BuildDir, &IsSystem); >>> if (IsSystem) >>> Suggested = "<" + Suggested + ">"; >>> else >>> @@ -161,5 +107,35 @@ calculateIncludePath(llvm::StringRef Fil >>> return Suggested; >>> } >>> >>> +Expected<Optional<TextEdit>> >>> +IncludeInserter::insert(const HeaderFile &DeclaringHeader, >>> + const HeaderFile &InsertedHeader) const { >>> + auto Validate = [](const HeaderFile &Header) { >>> + return Header.valid() >>> + ? llvm::Error::success() >>> + : llvm::make_error<llvm::StringError>( >>> + "Invalid HeaderFile: " + Header.File + >>> + " (verbatim=" + >>> std::to_string(Header.Verbatim) + ").", >>> + llvm::inconvertibleErrorCode()); >>> + }; >>> + if (auto Err = Validate(DeclaringHeader)) >>> + return std::move(Err); >>> + if (auto Err = Validate(InsertedHeader)) >>> + return std::move(Err); >>> + auto Include = >>> + calculateIncludePath(FileName, BuildDir, HeaderSearchInfo, >>> Inclusions, >>> + DeclaringHeader, InsertedHeader); >>> + if (!Include) >>> + return Include.takeError(); >>> + if (Include->empty()) >>> + return llvm::None; >>> + StringRef IncludeRef = *Include; >>> + auto Insertion = >>> + Inserter.insert(IncludeRef.trim("\"<>"), >>> IncludeRef.startswith("<")); >>> + if (!Insertion) >>> + return llvm::None; >>> + return replacementToEdit(Code, *Insertion); >>> +} >>> + >>> } // namespace clangd >>> } // namespace clang >>> >>> Modified: clang-tools-extra/trunk/clangd/Headers.h >>> URL: >>> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Headers.h?rev=332363&r1=332362&r2=332363&view=diff >>> >>> ============================================================================== >>> --- clang-tools-extra/trunk/clangd/Headers.h (original) >>> +++ clang-tools-extra/trunk/clangd/Headers.h Tue May 15 08:29:32 2018 >>> @@ -12,10 +12,14 @@ >>> >>> #include "Path.h" >>> #include "Protocol.h" >>> +#include "SourceCode.h" >>> #include "clang/Basic/VirtualFileSystem.h" >>> +#include "clang/Format/Format.h" >>> +#include "clang/Lex/HeaderSearch.h" >>> #include "clang/Lex/PPCallbacks.h" >>> -#include "clang/Tooling/CompilationDatabase.h" >>> +#include "clang/Tooling/Core/HeaderIncludes.h" >>> #include "llvm/ADT/StringRef.h" >>> +#include "llvm/ADT/StringSet.h" >>> #include "llvm/Support/Error.h" >>> >>> namespace clang { >>> @@ -51,20 +55,51 @@ collectInclusionsInMainFileCallback(cons >>> /// 'Foo/Bar.h' over a longer one like 'Baz/include/Foo/Bar.h'. >>> /// >>> /// \param File is an absolute file path. >>> +/// \param Inclusions Existing inclusions in the main file. >>> /// \param DeclaringHeader is the original header corresponding to \p >>> /// InsertedHeader e.g. the header that declares a symbol. >>> /// \param InsertedHeader The preferred header to be inserted. This >>> could be the >>> /// same as DeclaringHeader but must be provided. >>> // \return A quoted "path" or <path>. This returns an empty string if: >>> /// - Either \p DeclaringHeader or \p InsertedHeader is already >>> (directly) >>> -/// included in the file (including those included via different >>> paths). >>> +/// in \p Inclusions (including those included via different paths). >>> /// - \p DeclaringHeader or \p InsertedHeader is the same as \p File. >>> -llvm::Expected<std::string> >>> -calculateIncludePath(PathRef File, llvm::StringRef Code, >>> - const HeaderFile &DeclaringHeader, >>> - const HeaderFile &InsertedHeader, >>> - const tooling::CompileCommand &CompileCommand, >>> - IntrusiveRefCntPtr<vfs::FileSystem> FS); >>> +llvm::Expected<std::string> calculateIncludePath( >>> + PathRef File, StringRef BuildDir, HeaderSearch &HeaderSearchInfo, >>> + const std::vector<Inclusion> &Inclusions, const HeaderFile >>> &DeclaringHeader, >>> + const HeaderFile &InsertedHeader); >>> + >>> +// Calculates insertion edit for including a new header in a file. >>> +class IncludeInserter { >>> +public: >>> + IncludeInserter(StringRef FileName, StringRef Code, >>> + const format::FormatStyle &Style, StringRef BuildDir, >>> + HeaderSearch &HeaderSearchInfo) >>> + : FileName(FileName), Code(Code), BuildDir(BuildDir), >>> + HeaderSearchInfo(HeaderSearchInfo), >>> + Inserter(FileName, Code, Style.IncludeStyle) {} >>> + >>> + void addExisting(Inclusion Inc) { >>> Inclusions.push_back(std::move(Inc)); } >>> + >>> + /// Returns a TextEdit that inserts a new header; if the header is not >>> + /// inserted e.g. it's an existing header, this returns None. If any >>> header is >>> + /// invalid, this returns error. >>> + /// >>> + /// \param DeclaringHeader is the original header corresponding to \p >>> + /// InsertedHeader e.g. the header that declares a symbol. >>> + /// \param InsertedHeader The preferred header to be inserted. This >>> could be >>> + /// the same as DeclaringHeader but must be provided. >>> + Expected<Optional<TextEdit>> insert(const HeaderFile &DeclaringHeader, >>> + const HeaderFile &InsertedHeader) >>> const; >>> + >>> +private: >>> + StringRef FileName; >>> + StringRef Code; >>> + StringRef BuildDir; >>> + HeaderSearch &HeaderSearchInfo; >>> + std::vector<Inclusion> Inclusions; >>> + tooling::HeaderIncludes Inserter; // Computers insertion replacement. >>> +}; >>> >>> } // namespace clangd >>> } // namespace clang >>> >>> Modified: clang-tools-extra/trunk/clangd/Protocol.h >>> URL: >>> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=332363&r1=332362&r2=332363&view=diff >>> >>> ============================================================================== >>> --- clang-tools-extra/trunk/clangd/Protocol.h (original) >>> +++ clang-tools-extra/trunk/clangd/Protocol.h Tue May 15 08:29:32 2018 >>> @@ -99,6 +99,9 @@ struct Position { >>> return std::tie(LHS.line, LHS.character) == >>> std::tie(RHS.line, RHS.character); >>> } >>> + friend bool operator!=(const Position &LHS, const Position &RHS) { >>> + return !(LHS == RHS); >>> + } >>> friend bool operator<(const Position &LHS, const Position &RHS) { >>> return std::tie(LHS.line, LHS.character) < >>> std::tie(RHS.line, RHS.character); >>> >>> Modified: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp >>> URL: >>> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp?rev=332363&r1=332362&r2=332363&view=diff >>> >>> ============================================================================== >>> --- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp >>> (original) >>> +++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp Tue >>> May 15 08:29:32 2018 >>> @@ -45,6 +45,16 @@ MATCHER_P(Kind, K, "") { return arg.kind >>> MATCHER_P(Filter, F, "") { return arg.filterText == F; } >>> MATCHER_P(Doc, D, "") { return arg.documentation == D; } >>> MATCHER_P(Detail, D, "") { return arg.detail == D; } >>> +MATCHER_P(InsertInclude, IncludeHeader, "") { >>> + if (arg.additionalTextEdits.size() != 1) >>> + return false; >>> + const auto &Edit = arg.additionalTextEdits[0]; >>> + if (Edit.range.start != Edit.range.end) >>> + return false; >>> + SmallVector<StringRef, 2> Matches; >>> + llvm::Regex RE(R"(#include[ ]*(["<][^">]*[">]))"); >>> + return RE.match(Edit.newText, &Matches) && Matches[1] == >>> IncludeHeader; >>> +} >>> MATCHER_P(PlainText, Text, "") { >>> return arg.insertTextFormat == clangd::InsertTextFormat::PlainText && >>> arg.insertText == Text; >>> @@ -58,6 +68,8 @@ MATCHER(NameContainsFilter, "") { >>> return true; >>> return llvm::StringRef(arg.insertText).contains(arg.filterText); >>> } >>> +MATCHER(HasAdditionalEdits, "") { return >>> !arg.additionalTextEdits.empty(); } >>> + >>> // Shorthand for Contains(Named(Name)). >>> Matcher<const std::vector<CompletionItem> &> Has(std::string Name) { >>> return Contains(Named(std::move(Name))); >>> @@ -75,9 +87,7 @@ std::unique_ptr<SymbolIndex> memIndex(st >>> return MemIndex::build(std::move(Slab).build()); >>> } >>> >>> -// Builds a server and runs code completion. >>> -// If IndexSymbols is non-empty, an index will be built and passed to >>> opts. >>> -CompletionList completions(StringRef Text, >>> +CompletionList completions(ClangdServer &Server, StringRef Text, >>> std::vector<Symbol> IndexSymbols = {}, >>> clangd::CodeCompleteOptions Opts = {}) { >>> std::unique_ptr<SymbolIndex> OverrideIndex; >>> @@ -87,10 +97,6 @@ CompletionList completions(StringRef Tex >>> Opts.Index = OverrideIndex.get(); >>> } >>> >>> - MockFSProvider FS; >>> - MockCompilationDatabase CDB; >>> - IgnoreDiagnostics DiagConsumer; >>> - ClangdServer Server(CDB, FS, DiagConsumer, >>> ClangdServer::optsForTest()); >>> auto File = testPath("foo.cpp"); >>> Annotations Test(Text); >>> runAddDocument(Server, File, Test.code()); >>> @@ -101,6 +107,18 @@ CompletionList completions(StringRef Tex >>> return CompletionList; >>> } >>> >>> +// Builds a server and runs code completion. >>> +// If IndexSymbols is non-empty, an index will be built and passed to >>> opts. >>> +CompletionList completions(StringRef Text, >>> + std::vector<Symbol> IndexSymbols = {}, >>> + clangd::CodeCompleteOptions Opts = {}) { >>> + MockFSProvider FS; >>> + MockCompilationDatabase CDB; >>> + IgnoreDiagnostics DiagConsumer; >>> + ClangdServer Server(CDB, FS, DiagConsumer, >>> ClangdServer::optsForTest()); >>> + return completions(Server, Text, std::move(IndexSymbols), >>> std::move(Opts)); >>> +} >>> + >>> std::string replace(StringRef Haystack, StringRef Needle, StringRef >>> Repl) { >>> std::string Result; >>> raw_string_ostream OS(Result); >>> @@ -505,6 +523,42 @@ TEST(CompletionTest, SemaIndexMergeWithL >>> EXPECT_TRUE(Results.isIncomplete); >>> } >>> >>> +TEST(CompletionTest, IncludeInsertionPreprocessorIntegrationTests) { >>> + MockFSProvider FS; >>> + MockCompilationDatabase CDB; >>> + std::string Subdir = testPath("sub"); >>> + std::string SearchDirArg = (llvm::Twine("-I") + Subdir).str(); >>> + CDB.ExtraClangFlags = {SearchDirArg.c_str()}; >>> + std::string BarHeader = testPath("sub/bar.h"); >>> + FS.Files[BarHeader] = ""; >>> + >>> + IgnoreDiagnostics DiagConsumer; >>> + ClangdServer Server(CDB, FS, DiagConsumer, >>> ClangdServer::optsForTest()); >>> + Symbol::Details Scratch; >>> + auto BarURI = URI::createFile(BarHeader).toString(); >>> + Symbol Sym = cls("ns::X"); >>> + Sym.CanonicalDeclaration.FileURI = BarURI; >>> + Scratch.IncludeHeader = BarURI; >>> + Sym.Detail = &Scratch; >>> + // Shoten include path based on search dirctory and insert. >>> + auto Results = completions(Server, >>> + R"cpp( >>> + int main() { ns::^ } >>> + )cpp", >>> + {Sym}); >>> + EXPECT_THAT(Results.items, >>> + ElementsAre(AllOf(Named("X"), >>> InsertInclude("\"bar.h\"")))); >>> + // Duplicate based on inclusions in preamble. >>> + Results = completions(Server, >>> + R"cpp( >>> + #include "sub/bar.h" // not shortest, so should only match >>> resolved. >>> + int main() { ns::^ } >>> + )cpp", >>> + {Sym}); >>> + EXPECT_THAT(Results.items, >>> + ElementsAre(AllOf(Named("X"), >>> Not(HasAdditionalEdits())))); >>> +} >>> + >>> TEST(CompletionTest, IndexSuppressesPreambleCompletions) { >>> MockFSProvider FS; >>> MockCompilationDatabase CDB; >>> >>> Modified: clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp >>> URL: >>> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp?rev=332363&r1=332362&r2=332363&view=diff >>> >>> ============================================================================== >>> --- clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp (original) >>> +++ clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp Tue May 15 >>> 08:29:32 2018 >>> @@ -8,7 +8,13 @@ >>> >>> >>> //===----------------------------------------------------------------------===// >>> >>> #include "Headers.h" >>> + >>> +#include "Compiler.h" >>> #include "TestFS.h" >>> +#include "clang/Frontend/CompilerInstance.h" >>> +#include "clang/Frontend/CompilerInvocation.h" >>> +#include "clang/Frontend/FrontendActions.h" >>> +#include "clang/Lex/PreprocessorOptions.h" >>> #include "gmock/gmock.h" >>> #include "gtest/gtest.h" >>> >>> @@ -16,30 +22,83 @@ namespace clang { >>> namespace clangd { >>> namespace { >>> >>> +using ::testing::AllOf; >>> +using ::testing::UnorderedElementsAre; >>> + >>> class HeadersTest : public ::testing::Test { >>> public: >>> HeadersTest() { >>> CDB.ExtraClangFlags = {SearchDirArg.c_str()}; >>> FS.Files[MainFile] = ""; >>> + // Make sure directory sub/ exists. >>> + FS.Files[testPath("sub/EMPTY")] = ""; >>> + } >>> + >>> +private: >>> + std::unique_ptr<CompilerInstance> setupClang() { >>> + auto Cmd = CDB.getCompileCommand(MainFile); >>> + assert(static_cast<bool>(Cmd)); >>> + auto VFS = FS.getFileSystem(); >>> + VFS->setCurrentWorkingDirectory(Cmd->Directory); >>> + >>> + std::vector<const char *> Argv; >>> + for (const auto &S : Cmd->CommandLine) >>> + Argv.push_back(S.c_str()); >>> + auto CI = clang::createInvocationFromCommandLine( >>> + Argv, >>> + CompilerInstance::createDiagnostics(new DiagnosticOptions(), >>> + &IgnoreDiags, false), >>> + VFS); >>> + EXPECT_TRUE(static_cast<bool>(CI)); >>> + CI->getFrontendOpts().DisableFree = false; >>> + >>> + // The diagnostic options must be set before creating a >>> CompilerInstance. >>> + CI->getDiagnosticOpts().IgnoreWarnings = true; >>> + auto Clang = prepareCompilerInstance( >>> + std::move(CI), /*Preamble=*/nullptr, >>> + llvm::MemoryBuffer::getMemBuffer(FS.Files[MainFile], MainFile), >>> + std::make_shared<PCHContainerOperations>(), VFS, IgnoreDiags); >>> + >>> + EXPECT_FALSE(Clang->getFrontendOpts().Inputs.empty()); >>> + return Clang; >>> } >>> >>> protected: >>> + std::vector<Inclusion> collectIncludes() { >>> + auto Clang = setupClang(); >>> + PreprocessOnlyAction Action; >>> + EXPECT_TRUE( >>> + Action.BeginSourceFile(*Clang, >>> Clang->getFrontendOpts().Inputs[0])); >>> + std::vector<Inclusion> Inclusions; >>> + >>> Clang->getPreprocessor().addPPCallbacks(collectInclusionsInMainFileCallback( >>> + Clang->getSourceManager(), >>> + [&](Inclusion Inc) { Inclusions.push_back(std::move(Inc)); })); >>> + EXPECT_TRUE(Action.Execute()); >>> + Action.EndSourceFile(); >>> + return Inclusions; >>> + } >>> + >>> // Calculates the include path, or returns "" on error. >>> std::string calculate(PathRef Original, PathRef Preferred = "", >>> + const std::vector<Inclusion> &Inclusions = {}, >>> bool ExpectError = false) { >>> + auto Clang = setupClang(); >>> + PreprocessOnlyAction Action; >>> + EXPECT_TRUE( >>> + Action.BeginSourceFile(*Clang, >>> Clang->getFrontendOpts().Inputs[0])); >>> + >>> if (Preferred.empty()) >>> Preferred = Original; >>> - auto VFS = FS.getFileSystem(); >>> - auto Cmd = CDB.getCompileCommand(MainFile); >>> - assert(static_cast<bool>(Cmd)); >>> - VFS->setCurrentWorkingDirectory(Cmd->Directory); >>> auto ToHeaderFile = [](llvm::StringRef Header) { >>> return HeaderFile{Header, >>> >>> /*Verbatim=*/!llvm::sys::path::is_absolute(Header)}; >>> }; >>> - auto Path = calculateIncludePath(MainFile, FS.Files[MainFile], >>> - ToHeaderFile(Original), >>> - ToHeaderFile(Preferred), *Cmd, >>> VFS); >>> + >>> + auto Path = calculateIncludePath( >>> + MainFile, CDB.getCompileCommand(MainFile)->Directory, >>> + Clang->getPreprocessor().getHeaderSearchInfo(), Inclusions, >>> + ToHeaderFile(Original), ToHeaderFile(Preferred)); >>> + Action.EndSourceFile(); >>> if (!Path) { >>> llvm::consumeError(Path.takeError()); >>> EXPECT_TRUE(ExpectError); >>> @@ -49,52 +108,50 @@ protected: >>> } >>> return std::move(*Path); >>> } >>> + >>> + Expected<Optional<TextEdit>> >>> + insert(const HeaderFile &DeclaringHeader, const HeaderFile >>> &InsertedHeader, >>> + const std::vector<Inclusion> &ExistingInclusions = {}) { >>> + auto Clang = setupClang(); >>> + PreprocessOnlyAction Action; >>> + EXPECT_TRUE( >>> + Action.BeginSourceFile(*Clang, >>> Clang->getFrontendOpts().Inputs[0])); >>> + >>> + IncludeInserter Inserter(MainFile, /*Code=*/"", >>> format::getLLVMStyle(), >>> + CDB.getCompileCommand(MainFile)->Directory, >>> + >>> Clang->getPreprocessor().getHeaderSearchInfo()); >>> + for (const auto &Inc : ExistingInclusions) >>> + Inserter.addExisting(Inc); >>> + >>> + auto Edit = Inserter.insert(DeclaringHeader, InsertedHeader); >>> + Action.EndSourceFile(); >>> + return Edit; >>> + } >>> + >>> MockFSProvider FS; >>> MockCompilationDatabase CDB; >>> std::string MainFile = testPath("main.cpp"); >>> std::string Subdir = testPath("sub"); >>> std::string SearchDirArg = (llvm::Twine("-I") + Subdir).str(); >>> + IgnoringDiagConsumer IgnoreDiags; >>> }; >>> >>> -TEST_F(HeadersTest, InsertInclude) { >>> - std::string Path = testPath("sub/bar.h"); >>> - FS.Files[Path] = ""; >>> - EXPECT_EQ(calculate(Path), "\"bar.h\""); >>> -} >>> +MATCHER_P(Written, Name, "") { return arg.Written == Name; } >>> +MATCHER_P(Resolved, Name, "") { return arg.Resolved == Name; } >>> >>> -TEST_F(HeadersTest, DontInsertDuplicateSameName) { >>> +TEST_F(HeadersTest, CollectRewrittenAndResolved) { >>> FS.Files[MainFile] = R"cpp( >>> -#include "bar.h" >>> +#include "sub/bar.h" // not shortest >>> )cpp"; >>> std::string BarHeader = testPath("sub/bar.h"); >>> FS.Files[BarHeader] = ""; >>> - EXPECT_EQ(calculate(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(calculate("\"sub/bar.h\""), ""); // Duplicate rewritten. >>> - EXPECT_EQ(calculate(BarHeader), ""); // Duplicate resolved. >>> - EXPECT_EQ(calculate(BarHeader, "\"BAR.h\""), ""); // Do not insert >>> preferred. >>> -} >>> >>> -TEST_F(HeadersTest, DontInsertDuplicatePreferred) { >>> - std::string BarHeader = testPath("sub/bar.h"); >>> - FS.Files[BarHeader] = ""; >>> - FS.Files[MainFile] = R"cpp( >>> -#include "sub/bar.h" // not shortest >>> -)cpp"; >>> - // Duplicate written. >>> - EXPECT_EQ(calculate("\"original.h\"", "\"sub/bar.h\""), ""); >>> - // Duplicate resolved. >>> - EXPECT_EQ(calculate("\"original.h\"", BarHeader), ""); >>> + EXPECT_THAT(collectIncludes(), >>> + UnorderedElementsAre( >>> + AllOf(Written("\"sub/bar.h\""), >>> Resolved(BarHeader)))); >>> } >>> >>> -TEST_F(HeadersTest, StillInsertIfTrasitivelyIncluded) { >>> +TEST_F(HeadersTest, OnlyCollectInclusionsInMain) { >>> std::string BazHeader = testPath("sub/baz.h"); >>> FS.Files[BazHeader] = ""; >>> std::string BarHeader = testPath("sub/bar.h"); >>> @@ -104,27 +161,92 @@ TEST_F(HeadersTest, StillInsertIfTrasiti >>> FS.Files[MainFile] = R"cpp( >>> #include "bar.h" >>> )cpp"; >>> - EXPECT_EQ(calculate(BazHeader), "\"baz.h\""); >>> + EXPECT_THAT( >>> + collectIncludes(), >>> + UnorderedElementsAre(AllOf(Written("\"bar.h\""), >>> Resolved(BarHeader)))); >>> +} >>> + >>> +TEST_F(HeadersTest, UnResolvedInclusion) { >>> + FS.Files[MainFile] = R"cpp( >>> +#include "foo.h" >>> +)cpp"; >>> + >>> + EXPECT_THAT(collectIncludes(), >>> + UnorderedElementsAre(AllOf(Written("\"foo.h\""), >>> Resolved("")))); >>> +} >>> + >>> +TEST_F(HeadersTest, InsertInclude) { >>> + std::string Path = testPath("sub/bar.h"); >>> + FS.Files[Path] = ""; >>> + EXPECT_EQ(calculate(Path), "\"bar.h\""); >>> } >>> >>> TEST_F(HeadersTest, DoNotInsertIfInSameFile) { >>> MainFile = testPath("main.h"); >>> - FS.Files[MainFile] = ""; >>> EXPECT_EQ(calculate(MainFile), ""); >>> } >>> >>> +TEST_F(HeadersTest, ShortenedInclude) { >>> + std::string BarHeader = testPath("sub/bar.h"); >>> + EXPECT_EQ(calculate(BarHeader), "\"bar.h\""); >>> +} >>> + >>> +TEST_F(HeadersTest, NotShortenedInclude) { >>> + std::string BarHeader = testPath("sub-2/bar.h"); >>> + EXPECT_EQ(calculate(BarHeader, ""), "\"" + BarHeader + "\""); >>> +} >>> + >>> TEST_F(HeadersTest, PreferredHeader) { >>> - FS.Files[MainFile] = ""; >>> std::string BarHeader = testPath("sub/bar.h"); >>> - FS.Files[BarHeader] = ""; >>> EXPECT_EQ(calculate(BarHeader, "<bar>"), "<bar>"); >>> >>> std::string BazHeader = testPath("sub/baz.h"); >>> - FS.Files[BazHeader] = ""; >>> EXPECT_EQ(calculate(BarHeader, BazHeader), "\"baz.h\""); >>> } >>> >>> +TEST_F(HeadersTest, DontInsertDuplicatePreferred) { >>> + std::vector<Inclusion> Inclusions = { >>> + {Range(), /*Written*/ "\"bar.h\"", /*Resolved*/ ""}}; >>> + EXPECT_EQ(calculate(testPath("sub/bar.h"), "\"bar.h\"", Inclusions), >>> ""); >>> + EXPECT_EQ(calculate("\"x.h\"", "\"bar.h\"", Inclusions), ""); >>> +} >>> + >>> +TEST_F(HeadersTest, DontInsertDuplicateResolved) { >>> + std::string BarHeader = testPath("sub/bar.h"); >>> + std::vector<Inclusion> Inclusions = { >>> + {Range(), /*Written*/ "fake-bar.h", /*Resolved*/ BarHeader}}; >>> + EXPECT_EQ(calculate(BarHeader, "", Inclusions), ""); >>> + // Do not insert preferred. >>> + EXPECT_EQ(calculate(BarHeader, "\"BAR.h\"", Inclusions), ""); >>> +} >>> + >>> +HeaderFile literal(StringRef Include) { >>> + HeaderFile H{Include, /*Verbatim=*/true}; >>> + assert(H.valid()); >>> + return H; >>> +} >>> + >>> +TEST_F(HeadersTest, PreferInserted) { >>> + auto Edit = insert(literal("<x>"), literal("<y>")); >>> + EXPECT_TRUE(static_cast<bool>(Edit)); >>> + EXPECT_TRUE(llvm::StringRef((*Edit)->newText).contains("<y>")); >>> +} >>> + >>> +TEST_F(HeadersTest, ExistingInclusion) { >>> + Inclusion Existing{Range(), /*Written=*/"<c.h>", /*Resolved=*/""}; >>> + auto Edit = insert(literal("<c.h>"), literal("<c.h>"), {Existing}); >>> + EXPECT_TRUE(static_cast<bool>(Edit)); >>> + EXPECT_FALSE(static_cast<bool>(*Edit)); >>> +} >>> + >>> +TEST_F(HeadersTest, ValidateHeaders) { >>> + HeaderFile InvalidHeader{"a.h", /*Verbatim=*/true}; >>> + assert(!InvalidHeader.valid()); >>> + auto Edit = insert(InvalidHeader, literal("\"c.h\"")); >>> + EXPECT_FALSE(static_cast<bool>(Edit)); >>> + llvm::consumeError(Edit.takeError()); >>> +} >>> + >>> } // namespace >>> } // namespace clangd >>> } // namespace clang >>> - >>> >>> >>> _______________________________________________ >>> cfe-commits mailing list >>> cfe-commits@lists.llvm.org >>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >>> >> >>
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits