[clang-tools-extra] r332363 - [clangd] Populate #include insertions as additional edits in completion items.
Eric Liu via cfe-commits
cfe-commits at lists.llvm.org
Wed May 16 12:48:22 PDT 2018
Looking... sorry that I missed the email from this bot.
On Wed, May 16, 2018 at 9:43 PM Galina Kistanova <gkistanova at 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 at 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 at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20180516/30c70bbf/attachment-0001.html>
More information about the cfe-commits
mailing list