2017-12-05 07:20:26 +00:00
|
|
|
//===-- CodeCompleteTests.cpp -----------------------------------*- C++ -*-===//
|
|
|
|
//
|
2019-01-19 08:50:56 +00:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2017-12-05 07:20:26 +00:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
2017-12-19 16:50:37 +00:00
|
|
|
|
2017-12-20 16:06:05 +00:00
|
|
|
#include "Annotations.h"
|
2017-12-05 07:20:26 +00:00
|
|
|
#include "ClangdServer.h"
|
2017-12-20 16:06:05 +00:00
|
|
|
#include "CodeComplete.h"
|
2017-12-05 07:20:26 +00:00
|
|
|
#include "Compiler.h"
|
2017-12-13 12:53:16 +00:00
|
|
|
#include "Matchers.h"
|
2017-12-05 07:20:26 +00:00
|
|
|
#include "Protocol.h"
|
2018-07-02 11:13:16 +00:00
|
|
|
#include "Quality.h"
|
2017-12-19 12:23:48 +00:00
|
|
|
#include "SourceCode.h"
|
2018-02-12 11:37:28 +00:00
|
|
|
#include "SyncAPI.h"
|
2017-12-05 07:20:26 +00:00
|
|
|
#include "TestFS.h"
|
2017-12-19 16:50:37 +00:00
|
|
|
#include "index/MemIndex.h"
|
2018-07-23 10:56:37 +00:00
|
|
|
#include "clang/Sema/CodeCompleteConsumer.h"
|
2018-05-24 14:49:23 +00:00
|
|
|
#include "llvm/Support/Error.h"
|
2018-05-28 12:11:37 +00:00
|
|
|
#include "llvm/Testing/Support/Error.h"
|
2017-12-05 20:11:29 +00:00
|
|
|
#include "gmock/gmock.h"
|
2017-12-05 07:20:26 +00:00
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
2017-12-05 20:11:29 +00:00
|
|
|
|
2017-12-05 07:20:26 +00:00
|
|
|
namespace {
|
2019-01-07 15:45:19 +00:00
|
|
|
using ::llvm::Failed;
|
2017-12-05 20:11:29 +00:00
|
|
|
using ::testing::AllOf;
|
|
|
|
using ::testing::Contains;
|
2017-12-29 14:59:22 +00:00
|
|
|
using ::testing::Each;
|
2017-12-05 20:11:29 +00:00
|
|
|
using ::testing::ElementsAre;
|
2018-03-12 15:28:22 +00:00
|
|
|
using ::testing::Field;
|
[clangd] Add option to fold overloads into a single completion item.
Summary:
Adds a CodeCompleteOption to folds together compatible function/method overloads
into a single item. This feels pretty good (for editors with signatureHelp
support), but has limitations.
This happens in the code completion merge step, so there may be inconsistencies
(e.g. if only one overload made it into the index result list, no folding).
We don't want to bundle together completions that have different side-effects
(include insertion), because we can't constructo a coherent CompletionItem.
This may be confusing for users, as the reason for non-bundling may not
be immediately obvious. (Also, the implementation seems a little fragile)
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits
Differential Revision: https://reviews.llvm.org/D47957
llvm-svn: 334822
2018-06-15 11:06:29 +00:00
|
|
|
using ::testing::HasSubstr;
|
[clangd] Add "member" symbols to the index
Summary:
This adds more symbols to the index:
- member variables and functions
- enum constants in scoped enums
The code completion behavior should remain intact but workspace symbols should
now provide much more useful symbols.
Other symbols should be considered such as the ones in "main files" (files not
being included) but this can be done separately as this introduces its fair
share of problems.
Signed-off-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Reviewers: ioeric, sammccall
Reviewed By: ioeric, sammccall
Subscribers: hokein, sammccall, jkorous, klimek, ilya-biryukov, jkorous-apple, ioeric, MaskRay, cfe-commits
Differential Revision: https://reviews.llvm.org/D44954
llvm-svn: 334017
2018-06-05 14:01:40 +00:00
|
|
|
using ::testing::IsEmpty;
|
2017-12-05 20:11:29 +00:00
|
|
|
using ::testing::Not;
|
2018-01-12 18:30:08 +00:00
|
|
|
using ::testing::UnorderedElementsAre;
|
2017-12-05 07:20:26 +00:00
|
|
|
|
|
|
|
class IgnoreDiagnostics : public DiagnosticsConsumer {
|
2018-03-12 15:28:22 +00:00
|
|
|
void onDiagnosticsReady(PathRef File,
|
2018-03-12 23:22:35 +00:00
|
|
|
std::vector<Diag> Diagnostics) override {}
|
2017-12-05 07:20:26 +00:00
|
|
|
};
|
|
|
|
|
2017-12-05 20:11:29 +00:00
|
|
|
// GMock helpers for matching completion items.
|
2018-07-02 11:13:16 +00:00
|
|
|
MATCHER_P(Named, Name, "") { return arg.Name == Name; }
|
|
|
|
MATCHER_P(Scope, S, "") { return arg.Scope == S; }
|
|
|
|
MATCHER_P(Qualifier, Q, "") { return arg.RequiredQualifier == Q; }
|
2018-06-15 13:34:18 +00:00
|
|
|
MATCHER_P(Labeled, Label, "") {
|
2018-07-02 11:13:16 +00:00
|
|
|
return arg.RequiredQualifier + arg.Name + arg.Signature == Label;
|
2018-06-15 13:34:18 +00:00
|
|
|
}
|
|
|
|
MATCHER_P(SigHelpLabeled, Label, "") { return arg.label == Label; }
|
2018-07-02 11:13:16 +00:00
|
|
|
MATCHER_P(Kind, K, "") { return arg.Kind == K; }
|
|
|
|
MATCHER_P(Doc, D, "") { return arg.Documentation == D; }
|
|
|
|
MATCHER_P(ReturnType, D, "") { return arg.ReturnType == D; }
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
MATCHER_P(HasInclude, IncludeHeader, "") {
|
|
|
|
return !arg.Includes.empty() && arg.Includes[0].Header == IncludeHeader;
|
|
|
|
}
|
2018-05-15 15:29:32 +00:00
|
|
|
MATCHER_P(InsertInclude, IncludeHeader, "") {
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
return !arg.Includes.empty() && arg.Includes[0].Header == IncludeHeader &&
|
|
|
|
bool(arg.Includes[0].Insertion);
|
|
|
|
}
|
|
|
|
MATCHER(InsertInclude, "") {
|
|
|
|
return !arg.Includes.empty() && bool(arg.Includes[0].Insertion);
|
2017-12-08 15:00:59 +00:00
|
|
|
}
|
2018-07-02 11:13:16 +00:00
|
|
|
MATCHER_P(SnippetSuffix, Text, "") { return arg.SnippetSuffix == Text; }
|
2018-07-05 06:20:41 +00:00
|
|
|
MATCHER_P(Origin, OriginSet, "") { return arg.Origin == OriginSet; }
|
2018-11-14 09:05:19 +00:00
|
|
|
MATCHER_P(Signature, S, "") { return arg.Signature == S; }
|
2018-05-15 15:29:32 +00:00
|
|
|
|
2017-12-05 20:11:29 +00:00
|
|
|
// Shorthand for Contains(Named(Name)).
|
2018-07-02 11:13:16 +00:00
|
|
|
Matcher<const std::vector<CodeCompletion> &> Has(std::string Name) {
|
2017-12-05 20:11:29 +00:00
|
|
|
return Contains(Named(std::move(Name)));
|
2017-12-05 07:20:26 +00:00
|
|
|
}
|
2018-07-02 11:13:16 +00:00
|
|
|
Matcher<const std::vector<CodeCompletion> &> Has(std::string Name,
|
2017-12-08 15:00:59 +00:00
|
|
|
CompletionItemKind K) {
|
|
|
|
return Contains(AllOf(Named(std::move(Name)), Kind(K)));
|
2017-12-05 20:11:29 +00:00
|
|
|
}
|
2018-07-02 11:13:16 +00:00
|
|
|
MATCHER(IsDocumented, "") { return !arg.Documentation.empty(); }
|
2018-09-06 18:52:26 +00:00
|
|
|
MATCHER(Deprecated, "") { return arg.Deprecated; }
|
2017-12-05 20:11:29 +00:00
|
|
|
|
2018-01-18 09:27:56 +00:00
|
|
|
std::unique_ptr<SymbolIndex> memIndex(std::vector<Symbol> Symbols) {
|
|
|
|
SymbolSlab::Builder Slab;
|
|
|
|
for (const auto &Sym : Symbols)
|
|
|
|
Slab.insert(Sym);
|
[clangd] SymbolOccurrences -> Refs and cleanup
Summary:
A few things that I noticed while merging the SwapIndex patch:
- SymbolOccurrences and particularly SymbolOccurrenceSlab are unwieldy names,
and these names appear *a lot*. Ref, RefSlab, etc seem clear enough
and read/format much better.
- The asymmetry between SymbolSlab and RefSlab (build() vs freeze()) is
confusing and irritating, and doesn't even save much code.
Avoiding RefSlab::Builder was my idea, but it was a bad one; add it.
- DenseMap<SymbolID, ArrayRef<Ref>> seems like a reasonable compromise for
constructing MemIndex - and means many less wasted allocations than the
current DenseMap<SymbolID, vector<Ref*>> for FileIndex, and none for
slabs.
- RefSlab::find() is not actually used for anything, so we can throw
away the DenseMap and keep the representation much more compact.
- A few naming/consistency fixes: e.g. Slabs,Refs -> Symbols,Refs.
Reviewers: ioeric
Subscribers: ilya-biryukov, MaskRay, jkorous, mgrang, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51605
llvm-svn: 341368
2018-09-04 14:39:56 +00:00
|
|
|
return MemIndex::build(std::move(Slab).build(), RefSlab());
|
2018-01-18 09:27:56 +00:00
|
|
|
}
|
|
|
|
|
2019-01-07 15:45:19 +00:00
|
|
|
CodeCompleteResult completions(ClangdServer &Server, llvm::StringRef TestCode,
|
2018-08-08 08:59:29 +00:00
|
|
|
Position point,
|
|
|
|
std::vector<Symbol> IndexSymbols = {},
|
|
|
|
clangd::CodeCompleteOptions Opts = {}) {
|
|
|
|
std::unique_ptr<SymbolIndex> OverrideIndex;
|
|
|
|
if (!IndexSymbols.empty()) {
|
|
|
|
assert(!Opts.Index && "both Index and IndexSymbols given!");
|
|
|
|
OverrideIndex = memIndex(std::move(IndexSymbols));
|
|
|
|
Opts.Index = OverrideIndex.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto File = testPath("foo.cpp");
|
|
|
|
runAddDocument(Server, File, TestCode);
|
2019-01-07 15:45:19 +00:00
|
|
|
auto CompletionList =
|
|
|
|
llvm::cantFail(runCodeComplete(Server, File, point, Opts));
|
2018-08-08 08:59:29 +00:00
|
|
|
return CompletionList;
|
|
|
|
}
|
|
|
|
|
2019-01-07 15:45:19 +00:00
|
|
|
CodeCompleteResult completions(ClangdServer &Server, llvm::StringRef Text,
|
2018-07-02 11:13:16 +00:00
|
|
|
std::vector<Symbol> IndexSymbols = {},
|
2018-11-14 09:05:19 +00:00
|
|
|
clangd::CodeCompleteOptions Opts = {},
|
|
|
|
PathRef FilePath = "foo.cpp") {
|
2018-01-18 09:27:56 +00:00
|
|
|
std::unique_ptr<SymbolIndex> OverrideIndex;
|
|
|
|
if (!IndexSymbols.empty()) {
|
|
|
|
assert(!Opts.Index && "both Index and IndexSymbols given!");
|
|
|
|
OverrideIndex = memIndex(std::move(IndexSymbols));
|
|
|
|
Opts.Index = OverrideIndex.get();
|
|
|
|
}
|
|
|
|
|
2018-11-14 09:05:19 +00:00
|
|
|
auto File = testPath(FilePath);
|
2017-12-20 16:06:05 +00:00
|
|
|
Annotations Test(Text);
|
2018-03-05 17:28:54 +00:00
|
|
|
runAddDocument(Server, File, Test.code());
|
2018-03-12 23:22:35 +00:00
|
|
|
auto CompletionList =
|
2019-01-07 15:45:19 +00:00
|
|
|
llvm::cantFail(runCodeComplete(Server, File, Test.point(), Opts));
|
2017-12-29 14:59:22 +00:00
|
|
|
return CompletionList;
|
2017-12-05 20:11:29 +00:00
|
|
|
}
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2018-05-15 15:29:32 +00:00
|
|
|
// Builds a server and runs code completion.
|
|
|
|
// If IndexSymbols is non-empty, an index will be built and passed to opts.
|
2019-01-07 15:45:19 +00:00
|
|
|
CodeCompleteResult completions(llvm::StringRef Text,
|
2018-07-02 11:13:16 +00:00
|
|
|
std::vector<Symbol> IndexSymbols = {},
|
2018-11-14 09:05:19 +00:00
|
|
|
clangd::CodeCompleteOptions Opts = {},
|
|
|
|
PathRef FilePath = "foo.cpp") {
|
2018-05-15 15:29:32 +00:00
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
2018-11-14 09:05:19 +00:00
|
|
|
return completions(Server, Text, std::move(IndexSymbols), std::move(Opts),
|
|
|
|
FilePath);
|
2018-05-15 15:29:32 +00:00
|
|
|
}
|
|
|
|
|
2019-01-07 15:45:19 +00:00
|
|
|
std::string replace(llvm::StringRef Haystack, llvm::StringRef Needle,
|
|
|
|
llvm::StringRef Repl) {
|
2018-01-19 14:34:02 +00:00
|
|
|
std::string Result;
|
2019-01-07 15:45:19 +00:00
|
|
|
llvm::raw_string_ostream OS(Result);
|
|
|
|
std::pair<llvm::StringRef, llvm::StringRef> Split;
|
2018-01-19 14:34:02 +00:00
|
|
|
for (Split = Haystack.split(Needle); !Split.second.empty();
|
|
|
|
Split = Split.first.split(Needle))
|
|
|
|
OS << Split.first << Repl;
|
|
|
|
Result += Split.first;
|
|
|
|
OS.flush();
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2018-01-18 09:27:56 +00:00
|
|
|
// Helpers to produce fake index symbols for memIndex() or completions().
|
2018-01-19 14:34:02 +00:00
|
|
|
// USRFormat is a regex replacement string for the unqualified part of the USR.
|
2019-01-07 15:45:19 +00:00
|
|
|
Symbol sym(llvm::StringRef QName, index::SymbolKind Kind,
|
|
|
|
llvm::StringRef USRFormat) {
|
2018-01-18 09:27:56 +00:00
|
|
|
Symbol Sym;
|
2018-01-19 14:34:02 +00:00
|
|
|
std::string USR = "c:"; // We synthesize a few simple cases of USRs by hand!
|
2018-01-18 09:27:56 +00:00
|
|
|
size_t Pos = QName.rfind("::");
|
2019-01-07 15:45:19 +00:00
|
|
|
if (Pos == llvm::StringRef::npos) {
|
2018-01-18 09:27:56 +00:00
|
|
|
Sym.Name = QName;
|
|
|
|
Sym.Scope = "";
|
|
|
|
} else {
|
|
|
|
Sym.Name = QName.substr(Pos + 2);
|
2018-01-19 22:18:21 +00:00
|
|
|
Sym.Scope = QName.substr(0, Pos + 2);
|
|
|
|
USR += "@N@" + replace(QName.substr(0, Pos), "::", "@N@"); // ns:: -> @N@ns
|
2018-01-18 09:27:56 +00:00
|
|
|
}
|
2019-01-07 15:45:19 +00:00
|
|
|
USR += llvm::Regex("^.*$").sub(USRFormat, Sym.Name); // e.g. func -> @F@func#
|
2018-01-19 14:34:02 +00:00
|
|
|
Sym.ID = SymbolID(USR);
|
2018-01-18 09:27:56 +00:00
|
|
|
Sym.SymInfo.Kind = Kind;
|
2018-09-06 18:52:26 +00:00
|
|
|
Sym.Flags |= Symbol::IndexedForCodeCompletion;
|
2018-07-05 06:20:41 +00:00
|
|
|
Sym.Origin = SymbolOrigin::Static;
|
2018-01-18 09:27:56 +00:00
|
|
|
return Sym;
|
|
|
|
}
|
2019-01-07 15:45:19 +00:00
|
|
|
Symbol func(llvm::StringRef Name) { // Assumes the function has no args.
|
2018-01-19 14:34:02 +00:00
|
|
|
return sym(Name, index::SymbolKind::Function, "@F@\\0#"); // no args
|
|
|
|
}
|
2019-01-07 15:45:19 +00:00
|
|
|
Symbol cls(llvm::StringRef Name) {
|
2018-05-30 09:03:39 +00:00
|
|
|
return sym(Name, index::SymbolKind::Class, "@S@\\0");
|
2018-01-19 14:34:02 +00:00
|
|
|
}
|
2019-01-07 15:45:19 +00:00
|
|
|
Symbol var(llvm::StringRef Name) {
|
2018-01-19 14:34:02 +00:00
|
|
|
return sym(Name, index::SymbolKind::Variable, "@\\0");
|
|
|
|
}
|
2019-01-07 15:45:19 +00:00
|
|
|
Symbol ns(llvm::StringRef Name) {
|
2018-05-03 14:53:02 +00:00
|
|
|
return sym(Name, index::SymbolKind::Namespace, "@N@\\0");
|
|
|
|
}
|
|
|
|
Symbol withReferences(int N, Symbol S) {
|
|
|
|
S.References = N;
|
|
|
|
return S;
|
|
|
|
}
|
2018-01-18 09:27:56 +00:00
|
|
|
|
2017-12-05 20:11:29 +00:00
|
|
|
TEST(CompletionTest, Limit) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.Limit = 2;
|
|
|
|
auto Results = completions(R"cpp(
|
2017-12-05 07:20:26 +00:00
|
|
|
struct ClassWithMembers {
|
|
|
|
int AAA();
|
|
|
|
int BBB();
|
|
|
|
int CCC();
|
2018-12-21 09:32:49 +00:00
|
|
|
};
|
2017-12-05 20:11:29 +00:00
|
|
|
int main() { ClassWithMembers().^ }
|
2017-12-05 07:20:26 +00:00
|
|
|
)cpp",
|
2018-01-18 09:27:56 +00:00
|
|
|
/*IndexSymbols=*/{}, Opts);
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_TRUE(Results.HasMore);
|
|
|
|
EXPECT_THAT(Results.Completions, ElementsAre(Named("AAA"), Named("BBB")));
|
2017-12-05 07:20:26 +00:00
|
|
|
}
|
|
|
|
|
2017-12-05 20:11:29 +00:00
|
|
|
TEST(CompletionTest, Filter) {
|
|
|
|
std::string Body = R"cpp(
|
2018-06-14 13:50:30 +00:00
|
|
|
#define MotorCar
|
|
|
|
int Car;
|
2017-12-05 07:20:26 +00:00
|
|
|
struct S {
|
|
|
|
int FooBar;
|
|
|
|
int FooBaz;
|
|
|
|
int Qux;
|
|
|
|
};
|
|
|
|
)cpp";
|
2017-12-05 20:11:29 +00:00
|
|
|
|
2018-06-14 13:50:30 +00:00
|
|
|
// Only items matching the fuzzy query are returned.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(completions(Body + "int main() { S().Foba^ }").Completions,
|
2018-06-14 13:50:30 +00:00
|
|
|
AllOf(Has("FooBar"), Has("FooBaz"), Not(Has("Qux"))));
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2018-06-14 13:50:30 +00:00
|
|
|
// Macros require prefix match.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(completions(Body + "int main() { C^ }").Completions,
|
2018-06-14 13:50:30 +00:00
|
|
|
AllOf(Has("Car"), Not(Has("MotorCar"))));
|
2017-12-05 20:11:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestAfterDotCompletion(clangd::CodeCompleteOptions Opts) {
|
2017-12-08 15:00:59 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
int global_var;
|
2017-12-05 20:11:29 +00:00
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
int global_func();
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2018-09-19 09:35:04 +00:00
|
|
|
// Make sure this is not in preamble.
|
|
|
|
#define MACRO X
|
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
struct GlobalClass {};
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
struct ClassWithMembers {
|
|
|
|
/// Doc for method.
|
|
|
|
int method();
|
2017-12-05 20:11:29 +00:00
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
int field;
|
|
|
|
private:
|
|
|
|
int private_field;
|
|
|
|
};
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
int test() {
|
|
|
|
struct LocalClass {};
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
/// Doc for local_var.
|
|
|
|
int local_var;
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
ClassWithMembers().^
|
|
|
|
}
|
|
|
|
)cpp",
|
2018-01-19 14:34:02 +00:00
|
|
|
{cls("IndexClass"), var("index_var"), func("index_func")}, Opts);
|
2017-12-05 20:11:29 +00:00
|
|
|
|
|
|
|
// Class members. The only items that must be present in after-dot
|
|
|
|
// completion.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
AllOf(Has("method"), Has("field"), Not(Has("ClassWithMembers")),
|
2018-06-07 12:49:17 +00:00
|
|
|
Not(Has("operator=")), Not(Has("~ClassWithMembers"))));
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_IFF(Opts.IncludeIneligibleResults, Results.Completions,
|
2017-12-08 15:00:59 +00:00
|
|
|
Has("private_field"));
|
2017-12-05 20:11:29 +00:00
|
|
|
// Global items.
|
2018-01-19 14:34:02 +00:00
|
|
|
EXPECT_THAT(
|
2018-07-02 11:13:16 +00:00
|
|
|
Results.Completions,
|
2018-01-19 14:34:02 +00:00
|
|
|
Not(AnyOf(Has("global_var"), Has("index_var"), Has("global_func"),
|
|
|
|
Has("global_func()"), Has("index_func"), Has("GlobalClass"),
|
|
|
|
Has("IndexClass"), Has("MACRO"), Has("LocalClass"))));
|
2017-12-05 20:11:29 +00:00
|
|
|
// There should be no code patterns (aka snippets) in after-dot
|
|
|
|
// completion. At least there aren't any we're aware of.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
Not(Contains(Kind(CompletionItemKind::Snippet))));
|
2017-12-05 20:11:29 +00:00
|
|
|
// Check documentation.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_IFF(Opts.IncludeComments, Results.Completions,
|
|
|
|
Contains(IsDocumented()));
|
2017-12-05 20:11:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestGlobalScopeCompletion(clangd::CodeCompleteOptions Opts) {
|
2017-12-08 15:00:59 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
int global_var;
|
|
|
|
int global_func();
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2018-09-19 09:35:04 +00:00
|
|
|
// Make sure this is not in preamble.
|
|
|
|
#define MACRO X
|
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
struct GlobalClass {};
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
struct ClassWithMembers {
|
|
|
|
/// Doc for method.
|
|
|
|
int method();
|
|
|
|
};
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
int test() {
|
|
|
|
struct LocalClass {};
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
/// Doc for local_var.
|
|
|
|
int local_var;
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
^
|
|
|
|
}
|
|
|
|
)cpp",
|
2018-01-19 14:34:02 +00:00
|
|
|
{cls("IndexClass"), var("index_var"), func("index_func")}, Opts);
|
2017-12-05 20:11:29 +00:00
|
|
|
|
|
|
|
// Class members. Should never be present in global completions.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2017-12-05 20:11:29 +00:00
|
|
|
Not(AnyOf(Has("method"), Has("method()"), Has("field"))));
|
|
|
|
// Global items.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
AllOf(Has("global_var"), Has("index_var"), Has("global_func"),
|
2018-01-19 14:34:02 +00:00
|
|
|
Has("index_func" /* our fake symbol doesn't include () */),
|
|
|
|
Has("GlobalClass"), Has("IndexClass")));
|
2017-12-05 20:11:29 +00:00
|
|
|
// A macro.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_IFF(Opts.IncludeMacros, Results.Completions, Has("MACRO"));
|
2017-12-05 20:11:29 +00:00
|
|
|
// Local items. Must be present always.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2017-12-12 12:56:46 +00:00
|
|
|
AllOf(Has("local_var"), Has("LocalClass"),
|
|
|
|
Contains(Kind(CompletionItemKind::Snippet))));
|
2017-12-05 20:11:29 +00:00
|
|
|
// Check documentation.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_IFF(Opts.IncludeComments, Results.Completions,
|
|
|
|
Contains(IsDocumented()));
|
2017-12-05 07:20:26 +00:00
|
|
|
}
|
|
|
|
|
2017-12-05 20:11:29 +00:00
|
|
|
TEST(CompletionTest, CompletionOptions) {
|
2018-01-16 12:21:24 +00:00
|
|
|
auto Test = [&](const clangd::CodeCompleteOptions &Opts) {
|
|
|
|
TestAfterDotCompletion(Opts);
|
|
|
|
TestGlobalScopeCompletion(Opts);
|
|
|
|
};
|
|
|
|
// We used to test every combination of options, but that got too slow (2^N).
|
|
|
|
auto Flags = {
|
2018-03-12 15:28:22 +00:00
|
|
|
&clangd::CodeCompleteOptions::IncludeMacros,
|
2018-05-16 12:32:44 +00:00
|
|
|
&clangd::CodeCompleteOptions::IncludeComments,
|
2018-03-12 15:28:22 +00:00
|
|
|
&clangd::CodeCompleteOptions::IncludeCodePatterns,
|
|
|
|
&clangd::CodeCompleteOptions::IncludeIneligibleResults,
|
2018-01-16 12:21:24 +00:00
|
|
|
};
|
|
|
|
// Test default options.
|
|
|
|
Test({});
|
|
|
|
// Test with one flag flipped.
|
|
|
|
for (auto &F : Flags) {
|
|
|
|
clangd::CodeCompleteOptions O;
|
|
|
|
O.*F ^= true;
|
|
|
|
Test(O);
|
2017-12-05 07:20:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
TEST(CompletionTest, Priorities) {
|
|
|
|
auto Internal = completions(R"cpp(
|
|
|
|
class Foo {
|
|
|
|
public: void pub();
|
|
|
|
protected: void prot();
|
|
|
|
private: void priv();
|
|
|
|
};
|
|
|
|
void Foo::pub() { this->^ }
|
|
|
|
)cpp");
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Internal.Completions,
|
2017-12-08 15:00:59 +00:00
|
|
|
HasSubsequence(Named("priv"), Named("prot"), Named("pub")));
|
|
|
|
|
|
|
|
auto External = completions(R"cpp(
|
|
|
|
class Foo {
|
|
|
|
public: void pub();
|
|
|
|
protected: void prot();
|
|
|
|
private: void priv();
|
|
|
|
};
|
|
|
|
void test() {
|
|
|
|
Foo F;
|
|
|
|
F.^
|
|
|
|
}
|
|
|
|
)cpp");
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(External.Completions,
|
2017-12-08 15:00:59 +00:00
|
|
|
AllOf(Has("pub"), Not(Has("prot")), Not(Has("priv"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, Qualifiers) {
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
class Foo {
|
|
|
|
public: int foo() const;
|
|
|
|
int bar() const;
|
|
|
|
};
|
|
|
|
class Bar : public Foo {
|
|
|
|
int foo() const;
|
|
|
|
};
|
|
|
|
void test() { Bar().^ }
|
|
|
|
)cpp");
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2018-10-24 13:51:44 +00:00
|
|
|
Contains(AllOf(Qualifier(""), Named("bar"))));
|
|
|
|
// Hidden members are not shown.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2018-10-24 13:51:44 +00:00
|
|
|
Not(Contains(AllOf(Qualifier("Foo::"), Named("foo")))));
|
|
|
|
// Private members are not shown.
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
Not(Contains(AllOf(Qualifier(""), Named("foo")))));
|
2017-12-08 15:00:59 +00:00
|
|
|
}
|
|
|
|
|
2018-06-07 12:49:17 +00:00
|
|
|
TEST(CompletionTest, InjectedTypename) {
|
|
|
|
// These are suppressed when accessed as a member...
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(completions("struct X{}; void foo(){ X().^ }").Completions,
|
2018-06-07 12:49:17 +00:00
|
|
|
Not(Has("X")));
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(completions("struct X{ void foo(){ this->^ } };").Completions,
|
2018-06-07 12:49:17 +00:00
|
|
|
Not(Has("X")));
|
|
|
|
// ...but accessible in other, more useful cases.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(completions("struct X{ void foo(){ ^ } };").Completions,
|
|
|
|
Has("X"));
|
|
|
|
EXPECT_THAT(
|
|
|
|
completions("struct Y{}; struct X:Y{ void foo(){ ^ } };").Completions,
|
|
|
|
Has("Y"));
|
2018-06-07 12:49:17 +00:00
|
|
|
EXPECT_THAT(
|
|
|
|
completions(
|
|
|
|
"template<class> struct Y{}; struct X:Y<int>{ void foo(){ ^ } };")
|
2018-07-02 11:13:16 +00:00
|
|
|
.Completions,
|
2018-06-07 12:49:17 +00:00
|
|
|
Has("Y"));
|
|
|
|
// This case is marginal (`using X::X` is useful), we allow it for now.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(completions("struct X{}; void foo(){ X::^ }").Completions,
|
|
|
|
Has("X"));
|
2018-06-07 12:49:17 +00:00
|
|
|
}
|
|
|
|
|
2018-11-30 11:12:40 +00:00
|
|
|
TEST(CompletionTest, SkipInjectedWhenUnqualified) {
|
|
|
|
EXPECT_THAT(completions("struct X { void f() { X^ }};").Completions,
|
|
|
|
ElementsAre(Named("X"), Named("~X")));
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:00:59 +00:00
|
|
|
TEST(CompletionTest, Snippets) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
struct fake {
|
|
|
|
int a;
|
|
|
|
int f(int i, const float f) const;
|
|
|
|
};
|
|
|
|
int main() {
|
|
|
|
fake f;
|
|
|
|
f.^
|
|
|
|
}
|
|
|
|
)cpp",
|
2018-01-18 09:27:56 +00:00
|
|
|
/*IndexSymbols=*/{}, Opts);
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
HasSubsequence(Named("a"),
|
|
|
|
SnippetSuffix("(${1:int i}, ${2:const float f})")));
|
2017-12-08 15:00:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, Kinds) {
|
2018-01-19 14:34:02 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
int variable;
|
|
|
|
struct Struct {};
|
|
|
|
int function();
|
2018-09-19 09:35:04 +00:00
|
|
|
// make sure MACRO is not included in preamble.
|
|
|
|
#define MACRO 10
|
2018-01-19 14:34:02 +00:00
|
|
|
int X = ^
|
|
|
|
)cpp",
|
|
|
|
{func("indexFunction"), var("indexVariable"), cls("indexClass")});
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2018-01-19 14:34:02 +00:00
|
|
|
AllOf(Has("function", CompletionItemKind::Function),
|
|
|
|
Has("variable", CompletionItemKind::Variable),
|
|
|
|
Has("int", CompletionItemKind::Keyword),
|
|
|
|
Has("Struct", CompletionItemKind::Class),
|
|
|
|
Has("MACRO", CompletionItemKind::Text),
|
|
|
|
Has("indexFunction", CompletionItemKind::Function),
|
|
|
|
Has("indexVariable", CompletionItemKind::Variable),
|
|
|
|
Has("indexClass", CompletionItemKind::Class)));
|
2017-12-08 15:00:59 +00:00
|
|
|
|
|
|
|
Results = completions("nam^");
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
Has("namespace", CompletionItemKind::Snippet));
|
2017-12-08 15:00:59 +00:00
|
|
|
}
|
|
|
|
|
2018-01-12 16:16:09 +00:00
|
|
|
TEST(CompletionTest, NoDuplicates) {
|
2018-01-19 14:34:02 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
class Adapter {
|
|
|
|
};
|
2018-01-12 16:16:09 +00:00
|
|
|
|
2018-05-30 09:03:39 +00:00
|
|
|
void f() {
|
2018-01-19 14:34:02 +00:00
|
|
|
Adapter^
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
{cls("Adapter")});
|
2018-01-12 16:16:09 +00:00
|
|
|
|
|
|
|
// Make sure there are no duplicate entries of 'Adapter'.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions, ElementsAre(Named("Adapter")));
|
2018-01-12 16:16:09 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 14:34:02 +00:00
|
|
|
TEST(CompletionTest, ScopedNoIndex) {
|
2018-01-18 09:27:56 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
2018-06-14 13:50:30 +00:00
|
|
|
namespace fake { int BigBang, Babble, Box; };
|
|
|
|
int main() { fake::ba^ }
|
2018-01-19 14:34:02 +00:00
|
|
|
")cpp");
|
2018-06-14 13:50:30 +00:00
|
|
|
// Babble is a better match than BigBang. Box doesn't match at all.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
ElementsAre(Named("Babble"), Named("BigBang")));
|
2018-01-10 14:44:34 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 14:34:02 +00:00
|
|
|
TEST(CompletionTest, Scoped) {
|
2018-01-18 09:27:56 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
2018-06-14 13:50:30 +00:00
|
|
|
namespace fake { int Babble, Box; };
|
|
|
|
int main() { fake::ba^ }
|
2018-01-19 14:34:02 +00:00
|
|
|
")cpp",
|
|
|
|
{var("fake::BigBang")});
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
ElementsAre(Named("Babble"), Named("BigBang")));
|
2017-12-19 16:50:37 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 14:34:02 +00:00
|
|
|
TEST(CompletionTest, ScopedWithFilter) {
|
2018-01-18 09:27:56 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
void f() { ns::x^ }
|
|
|
|
)cpp",
|
|
|
|
{cls("ns::XYZ"), func("ns::foo")});
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions, UnorderedElementsAre(Named("XYZ")));
|
2017-12-19 16:50:37 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 14:53:02 +00:00
|
|
|
TEST(CompletionTest, ReferencesAffectRanking) {
|
2018-07-25 11:26:35 +00:00
|
|
|
auto Results = completions("int main() { abs^ }", {ns("absl"), func("absb")});
|
2019-01-03 13:28:05 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
HasSubsequence(Named("absb"), Named("absl")));
|
2018-05-03 14:53:02 +00:00
|
|
|
Results = completions("int main() { abs^ }",
|
2018-07-25 11:26:35 +00:00
|
|
|
{withReferences(10000, ns("absl")), func("absb")});
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
HasSubsequence(Named("absl"), Named("absb")));
|
2018-05-03 14:53:02 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 14:34:02 +00:00
|
|
|
TEST(CompletionTest, GlobalQualified) {
|
2018-01-18 09:27:56 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
void f() { ::^ }
|
|
|
|
)cpp",
|
|
|
|
{cls("XYZ")});
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
AllOf(Has("XYZ", CompletionItemKind::Class),
|
|
|
|
Has("f", CompletionItemKind::Function)));
|
2017-12-19 16:50:37 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 14:34:02 +00:00
|
|
|
TEST(CompletionTest, FullyQualified) {
|
2018-01-18 09:27:56 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
2018-01-19 14:34:02 +00:00
|
|
|
namespace ns { void bar(); }
|
2018-01-18 09:27:56 +00:00
|
|
|
void f() { ::ns::^ }
|
|
|
|
)cpp",
|
|
|
|
{cls("ns::XYZ")});
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
AllOf(Has("XYZ", CompletionItemKind::Class),
|
|
|
|
Has("bar", CompletionItemKind::Function)));
|
2018-01-19 14:34:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, SemaIndexMerge) {
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
namespace ns { int local; void both(); }
|
|
|
|
void f() { ::ns::^ }
|
|
|
|
)cpp",
|
|
|
|
{func("ns::both"), cls("ns::Index")});
|
|
|
|
// We get results from both index and sema, with no duplicates.
|
2018-07-05 06:20:41 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(
|
|
|
|
AllOf(Named("local"), Origin(SymbolOrigin::AST)),
|
|
|
|
AllOf(Named("Index"), Origin(SymbolOrigin::Static)),
|
|
|
|
AllOf(Named("both"),
|
|
|
|
Origin(SymbolOrigin::AST | SymbolOrigin::Static))));
|
2017-12-19 16:50:37 +00:00
|
|
|
}
|
|
|
|
|
2018-01-25 09:20:09 +00:00
|
|
|
TEST(CompletionTest, SemaIndexMergeWithLimit) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.Limit = 1;
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
namespace ns { int local; void both(); }
|
|
|
|
void f() { ::ns::^ }
|
|
|
|
)cpp",
|
|
|
|
{func("ns::both"), cls("ns::Index")}, Opts);
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_EQ(Results.Completions.size(), Opts.Limit);
|
|
|
|
EXPECT_TRUE(Results.HasMore);
|
2018-01-25 09:20:09 +00:00
|
|
|
}
|
|
|
|
|
2018-05-15 15:29:32 +00:00
|
|
|
TEST(CompletionTest, IncludeInsertionPreprocessorIntegrationTests) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
std::string Subdir = testPath("sub");
|
2018-10-20 15:30:37 +00:00
|
|
|
std::string SearchDirArg = (Twine("-I") + Subdir).str();
|
2018-05-15 15:29:32 +00:00
|
|
|
CDB.ExtraClangFlags = {SearchDirArg.c_str()};
|
|
|
|
std::string BarHeader = testPath("sub/bar.h");
|
|
|
|
FS.Files[BarHeader] = "";
|
|
|
|
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
[clangd] Cleanup: stop passing around list of supported URI schemes.
Summary:
Instead of passing around a list of supported URI schemes in clangd, we
expose an interface to convert a path to URI using any compatible scheme
that has been registered. It favors customized schemes and falls
back to "file" when no other scheme works.
Changes in this patch are:
- URI::create(AbsPath, URISchemes) -> URI::create(AbsPath). The new API finds a
compatible scheme from the registry.
- Remove URISchemes option everywhere (ClangdServer, SymbolCollecter, FileIndex etc).
- Unit tests will use "unittest" by default.
- Move "test" scheme from ClangdLSPServer to ClangdMain.cpp, and only
register the test scheme when lit-test or enable-lit-scheme is set.
(The new flag is added to make lit protocol.test work; I wonder if there
is alternative here.)
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D54800
llvm-svn: 347467
2018-11-22 15:02:05 +00:00
|
|
|
auto BarURI = URI::create(BarHeader).toString();
|
2018-05-15 15:29:32 +00:00
|
|
|
Symbol Sym = cls("ns::X");
|
2018-11-14 11:55:45 +00:00
|
|
|
Sym.CanonicalDeclaration.FileURI = BarURI.c_str();
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
Sym.IncludeHeaders.emplace_back(BarURI, 1);
|
2018-05-15 15:29:32 +00:00
|
|
|
// Shoten include path based on search dirctory and insert.
|
|
|
|
auto Results = completions(Server,
|
|
|
|
R"cpp(
|
|
|
|
int main() { ns::^ }
|
|
|
|
)cpp",
|
|
|
|
{Sym});
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
ElementsAre(AllOf(Named("X"), InsertInclude("\"bar.h\""))));
|
2018-05-15 15:29:32 +00:00
|
|
|
// 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});
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions, ElementsAre(AllOf(Named("X"), Labeled("X"),
|
|
|
|
Not(InsertInclude()))));
|
2018-05-15 15:29:32 +00:00
|
|
|
}
|
|
|
|
|
2018-05-30 09:03:39 +00:00
|
|
|
TEST(CompletionTest, NoIncludeInsertionWhenDeclFoundInFile) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
Symbol SymX = cls("ns::X");
|
|
|
|
Symbol SymY = cls("ns::Y");
|
|
|
|
std::string BarHeader = testPath("bar.h");
|
[clangd] Cleanup: stop passing around list of supported URI schemes.
Summary:
Instead of passing around a list of supported URI schemes in clangd, we
expose an interface to convert a path to URI using any compatible scheme
that has been registered. It favors customized schemes and falls
back to "file" when no other scheme works.
Changes in this patch are:
- URI::create(AbsPath, URISchemes) -> URI::create(AbsPath). The new API finds a
compatible scheme from the registry.
- Remove URISchemes option everywhere (ClangdServer, SymbolCollecter, FileIndex etc).
- Unit tests will use "unittest" by default.
- Move "test" scheme from ClangdLSPServer to ClangdMain.cpp, and only
register the test scheme when lit-test or enable-lit-scheme is set.
(The new flag is added to make lit protocol.test work; I wonder if there
is alternative here.)
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D54800
llvm-svn: 347467
2018-11-22 15:02:05 +00:00
|
|
|
auto BarURI = URI::create(BarHeader).toString();
|
2018-11-14 11:55:45 +00:00
|
|
|
SymX.CanonicalDeclaration.FileURI = BarURI.c_str();
|
|
|
|
SymY.CanonicalDeclaration.FileURI = BarURI.c_str();
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
SymX.IncludeHeaders.emplace_back("<bar>", 1);
|
|
|
|
SymY.IncludeHeaders.emplace_back("<bar>", 1);
|
2018-05-30 09:03:39 +00:00
|
|
|
// Shoten include path based on search dirctory and insert.
|
|
|
|
auto Results = completions(Server,
|
|
|
|
R"cpp(
|
|
|
|
namespace ns {
|
|
|
|
class X;
|
2018-12-21 09:32:49 +00:00
|
|
|
class Y {};
|
2018-05-30 09:03:39 +00:00
|
|
|
}
|
|
|
|
int main() { ns::^ }
|
|
|
|
)cpp",
|
|
|
|
{SymX, SymY});
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
ElementsAre(AllOf(Named("X"), Not(InsertInclude())),
|
|
|
|
AllOf(Named("Y"), Not(InsertInclude()))));
|
2018-05-30 09:03:39 +00:00
|
|
|
}
|
|
|
|
|
2018-01-12 18:30:08 +00:00
|
|
|
TEST(CompletionTest, IndexSuppressesPreambleCompletions) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
2018-03-05 17:28:54 +00:00
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
2018-01-12 18:30:08 +00:00
|
|
|
|
2018-02-16 09:41:43 +00:00
|
|
|
FS.Files[testPath("bar.h")] =
|
2018-01-24 17:53:32 +00:00
|
|
|
R"cpp(namespace ns { struct preamble { int member; }; })cpp";
|
2018-02-16 09:41:43 +00:00
|
|
|
auto File = testPath("foo.cpp");
|
2018-01-12 18:30:08 +00:00
|
|
|
Annotations Test(R"cpp(
|
|
|
|
#include "bar.h"
|
|
|
|
namespace ns { int local; }
|
2018-01-24 17:53:32 +00:00
|
|
|
void f() { ns::^; }
|
2018-12-21 09:32:49 +00:00
|
|
|
void f2() { ns::preamble().$2^; }
|
2018-01-12 18:30:08 +00:00
|
|
|
)cpp");
|
2018-03-05 17:28:54 +00:00
|
|
|
runAddDocument(Server, File, Test.code());
|
2018-01-12 18:30:08 +00:00
|
|
|
clangd::CodeCompleteOptions Opts = {};
|
|
|
|
|
2018-01-18 09:27:56 +00:00
|
|
|
auto I = memIndex({var("ns::index")});
|
2018-01-12 18:30:08 +00:00
|
|
|
Opts.Index = I.get();
|
2018-03-12 23:22:35 +00:00
|
|
|
auto WithIndex = cantFail(runCodeComplete(Server, File, Test.point(), Opts));
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(WithIndex.Completions,
|
2018-01-12 18:30:08 +00:00
|
|
|
UnorderedElementsAre(Named("local"), Named("index")));
|
2018-01-24 17:53:32 +00:00
|
|
|
auto ClassFromPreamble =
|
2018-03-12 23:22:35 +00:00
|
|
|
cantFail(runCodeComplete(Server, File, Test.point("2"), Opts));
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(ClassFromPreamble.Completions, Contains(Named("member")));
|
2018-02-13 08:59:23 +00:00
|
|
|
|
|
|
|
Opts.Index = nullptr;
|
2018-03-12 23:22:35 +00:00
|
|
|
auto WithoutIndex =
|
|
|
|
cantFail(runCodeComplete(Server, File, Test.point(), Opts));
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(WithoutIndex.Completions,
|
2018-02-13 08:59:23 +00:00
|
|
|
UnorderedElementsAre(Named("local"), Named("preamble")));
|
2018-01-12 18:30:08 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 12:36:06 +00:00
|
|
|
// This verifies that we get normal preprocessor completions in the preamble.
|
|
|
|
// This is a regression test for an old bug: if we override the preamble and
|
|
|
|
// try to complete inside it, clang kicks our completion point just outside the
|
|
|
|
// preamble, resulting in always getting top-level completions.
|
|
|
|
TEST(CompletionTest, CompletionInPreamble) {
|
2018-09-14 18:49:16 +00:00
|
|
|
auto Results = completions(R"cpp(
|
2018-09-14 12:36:06 +00:00
|
|
|
#ifnd^ef FOO_H_
|
|
|
|
#define BAR_H_
|
|
|
|
#include <bar.h>
|
|
|
|
int foo() {}
|
|
|
|
#endif
|
|
|
|
)cpp")
|
2018-09-14 18:49:16 +00:00
|
|
|
.Completions;
|
|
|
|
EXPECT_THAT(Results, ElementsAre(Named("ifndef")));
|
2018-09-14 19:42:37 +00:00
|
|
|
}
|
2018-09-14 12:36:06 +00:00
|
|
|
|
2018-01-18 09:27:56 +00:00
|
|
|
TEST(CompletionTest, DynamicIndexMultiFile) {
|
2017-12-19 18:00:37 +00:00
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
2018-03-05 17:28:54 +00:00
|
|
|
auto Opts = ClangdServer::optsForTest();
|
|
|
|
Opts.BuildDynamicSymbolIndex = true;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, Opts);
|
2017-12-19 18:00:37 +00:00
|
|
|
|
2018-02-19 18:48:44 +00:00
|
|
|
FS.Files[testPath("foo.h")] = R"cpp(
|
2018-01-09 17:32:00 +00:00
|
|
|
namespace ns { class XYZ {}; void foo(int x) {} }
|
2018-02-19 18:48:44 +00:00
|
|
|
)cpp";
|
2018-03-05 17:28:54 +00:00
|
|
|
runAddDocument(Server, testPath("foo.cpp"), R"cpp(
|
2018-02-19 18:48:44 +00:00
|
|
|
#include "foo.h"
|
2018-02-13 08:59:23 +00:00
|
|
|
)cpp");
|
2017-12-19 18:00:37 +00:00
|
|
|
|
2018-02-16 09:41:43 +00:00
|
|
|
auto File = testPath("bar.cpp");
|
2017-12-20 16:06:05 +00:00
|
|
|
Annotations Test(R"cpp(
|
2018-01-09 17:32:00 +00:00
|
|
|
namespace ns {
|
|
|
|
class XXX {};
|
|
|
|
/// Doooc
|
|
|
|
void fooooo() {}
|
|
|
|
}
|
2017-12-19 18:00:37 +00:00
|
|
|
void f() { ns::^ }
|
|
|
|
)cpp");
|
2018-03-05 17:28:54 +00:00
|
|
|
runAddDocument(Server, File, Test.code());
|
2017-12-19 18:00:37 +00:00
|
|
|
|
2018-03-12 23:22:35 +00:00
|
|
|
auto Results = cantFail(runCodeComplete(Server, File, Test.point(), {}));
|
2017-12-19 18:00:37 +00:00
|
|
|
// "XYZ" and "foo" are not included in the file being completed but are still
|
|
|
|
// visible through the index.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions, Has("XYZ", CompletionItemKind::Class));
|
|
|
|
EXPECT_THAT(Results.Completions, Has("foo", CompletionItemKind::Function));
|
|
|
|
EXPECT_THAT(Results.Completions, Has("XXX", CompletionItemKind::Class));
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
Contains((Named("fooooo"), Kind(CompletionItemKind::Function),
|
|
|
|
Doc("Doooc"), ReturnType("void"))));
|
2017-12-19 18:00:37 +00:00
|
|
|
}
|
|
|
|
|
2018-05-16 12:32:49 +00:00
|
|
|
TEST(CompletionTest, Documentation) {
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
// Non-doxygen comment.
|
|
|
|
int foo();
|
|
|
|
/// Doxygen comment.
|
|
|
|
/// \param int a
|
|
|
|
int bar(int a);
|
|
|
|
/* Multi-line
|
|
|
|
block comment
|
|
|
|
*/
|
|
|
|
int baz();
|
|
|
|
|
|
|
|
int x = ^
|
|
|
|
)cpp");
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2018-05-16 12:32:49 +00:00
|
|
|
Contains(AllOf(Named("foo"), Doc("Non-doxygen comment."))));
|
|
|
|
EXPECT_THAT(
|
2018-07-02 11:13:16 +00:00
|
|
|
Results.Completions,
|
2018-05-16 12:32:49 +00:00
|
|
|
Contains(AllOf(Named("bar"), Doc("Doxygen comment.\n\\param int a"))));
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2018-05-16 12:32:49 +00:00
|
|
|
Contains(AllOf(Named("baz"), Doc("Multi-line\nblock comment"))));
|
|
|
|
}
|
|
|
|
|
[clangd] Add "member" symbols to the index
Summary:
This adds more symbols to the index:
- member variables and functions
- enum constants in scoped enums
The code completion behavior should remain intact but workspace symbols should
now provide much more useful symbols.
Other symbols should be considered such as the ones in "main files" (files not
being included) but this can be done separately as this introduces its fair
share of problems.
Signed-off-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Reviewers: ioeric, sammccall
Reviewed By: ioeric, sammccall
Subscribers: hokein, sammccall, jkorous, klimek, ilya-biryukov, jkorous-apple, ioeric, MaskRay, cfe-commits
Differential Revision: https://reviews.llvm.org/D44954
llvm-svn: 334017
2018-06-05 14:01:40 +00:00
|
|
|
TEST(CompletionTest, GlobalCompletionFiltering) {
|
|
|
|
|
|
|
|
Symbol Class = cls("XYZ");
|
2018-09-06 18:52:26 +00:00
|
|
|
Class.Flags = static_cast<Symbol::SymbolFlag>(
|
|
|
|
Class.Flags & ~(Symbol::IndexedForCodeCompletion));
|
[clangd] Add "member" symbols to the index
Summary:
This adds more symbols to the index:
- member variables and functions
- enum constants in scoped enums
The code completion behavior should remain intact but workspace symbols should
now provide much more useful symbols.
Other symbols should be considered such as the ones in "main files" (files not
being included) but this can be done separately as this introduces its fair
share of problems.
Signed-off-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Reviewers: ioeric, sammccall
Reviewed By: ioeric, sammccall
Subscribers: hokein, sammccall, jkorous, klimek, ilya-biryukov, jkorous-apple, ioeric, MaskRay, cfe-commits
Differential Revision: https://reviews.llvm.org/D44954
llvm-svn: 334017
2018-06-05 14:01:40 +00:00
|
|
|
Symbol Func = func("XYZ::foooo");
|
2018-09-06 18:52:26 +00:00
|
|
|
Func.Flags = static_cast<Symbol::SymbolFlag>(
|
|
|
|
Func.Flags & ~(Symbol::IndexedForCodeCompletion));
|
[clangd] Add "member" symbols to the index
Summary:
This adds more symbols to the index:
- member variables and functions
- enum constants in scoped enums
The code completion behavior should remain intact but workspace symbols should
now provide much more useful symbols.
Other symbols should be considered such as the ones in "main files" (files not
being included) but this can be done separately as this introduces its fair
share of problems.
Signed-off-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Reviewers: ioeric, sammccall
Reviewed By: ioeric, sammccall
Subscribers: hokein, sammccall, jkorous, klimek, ilya-biryukov, jkorous-apple, ioeric, MaskRay, cfe-commits
Differential Revision: https://reviews.llvm.org/D44954
llvm-svn: 334017
2018-06-05 14:01:40 +00:00
|
|
|
|
|
|
|
auto Results = completions(R"(// void f() {
|
|
|
|
XYZ::foooo^
|
|
|
|
})",
|
|
|
|
{Class, Func});
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions, IsEmpty());
|
[clangd] Add "member" symbols to the index
Summary:
This adds more symbols to the index:
- member variables and functions
- enum constants in scoped enums
The code completion behavior should remain intact but workspace symbols should
now provide much more useful symbols.
Other symbols should be considered such as the ones in "main files" (files not
being included) but this can be done separately as this introduces its fair
share of problems.
Signed-off-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Reviewers: ioeric, sammccall
Reviewed By: ioeric, sammccall
Subscribers: hokein, sammccall, jkorous, klimek, ilya-biryukov, jkorous-apple, ioeric, MaskRay, cfe-commits
Differential Revision: https://reviews.llvm.org/D44954
llvm-svn: 334017
2018-06-05 14:01:40 +00:00
|
|
|
}
|
|
|
|
|
2018-01-25 09:44:06 +00:00
|
|
|
TEST(CodeCompleteTest, DisableTypoCorrection) {
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
namespace clang { int v; }
|
|
|
|
void f() { clangd::^
|
|
|
|
)cpp");
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_TRUE(Results.Completions.empty());
|
2018-01-25 09:44:06 +00:00
|
|
|
}
|
|
|
|
|
2018-03-06 16:45:21 +00:00
|
|
|
TEST(CodeCompleteTest, NoColonColonAtTheEnd) {
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
namespace clang { }
|
|
|
|
void f() {
|
|
|
|
clan^
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions, Contains(Labeled("clang")));
|
|
|
|
EXPECT_THAT(Results.Completions, Not(Contains(Labeled("clang::"))));
|
2018-03-06 16:45:21 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 15:23:44 +00:00
|
|
|
TEST(CompletionTest, BacktrackCrashes) {
|
|
|
|
// Sema calls code completion callbacks twice in these cases.
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
namespace ns {
|
|
|
|
struct FooBarBaz {};
|
|
|
|
} // namespace ns
|
|
|
|
|
|
|
|
int foo(ns::FooBar^
|
|
|
|
)cpp");
|
|
|
|
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions, ElementsAre(Labeled("FooBarBaz")));
|
2018-03-16 15:23:44 +00:00
|
|
|
|
|
|
|
// Check we don't crash in that case too.
|
|
|
|
completions(R"cpp(
|
|
|
|
struct FooBarBaz {};
|
|
|
|
void test() {
|
|
|
|
if (FooBarBaz * x^) {}
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
}
|
|
|
|
|
2018-05-24 11:20:19 +00:00
|
|
|
TEST(CompletionTest, CompleteInMacroWithStringification) {
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
void f(const char *, int x);
|
|
|
|
#define F(x) f(#x, x)
|
|
|
|
|
|
|
|
namespace ns {
|
|
|
|
int X;
|
|
|
|
int Y;
|
|
|
|
} // namespace ns
|
|
|
|
|
|
|
|
int f(int input_num) {
|
|
|
|
F(ns::^)
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2018-05-24 11:20:19 +00:00
|
|
|
UnorderedElementsAre(Named("X"), Named("Y")));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, CompleteInMacroAndNamespaceWithStringification) {
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
void f(const char *, int x);
|
|
|
|
#define F(x) f(#x, x)
|
|
|
|
|
|
|
|
namespace ns {
|
|
|
|
int X;
|
|
|
|
|
|
|
|
int f(int input_num) {
|
|
|
|
F(^)
|
|
|
|
}
|
|
|
|
} // namespace ns
|
|
|
|
)cpp");
|
|
|
|
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions, Contains(Named("X")));
|
2018-05-24 11:20:19 +00:00
|
|
|
}
|
|
|
|
|
2018-07-11 13:15:31 +00:00
|
|
|
TEST(CompletionTest, IgnoreCompleteInExcludedPPBranchWithRecoveryContext) {
|
2018-03-16 15:23:44 +00:00
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
int bar(int param_in_bar) {
|
|
|
|
}
|
|
|
|
|
|
|
|
int foo(int param_in_foo) {
|
|
|
|
#if 0
|
2018-07-11 13:15:31 +00:00
|
|
|
// In recorvery mode, "param_in_foo" will also be suggested among many other
|
|
|
|
// unrelated symbols; however, this is really a special case where this works.
|
|
|
|
// If the #if block is outside of the function, "param_in_foo" is still
|
|
|
|
// suggested, but "bar" and "foo" are missing. So the recovery mode doesn't
|
|
|
|
// really provide useful results in excluded branches.
|
2018-03-16 15:23:44 +00:00
|
|
|
par^
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
|
2018-07-11 13:15:31 +00:00
|
|
|
EXPECT_TRUE(Results.Completions.empty());
|
2018-03-16 15:23:44 +00:00
|
|
|
}
|
2019-01-07 15:45:19 +00:00
|
|
|
SignatureHelp signatures(llvm::StringRef Text, Position Point,
|
2018-08-17 09:32:30 +00:00
|
|
|
std::vector<Symbol> IndexSymbols = {}) {
|
|
|
|
std::unique_ptr<SymbolIndex> Index;
|
|
|
|
if (!IndexSymbols.empty())
|
|
|
|
Index = memIndex(IndexSymbols);
|
|
|
|
|
2018-01-18 09:27:56 +00:00
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
2018-08-17 09:32:30 +00:00
|
|
|
ClangdServer::Options Opts = ClangdServer::optsForTest();
|
|
|
|
Opts.StaticIndex = Index.get();
|
|
|
|
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, Opts);
|
2018-02-16 09:41:43 +00:00
|
|
|
auto File = testPath("foo.cpp");
|
2018-08-30 13:14:31 +00:00
|
|
|
runAddDocument(Server, File, Text);
|
2019-01-07 15:45:19 +00:00
|
|
|
return llvm::cantFail(runSignatureHelp(Server, File, Point));
|
2018-08-30 13:14:31 +00:00
|
|
|
}
|
|
|
|
|
2019-01-07 15:45:19 +00:00
|
|
|
SignatureHelp signatures(llvm::StringRef Text,
|
2018-08-30 13:14:31 +00:00
|
|
|
std::vector<Symbol> IndexSymbols = {}) {
|
2018-01-18 09:27:56 +00:00
|
|
|
Annotations Test(Text);
|
2018-08-30 13:14:31 +00:00
|
|
|
return signatures(Test.code(), Test.point(), std::move(IndexSymbols));
|
2018-01-18 09:27:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MATCHER_P(ParamsAre, P, "") {
|
|
|
|
if (P.size() != arg.parameters.size())
|
|
|
|
return false;
|
|
|
|
for (unsigned I = 0; I < P.size(); ++I)
|
|
|
|
if (P[I] != arg.parameters[I].label)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
2018-08-17 09:32:30 +00:00
|
|
|
MATCHER_P(SigDoc, Doc, "") { return arg.documentation == Doc; }
|
2018-01-18 09:27:56 +00:00
|
|
|
|
|
|
|
Matcher<SignatureInformation> Sig(std::string Label,
|
|
|
|
std::vector<std::string> Params) {
|
2018-06-15 13:34:18 +00:00
|
|
|
return AllOf(SigHelpLabeled(Label), ParamsAre(Params));
|
2018-01-18 09:27:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(SignatureHelpTest, Overloads) {
|
|
|
|
auto Results = signatures(R"cpp(
|
|
|
|
void foo(int x, int y);
|
|
|
|
void foo(int x, float y);
|
|
|
|
void foo(float x, int y);
|
|
|
|
void foo(float x, float y);
|
|
|
|
void bar(int x, int y = 0);
|
|
|
|
int main() { foo(^); }
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(Results.signatures,
|
|
|
|
UnorderedElementsAre(
|
|
|
|
Sig("foo(float x, float y) -> void", {"float x", "float y"}),
|
|
|
|
Sig("foo(float x, int y) -> void", {"float x", "int y"}),
|
|
|
|
Sig("foo(int x, float y) -> void", {"int x", "float y"}),
|
|
|
|
Sig("foo(int x, int y) -> void", {"int x", "int y"})));
|
|
|
|
// We always prefer the first signature.
|
|
|
|
EXPECT_EQ(0, Results.activeSignature);
|
|
|
|
EXPECT_EQ(0, Results.activeParameter);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(SignatureHelpTest, DefaultArgs) {
|
|
|
|
auto Results = signatures(R"cpp(
|
|
|
|
void bar(int x, int y = 0);
|
|
|
|
void bar(float x = 0, int y = 42);
|
|
|
|
int main() { bar(^
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(Results.signatures,
|
|
|
|
UnorderedElementsAre(
|
|
|
|
Sig("bar(int x, int y = 0) -> void", {"int x", "int y = 0"}),
|
|
|
|
Sig("bar(float x = 0, int y = 42) -> void",
|
|
|
|
{"float x = 0", "int y = 42"})));
|
|
|
|
EXPECT_EQ(0, Results.activeSignature);
|
|
|
|
EXPECT_EQ(0, Results.activeParameter);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(SignatureHelpTest, ActiveArg) {
|
|
|
|
auto Results = signatures(R"cpp(
|
|
|
|
int baz(int a, int b, int c);
|
|
|
|
int main() { baz(baz(1,2,3), ^); }
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(Results.signatures,
|
|
|
|
ElementsAre(Sig("baz(int a, int b, int c) -> int",
|
|
|
|
{"int a", "int b", "int c"})));
|
|
|
|
EXPECT_EQ(0, Results.activeSignature);
|
|
|
|
EXPECT_EQ(1, Results.activeParameter);
|
|
|
|
}
|
|
|
|
|
2018-08-30 13:14:31 +00:00
|
|
|
TEST(SignatureHelpTest, OpeningParen) {
|
|
|
|
llvm::StringLiteral Tests[] = {// Recursive function call.
|
|
|
|
R"cpp(
|
|
|
|
int foo(int a, int b, int c);
|
|
|
|
int main() {
|
|
|
|
foo(foo $p^( foo(10, 10, 10), ^ )));
|
|
|
|
})cpp",
|
|
|
|
// Functional type cast.
|
|
|
|
R"cpp(
|
|
|
|
struct Foo {
|
|
|
|
Foo(int a, int b, int c);
|
|
|
|
};
|
|
|
|
int main() {
|
|
|
|
Foo $p^( 10, ^ );
|
|
|
|
})cpp",
|
|
|
|
// New expression.
|
|
|
|
R"cpp(
|
|
|
|
struct Foo {
|
|
|
|
Foo(int a, int b, int c);
|
|
|
|
};
|
|
|
|
int main() {
|
|
|
|
new Foo $p^( 10, ^ );
|
|
|
|
})cpp",
|
|
|
|
// Macro expansion.
|
|
|
|
R"cpp(
|
|
|
|
int foo(int a, int b, int c);
|
|
|
|
#define FOO foo(
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
// Macro expansions.
|
|
|
|
$p^FOO 10, ^ );
|
|
|
|
})cpp",
|
|
|
|
// Macro arguments.
|
|
|
|
R"cpp(
|
|
|
|
int foo(int a, int b, int c);
|
|
|
|
int main() {
|
|
|
|
#define ID(X) X
|
|
|
|
ID(foo $p^( foo(10), ^ ))
|
|
|
|
})cpp"};
|
|
|
|
|
|
|
|
for (auto Test : Tests) {
|
|
|
|
Annotations Code(Test);
|
|
|
|
EXPECT_EQ(signatures(Code.code(), Code.point()).argListStart,
|
|
|
|
Code.point("p"))
|
|
|
|
<< "Test source:" << Test;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-23 11:37:26 +00:00
|
|
|
class IndexRequestCollector : public SymbolIndex {
|
|
|
|
public:
|
2019-01-07 15:45:19 +00:00
|
|
|
bool
|
|
|
|
fuzzyFind(const FuzzyFindRequest &Req,
|
|
|
|
llvm::function_ref<void(const Symbol &)> Callback) const override {
|
2018-09-06 11:04:56 +00:00
|
|
|
std::lock_guard<std::mutex> Lock(Mut);
|
2018-01-23 11:37:26 +00:00
|
|
|
Requests.push_back(Req);
|
2018-02-19 13:04:41 +00:00
|
|
|
return true;
|
2018-01-23 11:37:26 +00:00
|
|
|
}
|
|
|
|
|
2018-03-14 09:48:05 +00:00
|
|
|
void lookup(const LookupRequest &,
|
2019-01-07 15:45:19 +00:00
|
|
|
llvm::function_ref<void(const Symbol &)>) const override {}
|
2018-03-14 09:48:05 +00:00
|
|
|
|
[clangd] SymbolOccurrences -> Refs and cleanup
Summary:
A few things that I noticed while merging the SwapIndex patch:
- SymbolOccurrences and particularly SymbolOccurrenceSlab are unwieldy names,
and these names appear *a lot*. Ref, RefSlab, etc seem clear enough
and read/format much better.
- The asymmetry between SymbolSlab and RefSlab (build() vs freeze()) is
confusing and irritating, and doesn't even save much code.
Avoiding RefSlab::Builder was my idea, but it was a bad one; add it.
- DenseMap<SymbolID, ArrayRef<Ref>> seems like a reasonable compromise for
constructing MemIndex - and means many less wasted allocations than the
current DenseMap<SymbolID, vector<Ref*>> for FileIndex, and none for
slabs.
- RefSlab::find() is not actually used for anything, so we can throw
away the DenseMap and keep the representation much more compact.
- A few naming/consistency fixes: e.g. Slabs,Refs -> Symbols,Refs.
Reviewers: ioeric
Subscribers: ilya-biryukov, MaskRay, jkorous, mgrang, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51605
llvm-svn: 341368
2018-09-04 14:39:56 +00:00
|
|
|
void refs(const RefsRequest &,
|
2019-01-07 15:45:19 +00:00
|
|
|
llvm::function_ref<void(const Ref &)>) const override {}
|
2018-08-06 13:14:32 +00:00
|
|
|
|
2018-08-24 09:12:54 +00:00
|
|
|
// This is incorrect, but IndexRequestCollector is not an actual index and it
|
|
|
|
// isn't used in production code.
|
|
|
|
size_t estimateMemoryUsage() const override { return 0; }
|
|
|
|
|
[clangd] Speculative code completion index request before Sema is run.
Summary:
For index-based code completion, send an asynchronous speculative index
request, based on the index request for the last code completion on the same
file and the filter text typed before the cursor, before sema code completion
is invoked. This can reduce the code completion latency (by roughly latency of
sema code completion) if the speculative request is the same as the one
generated for the ongoing code completion from sema. As a sequence of code
completions often have the same scopes and proximity paths etc, this should be
effective for a number of code completions.
Trace with speculative index request:{F6997544}
Reviewers: hokein, ilya-biryukov
Reviewed By: ilya-biryukov
Subscribers: javed.absar, jfb, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D50962
llvm-svn: 340604
2018-08-24 11:23:56 +00:00
|
|
|
const std::vector<FuzzyFindRequest> consumeRequests() const {
|
2018-09-06 11:04:56 +00:00
|
|
|
std::lock_guard<std::mutex> Lock(Mut);
|
[clangd] Speculative code completion index request before Sema is run.
Summary:
For index-based code completion, send an asynchronous speculative index
request, based on the index request for the last code completion on the same
file and the filter text typed before the cursor, before sema code completion
is invoked. This can reduce the code completion latency (by roughly latency of
sema code completion) if the speculative request is the same as the one
generated for the ongoing code completion from sema. As a sequence of code
completions often have the same scopes and proximity paths etc, this should be
effective for a number of code completions.
Trace with speculative index request:{F6997544}
Reviewers: hokein, ilya-biryukov
Reviewed By: ilya-biryukov
Subscribers: javed.absar, jfb, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D50962
llvm-svn: 340604
2018-08-24 11:23:56 +00:00
|
|
|
auto Reqs = std::move(Requests);
|
|
|
|
Requests = {};
|
|
|
|
return Reqs;
|
|
|
|
}
|
2018-01-23 11:37:26 +00:00
|
|
|
|
|
|
|
private:
|
2018-09-06 11:04:56 +00:00
|
|
|
// We need a mutex to handle async fuzzy find requests.
|
|
|
|
mutable std::mutex Mut;
|
2018-01-23 11:37:26 +00:00
|
|
|
mutable std::vector<FuzzyFindRequest> Requests;
|
|
|
|
};
|
|
|
|
|
2019-01-07 15:45:19 +00:00
|
|
|
std::vector<FuzzyFindRequest> captureIndexRequests(llvm::StringRef Code) {
|
2018-01-23 11:37:26 +00:00
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
IndexRequestCollector Requests;
|
|
|
|
Opts.Index = &Requests;
|
|
|
|
completions(Code, {}, Opts);
|
[clangd] Speculative code completion index request before Sema is run.
Summary:
For index-based code completion, send an asynchronous speculative index
request, based on the index request for the last code completion on the same
file and the filter text typed before the cursor, before sema code completion
is invoked. This can reduce the code completion latency (by roughly latency of
sema code completion) if the speculative request is the same as the one
generated for the ongoing code completion from sema. As a sequence of code
completions often have the same scopes and proximity paths etc, this should be
effective for a number of code completions.
Trace with speculative index request:{F6997544}
Reviewers: hokein, ilya-biryukov
Reviewed By: ilya-biryukov
Subscribers: javed.absar, jfb, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D50962
llvm-svn: 340604
2018-08-24 11:23:56 +00:00
|
|
|
return Requests.consumeRequests();
|
2018-01-23 11:37:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, UnqualifiedIdQuery) {
|
|
|
|
auto Requests = captureIndexRequests(R"cpp(
|
|
|
|
namespace std {}
|
|
|
|
using namespace std;
|
|
|
|
namespace ns {
|
|
|
|
void f() {
|
|
|
|
vec^
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
|
|
|
|
EXPECT_THAT(Requests,
|
|
|
|
ElementsAre(Field(&FuzzyFindRequest::Scopes,
|
|
|
|
UnorderedElementsAre("", "ns::", "std::"))));
|
|
|
|
}
|
|
|
|
|
2018-10-17 11:19:02 +00:00
|
|
|
TEST(CompletionTest, EnclosingScopeComesFirst) {
|
|
|
|
auto Requests = captureIndexRequests(R"cpp(
|
|
|
|
namespace std {}
|
|
|
|
using namespace std;
|
|
|
|
namespace nx {
|
|
|
|
namespace ns {
|
|
|
|
namespace {
|
|
|
|
void f() {
|
|
|
|
vec^
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
|
|
|
|
EXPECT_THAT(Requests,
|
|
|
|
ElementsAre(Field(
|
|
|
|
&FuzzyFindRequest::Scopes,
|
|
|
|
UnorderedElementsAre("", "std::", "nx::ns::", "nx::"))));
|
|
|
|
EXPECT_EQ(Requests[0].Scopes[0], "nx::ns::");
|
|
|
|
}
|
|
|
|
|
2018-01-23 11:37:26 +00:00
|
|
|
TEST(CompletionTest, ResolvedQualifiedIdQuery) {
|
|
|
|
auto Requests = captureIndexRequests(R"cpp(
|
|
|
|
namespace ns1 {}
|
|
|
|
namespace ns2 {} // ignore
|
|
|
|
namespace ns3 { namespace nns3 {} }
|
|
|
|
namespace foo {
|
|
|
|
using namespace ns1;
|
|
|
|
using namespace ns3::nns3;
|
|
|
|
}
|
|
|
|
namespace ns {
|
|
|
|
void f() {
|
|
|
|
foo::^
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
|
|
|
|
EXPECT_THAT(Requests,
|
|
|
|
ElementsAre(Field(
|
|
|
|
&FuzzyFindRequest::Scopes,
|
|
|
|
UnorderedElementsAre("foo::", "ns1::", "ns3::nns3::"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, UnresolvedQualifierIdQuery) {
|
|
|
|
auto Requests = captureIndexRequests(R"cpp(
|
|
|
|
namespace a {}
|
|
|
|
using namespace a;
|
|
|
|
namespace ns {
|
|
|
|
void f() {
|
|
|
|
bar::^
|
|
|
|
}
|
|
|
|
} // namespace ns
|
|
|
|
)cpp");
|
|
|
|
|
|
|
|
EXPECT_THAT(Requests, ElementsAre(Field(&FuzzyFindRequest::Scopes,
|
|
|
|
UnorderedElementsAre("bar::"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, UnresolvedNestedQualifierIdQuery) {
|
|
|
|
auto Requests = captureIndexRequests(R"cpp(
|
|
|
|
namespace a {}
|
|
|
|
using namespace a;
|
|
|
|
namespace ns {
|
|
|
|
void f() {
|
|
|
|
::a::bar::^
|
|
|
|
}
|
|
|
|
} // namespace ns
|
|
|
|
)cpp");
|
|
|
|
|
|
|
|
EXPECT_THAT(Requests, ElementsAre(Field(&FuzzyFindRequest::Scopes,
|
|
|
|
UnorderedElementsAre("a::bar::"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, EmptyQualifiedQuery) {
|
|
|
|
auto Requests = captureIndexRequests(R"cpp(
|
|
|
|
namespace ns {
|
|
|
|
void f() {
|
|
|
|
^
|
|
|
|
}
|
|
|
|
} // namespace ns
|
|
|
|
)cpp");
|
|
|
|
|
|
|
|
EXPECT_THAT(Requests, ElementsAre(Field(&FuzzyFindRequest::Scopes,
|
|
|
|
UnorderedElementsAre("", "ns::"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, GlobalQualifiedQuery) {
|
|
|
|
auto Requests = captureIndexRequests(R"cpp(
|
|
|
|
namespace ns {
|
|
|
|
void f() {
|
|
|
|
::^
|
|
|
|
}
|
|
|
|
} // namespace ns
|
|
|
|
)cpp");
|
|
|
|
|
|
|
|
EXPECT_THAT(Requests, ElementsAre(Field(&FuzzyFindRequest::Scopes,
|
|
|
|
UnorderedElementsAre(""))));
|
|
|
|
}
|
|
|
|
|
2018-11-06 11:17:40 +00:00
|
|
|
TEST(CompletionTest, NoDuplicatedQueryScopes) {
|
|
|
|
auto Requests = captureIndexRequests(R"cpp(
|
|
|
|
namespace {}
|
|
|
|
|
|
|
|
namespace na {
|
|
|
|
namespace {}
|
|
|
|
namespace nb {
|
|
|
|
^
|
|
|
|
} // namespace nb
|
|
|
|
} // namespace na
|
|
|
|
)cpp");
|
|
|
|
|
|
|
|
EXPECT_THAT(Requests,
|
|
|
|
ElementsAre(Field(&FuzzyFindRequest::Scopes,
|
|
|
|
UnorderedElementsAre("na::", "na::nb::", ""))));
|
|
|
|
}
|
|
|
|
|
2018-05-14 10:50:04 +00:00
|
|
|
TEST(CompletionTest, NoIndexCompletionsInsideClasses) {
|
|
|
|
auto Completions = completions(
|
|
|
|
R"cpp(
|
|
|
|
struct Foo {
|
|
|
|
int SomeNameOfField;
|
|
|
|
typedef int SomeNameOfTypedefField;
|
|
|
|
};
|
|
|
|
|
|
|
|
Foo::^)cpp",
|
|
|
|
{func("::SomeNameInTheIndex"), func("::Foo::SomeNameInTheIndex")});
|
|
|
|
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Completions.Completions,
|
2018-05-14 10:50:04 +00:00
|
|
|
AllOf(Contains(Labeled("SomeNameOfField")),
|
|
|
|
Contains(Labeled("SomeNameOfTypedefField")),
|
|
|
|
Not(Contains(Labeled("SomeNameInTheIndex")))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, NoIndexCompletionsInsideDependentCode) {
|
|
|
|
{
|
|
|
|
auto Completions = completions(
|
|
|
|
R"cpp(
|
|
|
|
template <class T>
|
|
|
|
void foo() {
|
|
|
|
T::^
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
{func("::SomeNameInTheIndex")});
|
|
|
|
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Completions.Completions,
|
2018-05-14 10:50:04 +00:00
|
|
|
Not(Contains(Labeled("SomeNameInTheIndex"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
auto Completions = completions(
|
|
|
|
R"cpp(
|
|
|
|
template <class T>
|
|
|
|
void foo() {
|
|
|
|
T::template Y<int>::^
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
{func("::SomeNameInTheIndex")});
|
|
|
|
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Completions.Completions,
|
2018-05-14 10:50:04 +00:00
|
|
|
Not(Contains(Labeled("SomeNameInTheIndex"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
auto Completions = completions(
|
|
|
|
R"cpp(
|
|
|
|
template <class T>
|
|
|
|
void foo() {
|
|
|
|
T::foo::^
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
{func("::SomeNameInTheIndex")});
|
|
|
|
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Completions.Completions,
|
2018-05-14 10:50:04 +00:00
|
|
|
Not(Contains(Labeled("SomeNameInTheIndex"))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[clangd] Add option to fold overloads into a single completion item.
Summary:
Adds a CodeCompleteOption to folds together compatible function/method overloads
into a single item. This feels pretty good (for editors with signatureHelp
support), but has limitations.
This happens in the code completion merge step, so there may be inconsistencies
(e.g. if only one overload made it into the index result list, no folding).
We don't want to bundle together completions that have different side-effects
(include insertion), because we can't constructo a coherent CompletionItem.
This may be confusing for users, as the reason for non-bundling may not
be immediately obvious. (Also, the implementation seems a little fragile)
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits
Differential Revision: https://reviews.llvm.org/D47957
llvm-svn: 334822
2018-06-15 11:06:29 +00:00
|
|
|
TEST(CompletionTest, OverloadBundling) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.BundleOverloads = true;
|
|
|
|
|
|
|
|
std::string Context = R"cpp(
|
|
|
|
struct X {
|
|
|
|
// Overload with int
|
|
|
|
int a(int);
|
|
|
|
// Overload with bool
|
|
|
|
int a(bool);
|
|
|
|
int b(float);
|
|
|
|
};
|
|
|
|
int GFuncC(int);
|
|
|
|
int GFuncD(int);
|
|
|
|
)cpp";
|
|
|
|
|
|
|
|
// Member completions are bundled.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(completions(Context + "int y = X().^", {}, Opts).Completions,
|
[clangd] Add option to fold overloads into a single completion item.
Summary:
Adds a CodeCompleteOption to folds together compatible function/method overloads
into a single item. This feels pretty good (for editors with signatureHelp
support), but has limitations.
This happens in the code completion merge step, so there may be inconsistencies
(e.g. if only one overload made it into the index result list, no folding).
We don't want to bundle together completions that have different side-effects
(include insertion), because we can't constructo a coherent CompletionItem.
This may be confusing for users, as the reason for non-bundling may not
be immediately obvious. (Also, the implementation seems a little fragile)
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits
Differential Revision: https://reviews.llvm.org/D47957
llvm-svn: 334822
2018-06-15 11:06:29 +00:00
|
|
|
UnorderedElementsAre(Labeled("a(…)"), Labeled("b(float)")));
|
|
|
|
|
|
|
|
// Non-member completions are bundled, including index+sema.
|
|
|
|
Symbol NoArgsGFunc = func("GFuncC");
|
|
|
|
EXPECT_THAT(
|
2018-07-02 11:13:16 +00:00
|
|
|
completions(Context + "int y = GFunc^", {NoArgsGFunc}, Opts).Completions,
|
[clangd] Add option to fold overloads into a single completion item.
Summary:
Adds a CodeCompleteOption to folds together compatible function/method overloads
into a single item. This feels pretty good (for editors with signatureHelp
support), but has limitations.
This happens in the code completion merge step, so there may be inconsistencies
(e.g. if only one overload made it into the index result list, no folding).
We don't want to bundle together completions that have different side-effects
(include insertion), because we can't constructo a coherent CompletionItem.
This may be confusing for users, as the reason for non-bundling may not
be immediately obvious. (Also, the implementation seems a little fragile)
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits
Differential Revision: https://reviews.llvm.org/D47957
llvm-svn: 334822
2018-06-15 11:06:29 +00:00
|
|
|
UnorderedElementsAre(Labeled("GFuncC(…)"), Labeled("GFuncD(int)")));
|
|
|
|
|
|
|
|
// Differences in header-to-insert suppress bundling.
|
[clangd] Cleanup: stop passing around list of supported URI schemes.
Summary:
Instead of passing around a list of supported URI schemes in clangd, we
expose an interface to convert a path to URI using any compatible scheme
that has been registered. It favors customized schemes and falls
back to "file" when no other scheme works.
Changes in this patch are:
- URI::create(AbsPath, URISchemes) -> URI::create(AbsPath). The new API finds a
compatible scheme from the registry.
- Remove URISchemes option everywhere (ClangdServer, SymbolCollecter, FileIndex etc).
- Unit tests will use "unittest" by default.
- Move "test" scheme from ClangdLSPServer to ClangdMain.cpp, and only
register the test scheme when lit-test or enable-lit-scheme is set.
(The new flag is added to make lit protocol.test work; I wonder if there
is alternative here.)
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D54800
llvm-svn: 347467
2018-11-22 15:02:05 +00:00
|
|
|
std::string DeclFile = URI::create(testPath("foo")).toString();
|
2018-11-14 11:55:45 +00:00
|
|
|
NoArgsGFunc.CanonicalDeclaration.FileURI = DeclFile.c_str();
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
NoArgsGFunc.IncludeHeaders.emplace_back("<foo>", 1);
|
[clangd] Add option to fold overloads into a single completion item.
Summary:
Adds a CodeCompleteOption to folds together compatible function/method overloads
into a single item. This feels pretty good (for editors with signatureHelp
support), but has limitations.
This happens in the code completion merge step, so there may be inconsistencies
(e.g. if only one overload made it into the index result list, no folding).
We don't want to bundle together completions that have different side-effects
(include insertion), because we can't constructo a coherent CompletionItem.
This may be confusing for users, as the reason for non-bundling may not
be immediately obvious. (Also, the implementation seems a little fragile)
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits
Differential Revision: https://reviews.llvm.org/D47957
llvm-svn: 334822
2018-06-15 11:06:29 +00:00
|
|
|
EXPECT_THAT(
|
2018-07-02 11:13:16 +00:00
|
|
|
completions(Context + "int y = GFunc^", {NoArgsGFunc}, Opts).Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Named("GFuncC"), InsertInclude("<foo>")),
|
|
|
|
Labeled("GFuncC(int)"), Labeled("GFuncD(int)")));
|
[clangd] Add option to fold overloads into a single completion item.
Summary:
Adds a CodeCompleteOption to folds together compatible function/method overloads
into a single item. This feels pretty good (for editors with signatureHelp
support), but has limitations.
This happens in the code completion merge step, so there may be inconsistencies
(e.g. if only one overload made it into the index result list, no folding).
We don't want to bundle together completions that have different side-effects
(include insertion), because we can't constructo a coherent CompletionItem.
This may be confusing for users, as the reason for non-bundling may not
be immediately obvious. (Also, the implementation seems a little fragile)
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits
Differential Revision: https://reviews.llvm.org/D47957
llvm-svn: 334822
2018-06-15 11:06:29 +00:00
|
|
|
|
|
|
|
// Examine a bundled completion in detail.
|
2018-07-02 11:13:16 +00:00
|
|
|
auto A =
|
|
|
|
completions(Context + "int y = X().a^", {}, Opts).Completions.front();
|
|
|
|
EXPECT_EQ(A.Name, "a");
|
|
|
|
EXPECT_EQ(A.Signature, "(…)");
|
|
|
|
EXPECT_EQ(A.BundleSize, 2u);
|
|
|
|
EXPECT_EQ(A.Kind, CompletionItemKind::Method);
|
|
|
|
EXPECT_EQ(A.ReturnType, "int"); // All overloads return int.
|
[clangd] Add option to fold overloads into a single completion item.
Summary:
Adds a CodeCompleteOption to folds together compatible function/method overloads
into a single item. This feels pretty good (for editors with signatureHelp
support), but has limitations.
This happens in the code completion merge step, so there may be inconsistencies
(e.g. if only one overload made it into the index result list, no folding).
We don't want to bundle together completions that have different side-effects
(include insertion), because we can't constructo a coherent CompletionItem.
This may be confusing for users, as the reason for non-bundling may not
be immediately obvious. (Also, the implementation seems a little fragile)
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits
Differential Revision: https://reviews.llvm.org/D47957
llvm-svn: 334822
2018-06-15 11:06:29 +00:00
|
|
|
// For now we just return one of the doc strings arbitrarily.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(A.Documentation, AnyOf(HasSubstr("Overload with int"),
|
[clangd] Add option to fold overloads into a single completion item.
Summary:
Adds a CodeCompleteOption to folds together compatible function/method overloads
into a single item. This feels pretty good (for editors with signatureHelp
support), but has limitations.
This happens in the code completion merge step, so there may be inconsistencies
(e.g. if only one overload made it into the index result list, no folding).
We don't want to bundle together completions that have different side-effects
(include insertion), because we can't constructo a coherent CompletionItem.
This may be confusing for users, as the reason for non-bundling may not
be immediately obvious. (Also, the implementation seems a little fragile)
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits
Differential Revision: https://reviews.llvm.org/D47957
llvm-svn: 334822
2018-06-15 11:06:29 +00:00
|
|
|
HasSubstr("Overload with bool")));
|
2018-08-23 12:19:39 +00:00
|
|
|
EXPECT_EQ(A.SnippetSuffix, "($0)");
|
[clangd] Add option to fold overloads into a single completion item.
Summary:
Adds a CodeCompleteOption to folds together compatible function/method overloads
into a single item. This feels pretty good (for editors with signatureHelp
support), but has limitations.
This happens in the code completion merge step, so there may be inconsistencies
(e.g. if only one overload made it into the index result list, no folding).
We don't want to bundle together completions that have different side-effects
(include insertion), because we can't constructo a coherent CompletionItem.
This may be confusing for users, as the reason for non-bundling may not
be immediately obvious. (Also, the implementation seems a little fragile)
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits
Differential Revision: https://reviews.llvm.org/D47957
llvm-svn: 334822
2018-06-15 11:06:29 +00:00
|
|
|
}
|
|
|
|
|
2018-05-28 09:54:51 +00:00
|
|
|
TEST(CompletionTest, DocumentationFromChangedFileCrash) {
|
2018-05-24 14:49:23 +00:00
|
|
|
MockFSProvider FS;
|
|
|
|
auto FooH = testPath("foo.h");
|
|
|
|
auto FooCpp = testPath("foo.cpp");
|
|
|
|
FS.Files[FooH] = R"cpp(
|
|
|
|
// this is my documentation comment.
|
|
|
|
int func();
|
|
|
|
)cpp";
|
|
|
|
FS.Files[FooCpp] = "";
|
|
|
|
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
|
|
|
|
Annotations Source(R"cpp(
|
|
|
|
#include "foo.h"
|
|
|
|
int func() {
|
|
|
|
// This makes sure we have func from header in the AST.
|
|
|
|
}
|
|
|
|
int a = fun^
|
|
|
|
)cpp");
|
|
|
|
Server.addDocument(FooCpp, Source.code(), WantDiagnostics::Yes);
|
|
|
|
// We need to wait for preamble to build.
|
|
|
|
ASSERT_TRUE(Server.blockUntilIdleForTest());
|
|
|
|
|
|
|
|
// Change the header file. Completion will reuse the old preamble!
|
|
|
|
FS.Files[FooH] = R"cpp(
|
|
|
|
int func();
|
|
|
|
)cpp";
|
|
|
|
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.IncludeComments = true;
|
2018-07-02 11:13:16 +00:00
|
|
|
CodeCompleteResult Completions =
|
2018-05-24 14:49:23 +00:00
|
|
|
cantFail(runCodeComplete(Server, FooCpp, Source.point(), Opts));
|
|
|
|
// We shouldn't crash. Unfortunately, current workaround is to not produce
|
|
|
|
// comments for symbols from headers.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Completions.Completions,
|
2018-05-24 14:49:23 +00:00
|
|
|
Contains(AllOf(Not(IsDocumented()), Named("func"))));
|
|
|
|
}
|
|
|
|
|
2018-06-15 08:31:17 +00:00
|
|
|
TEST(CompletionTest, NonDocComments) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
auto FooCpp = testPath("foo.cpp");
|
|
|
|
FS.Files[FooCpp] = "";
|
|
|
|
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
|
|
|
|
Annotations Source(R"cpp(
|
2018-06-27 09:47:20 +00:00
|
|
|
// We ignore namespace comments, for rationale see CodeCompletionStrings.h.
|
|
|
|
namespace comments_ns {
|
|
|
|
}
|
|
|
|
|
2018-06-15 08:31:17 +00:00
|
|
|
// ------------------
|
|
|
|
int comments_foo();
|
|
|
|
|
|
|
|
// A comment and a decl are separated by newlines.
|
|
|
|
// Therefore, the comment shouldn't show up as doc comment.
|
|
|
|
|
|
|
|
int comments_bar();
|
|
|
|
|
|
|
|
// this comment should be in the results.
|
|
|
|
int comments_baz();
|
|
|
|
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
struct Struct {
|
|
|
|
int comments_qux();
|
|
|
|
int comments_quux();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// This comment should not be there.
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
int Struct<T>::comments_qux() {
|
|
|
|
}
|
|
|
|
|
|
|
|
// This comment **should** be in results.
|
|
|
|
template <class T>
|
|
|
|
int Struct<T>::comments_quux() {
|
|
|
|
int a = comments^;
|
|
|
|
}
|
|
|
|
)cpp");
|
2018-06-18 18:55:10 +00:00
|
|
|
// FIXME: Auto-completion in a template requires disabling delayed template
|
|
|
|
// parsing.
|
|
|
|
CDB.ExtraClangFlags.push_back("-fno-delayed-template-parsing");
|
2018-06-15 08:31:17 +00:00
|
|
|
Server.addDocument(FooCpp, Source.code(), WantDiagnostics::Yes);
|
2018-07-02 11:13:16 +00:00
|
|
|
CodeCompleteResult Completions = cantFail(runCodeComplete(
|
2018-06-15 08:31:17 +00:00
|
|
|
Server, FooCpp, Source.point(), clangd::CodeCompleteOptions()));
|
|
|
|
|
|
|
|
// We should not get any of those comments in completion.
|
|
|
|
EXPECT_THAT(
|
2018-07-02 11:13:16 +00:00
|
|
|
Completions.Completions,
|
2018-06-15 08:31:17 +00:00
|
|
|
UnorderedElementsAre(AllOf(Not(IsDocumented()), Named("comments_foo")),
|
|
|
|
AllOf(IsDocumented(), Named("comments_baz")),
|
|
|
|
AllOf(IsDocumented(), Named("comments_quux")),
|
2018-06-27 09:47:20 +00:00
|
|
|
AllOf(Not(IsDocumented()), Named("comments_ns")),
|
2018-06-15 08:31:17 +00:00
|
|
|
// FIXME(ibiryukov): the following items should have
|
|
|
|
// empty documentation, since they are separated from
|
|
|
|
// a comment with an empty line. Unfortunately, I
|
|
|
|
// couldn't make Sema tests pass if we ignore those.
|
|
|
|
AllOf(IsDocumented(), Named("comments_bar")),
|
|
|
|
AllOf(IsDocumented(), Named("comments_qux"))));
|
|
|
|
}
|
|
|
|
|
2018-05-28 12:11:37 +00:00
|
|
|
TEST(CompletionTest, CompleteOnInvalidLine) {
|
|
|
|
auto FooCpp = testPath("foo.cpp");
|
|
|
|
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
MockFSProvider FS;
|
|
|
|
FS.Files[FooCpp] = "// empty file";
|
|
|
|
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
// Run completion outside the file range.
|
|
|
|
Position Pos;
|
|
|
|
Pos.line = 100;
|
|
|
|
Pos.character = 0;
|
|
|
|
EXPECT_THAT_EXPECTED(
|
|
|
|
runCodeComplete(Server, FooCpp, Pos, clangd::CodeCompleteOptions()),
|
|
|
|
Failed());
|
|
|
|
}
|
|
|
|
|
2018-06-22 10:46:59 +00:00
|
|
|
TEST(CompletionTest, QualifiedNames) {
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
namespace ns { int local; void both(); }
|
|
|
|
void f() { ::ns::^ }
|
|
|
|
)cpp",
|
|
|
|
{func("ns::both"), cls("ns::Index")});
|
|
|
|
// We get results from both index and sema, with no duplicates.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
UnorderedElementsAre(Scope("ns::"), Scope("ns::"), Scope("ns::")));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, Render) {
|
|
|
|
CodeCompletion C;
|
|
|
|
C.Name = "x";
|
|
|
|
C.Signature = "(bool) const";
|
|
|
|
C.SnippetSuffix = "(${0:bool})";
|
|
|
|
C.ReturnType = "int";
|
|
|
|
C.RequiredQualifier = "Foo::";
|
|
|
|
C.Scope = "ns::Foo::";
|
|
|
|
C.Documentation = "This is x().";
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
C.Includes.emplace_back();
|
|
|
|
auto &Include = C.Includes.back();
|
|
|
|
Include.Header = "\"foo.h\"";
|
2018-07-02 11:13:16 +00:00
|
|
|
C.Kind = CompletionItemKind::Method;
|
|
|
|
C.Score.Total = 1.0;
|
2018-07-06 11:50:49 +00:00
|
|
|
C.Origin = SymbolOrigin::AST | SymbolOrigin::Static;
|
2018-07-02 11:13:16 +00:00
|
|
|
|
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.IncludeIndicator.Insert = "^";
|
|
|
|
Opts.IncludeIndicator.NoInsert = "";
|
|
|
|
Opts.EnableSnippets = false;
|
|
|
|
|
|
|
|
auto R = C.render(Opts);
|
|
|
|
EXPECT_EQ(R.label, "Foo::x(bool) const");
|
|
|
|
EXPECT_EQ(R.insertText, "Foo::x");
|
|
|
|
EXPECT_EQ(R.insertTextFormat, InsertTextFormat::PlainText);
|
|
|
|
EXPECT_EQ(R.filterText, "x");
|
|
|
|
EXPECT_EQ(R.detail, "int\n\"foo.h\"");
|
|
|
|
EXPECT_EQ(R.documentation, "This is x().");
|
|
|
|
EXPECT_THAT(R.additionalTextEdits, IsEmpty());
|
|
|
|
EXPECT_EQ(R.sortText, sortText(1.0, "x"));
|
2018-09-06 18:52:26 +00:00
|
|
|
EXPECT_FALSE(R.deprecated);
|
2018-07-02 11:13:16 +00:00
|
|
|
|
|
|
|
Opts.EnableSnippets = true;
|
|
|
|
R = C.render(Opts);
|
|
|
|
EXPECT_EQ(R.insertText, "Foo::x(${0:bool})");
|
|
|
|
EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet);
|
|
|
|
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
Include.Insertion.emplace();
|
2018-07-02 11:13:16 +00:00
|
|
|
R = C.render(Opts);
|
|
|
|
EXPECT_EQ(R.label, "^Foo::x(bool) const");
|
|
|
|
EXPECT_THAT(R.additionalTextEdits, Not(IsEmpty()));
|
|
|
|
|
2018-07-05 06:20:41 +00:00
|
|
|
Opts.ShowOrigins = true;
|
|
|
|
R = C.render(Opts);
|
|
|
|
EXPECT_EQ(R.label, "^[AS]Foo::x(bool) const");
|
|
|
|
|
2018-07-02 11:13:16 +00:00
|
|
|
C.BundleSize = 2;
|
|
|
|
R = C.render(Opts);
|
|
|
|
EXPECT_EQ(R.detail, "[2 overloads]\n\"foo.h\"");
|
2018-09-06 18:52:26 +00:00
|
|
|
|
|
|
|
C.Deprecated = true;
|
|
|
|
R = C.render(Opts);
|
|
|
|
EXPECT_TRUE(R.deprecated);
|
2018-06-22 10:46:59 +00:00
|
|
|
}
|
2018-05-28 12:11:37 +00:00
|
|
|
|
2018-07-11 13:15:31 +00:00
|
|
|
TEST(CompletionTest, IgnoreRecoveryResults) {
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
namespace ns { int NotRecovered() { return 0; } }
|
|
|
|
void f() {
|
|
|
|
// Sema enters recovery mode first and then normal mode.
|
|
|
|
if (auto x = ns::NotRecover^)
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(Results.Completions, UnorderedElementsAre(Named("NotRecovered")));
|
|
|
|
}
|
|
|
|
|
2018-07-18 15:31:14 +00:00
|
|
|
TEST(CompletionTest, ScopeOfClassFieldInConstructorInitializer) {
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
namespace ns {
|
|
|
|
class X { public: X(); int x_; };
|
|
|
|
X::X() : x_^(0) {}
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Scope("ns::X::"), Named("x_"))));
|
|
|
|
}
|
|
|
|
|
2018-07-23 10:56:37 +00:00
|
|
|
TEST(CompletionTest, CodeCompletionContext) {
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
namespace ns {
|
|
|
|
class X { public: X(); int x_; };
|
|
|
|
void f() {
|
|
|
|
X x;
|
|
|
|
x.^;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
|
|
|
|
EXPECT_THAT(Results.Context, CodeCompletionContext::CCC_DotMemberAccess);
|
|
|
|
}
|
|
|
|
|
2018-08-08 08:59:29 +00:00
|
|
|
TEST(CompletionTest, FixItForArrowToDot) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
|
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.IncludeFixIts = true;
|
|
|
|
Annotations TestCode(
|
|
|
|
R"cpp(
|
|
|
|
class Auxilary {
|
|
|
|
public:
|
|
|
|
void AuxFunction();
|
|
|
|
};
|
|
|
|
class ClassWithPtr {
|
|
|
|
public:
|
|
|
|
void MemberFunction();
|
|
|
|
Auxilary* operator->() const;
|
|
|
|
Auxilary* Aux;
|
|
|
|
};
|
|
|
|
void f() {
|
|
|
|
ClassWithPtr x;
|
|
|
|
x[[->]]^;
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
auto Results =
|
|
|
|
completions(Server, TestCode.code(), TestCode.point(), {}, Opts);
|
|
|
|
EXPECT_EQ(Results.Completions.size(), 3u);
|
|
|
|
|
|
|
|
TextEdit ReplacementEdit;
|
|
|
|
ReplacementEdit.range = TestCode.range();
|
|
|
|
ReplacementEdit.newText = ".";
|
|
|
|
for (const auto &C : Results.Completions) {
|
|
|
|
EXPECT_TRUE(C.FixIts.size() == 1u || C.Name == "AuxFunction");
|
2018-08-10 08:34:16 +00:00
|
|
|
if (!C.FixIts.empty()) {
|
2018-08-08 08:59:29 +00:00
|
|
|
EXPECT_THAT(C.FixIts, ElementsAre(ReplacementEdit));
|
2018-08-10 08:34:16 +00:00
|
|
|
}
|
2018-08-08 08:59:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, FixItForDotToArrow) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
|
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.IncludeFixIts = true;
|
|
|
|
Annotations TestCode(
|
|
|
|
R"cpp(
|
|
|
|
class Auxilary {
|
|
|
|
public:
|
|
|
|
void AuxFunction();
|
|
|
|
};
|
|
|
|
class ClassWithPtr {
|
|
|
|
public:
|
|
|
|
void MemberFunction();
|
|
|
|
Auxilary* operator->() const;
|
|
|
|
Auxilary* Aux;
|
|
|
|
};
|
|
|
|
void f() {
|
|
|
|
ClassWithPtr x;
|
|
|
|
x[[.]]^;
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
auto Results =
|
|
|
|
completions(Server, TestCode.code(), TestCode.point(), {}, Opts);
|
|
|
|
EXPECT_EQ(Results.Completions.size(), 3u);
|
|
|
|
|
|
|
|
TextEdit ReplacementEdit;
|
|
|
|
ReplacementEdit.range = TestCode.range();
|
|
|
|
ReplacementEdit.newText = "->";
|
|
|
|
for (const auto &C : Results.Completions) {
|
|
|
|
EXPECT_TRUE(C.FixIts.empty() || C.Name == "AuxFunction");
|
|
|
|
if (!C.FixIts.empty()) {
|
|
|
|
EXPECT_THAT(C.FixIts, ElementsAre(ReplacementEdit));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-13 08:23:01 +00:00
|
|
|
TEST(CompletionTest, RenderWithFixItMerged) {
|
|
|
|
TextEdit FixIt;
|
|
|
|
FixIt.range.end.character = 5;
|
|
|
|
FixIt.newText = "->";
|
|
|
|
|
|
|
|
CodeCompletion C;
|
|
|
|
C.Name = "x";
|
|
|
|
C.RequiredQualifier = "Foo::";
|
|
|
|
C.FixIts = {FixIt};
|
|
|
|
C.CompletionTokenRange.start.character = 5;
|
|
|
|
|
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.IncludeFixIts = true;
|
|
|
|
|
|
|
|
auto R = C.render(Opts);
|
|
|
|
EXPECT_TRUE(R.textEdit);
|
|
|
|
EXPECT_EQ(R.textEdit->newText, "->Foo::x");
|
|
|
|
EXPECT_TRUE(R.additionalTextEdits.empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, RenderWithFixItNonMerged) {
|
|
|
|
TextEdit FixIt;
|
|
|
|
FixIt.range.end.character = 4;
|
|
|
|
FixIt.newText = "->";
|
|
|
|
|
|
|
|
CodeCompletion C;
|
|
|
|
C.Name = "x";
|
|
|
|
C.RequiredQualifier = "Foo::";
|
|
|
|
C.FixIts = {FixIt};
|
|
|
|
C.CompletionTokenRange.start.character = 5;
|
|
|
|
|
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.IncludeFixIts = true;
|
|
|
|
|
|
|
|
auto R = C.render(Opts);
|
|
|
|
EXPECT_TRUE(R.textEdit);
|
|
|
|
EXPECT_EQ(R.textEdit->newText, "Foo::x");
|
|
|
|
EXPECT_THAT(R.additionalTextEdits, UnorderedElementsAre(FixIt));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, CompletionTokenRange) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
|
|
|
|
constexpr const char *TestCodes[] = {
|
|
|
|
R"cpp(
|
|
|
|
class Auxilary {
|
|
|
|
public:
|
|
|
|
void AuxFunction();
|
|
|
|
};
|
|
|
|
void f() {
|
|
|
|
Auxilary x;
|
|
|
|
x.[[Aux]]^;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
class Auxilary {
|
|
|
|
public:
|
|
|
|
void AuxFunction();
|
|
|
|
};
|
|
|
|
void f() {
|
|
|
|
Auxilary x;
|
|
|
|
x.[[]]^;
|
|
|
|
}
|
|
|
|
)cpp"};
|
|
|
|
for (const auto &Text : TestCodes) {
|
|
|
|
Annotations TestCode(Text);
|
|
|
|
auto Results = completions(Server, TestCode.code(), TestCode.point());
|
|
|
|
|
|
|
|
EXPECT_EQ(Results.Completions.size(), 1u);
|
2019-01-03 13:28:05 +00:00
|
|
|
EXPECT_THAT(Results.Completions.front().CompletionTokenRange,
|
|
|
|
TestCode.range());
|
2018-08-13 08:23:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-13 08:40:05 +00:00
|
|
|
TEST(SignatureHelpTest, OverloadsOrdering) {
|
|
|
|
const auto Results = signatures(R"cpp(
|
|
|
|
void foo(int x);
|
|
|
|
void foo(int x, float y);
|
|
|
|
void foo(float x, int y);
|
|
|
|
void foo(float x, float y);
|
|
|
|
void foo(int x, int y = 0);
|
|
|
|
int main() { foo(^); }
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.signatures,
|
|
|
|
ElementsAre(
|
|
|
|
Sig("foo(int x) -> void", {"int x"}),
|
|
|
|
Sig("foo(int x, int y = 0) -> void", {"int x", "int y = 0"}),
|
|
|
|
Sig("foo(float x, int y) -> void", {"float x", "int y"}),
|
|
|
|
Sig("foo(int x, float y) -> void", {"int x", "float y"}),
|
|
|
|
Sig("foo(float x, float y) -> void", {"float x", "float y"})));
|
|
|
|
// We always prefer the first signature.
|
|
|
|
EXPECT_EQ(0, Results.activeSignature);
|
|
|
|
EXPECT_EQ(0, Results.activeParameter);
|
|
|
|
}
|
|
|
|
|
2018-08-14 09:36:32 +00:00
|
|
|
TEST(SignatureHelpTest, InstantiatedSignatures) {
|
2018-08-16 11:41:19 +00:00
|
|
|
StringRef Sig0 = R"cpp(
|
2018-08-14 09:36:32 +00:00
|
|
|
template <class T>
|
|
|
|
void foo(T, T, T);
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
foo<int>(^);
|
|
|
|
}
|
2018-08-16 11:41:19 +00:00
|
|
|
)cpp";
|
|
|
|
|
|
|
|
EXPECT_THAT(signatures(Sig0).signatures,
|
2018-08-14 09:36:32 +00:00
|
|
|
ElementsAre(Sig("foo(T, T, T) -> void", {"T", "T", "T"})));
|
|
|
|
|
2018-08-16 11:41:19 +00:00
|
|
|
StringRef Sig1 = R"cpp(
|
2018-08-14 09:36:32 +00:00
|
|
|
template <class T>
|
|
|
|
void foo(T, T, T);
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
foo(10, ^);
|
2018-08-16 11:41:19 +00:00
|
|
|
})cpp";
|
|
|
|
|
|
|
|
EXPECT_THAT(signatures(Sig1).signatures,
|
2018-08-14 09:36:32 +00:00
|
|
|
ElementsAre(Sig("foo(T, T, T) -> void", {"T", "T", "T"})));
|
|
|
|
|
2018-08-16 11:41:19 +00:00
|
|
|
StringRef Sig2 = R"cpp(
|
2018-08-14 09:36:32 +00:00
|
|
|
template <class ...T>
|
|
|
|
void foo(T...);
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
foo<int>(^);
|
|
|
|
}
|
2018-08-16 11:41:19 +00:00
|
|
|
)cpp";
|
|
|
|
|
|
|
|
EXPECT_THAT(signatures(Sig2).signatures,
|
2018-08-14 09:36:32 +00:00
|
|
|
ElementsAre(Sig("foo(T...) -> void", {"T..."})));
|
|
|
|
|
|
|
|
// It is debatable whether we should substitute the outer template parameter
|
|
|
|
// ('T') in that case. Currently we don't substitute it in signature help, but
|
|
|
|
// do substitute in code complete.
|
|
|
|
// FIXME: make code complete and signature help consistent, figure out which
|
|
|
|
// way is better.
|
2018-08-16 11:41:19 +00:00
|
|
|
StringRef Sig3 = R"cpp(
|
2018-08-14 09:36:32 +00:00
|
|
|
template <class T>
|
|
|
|
struct X {
|
|
|
|
template <class U>
|
|
|
|
void foo(T, U);
|
|
|
|
};
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
X<int>().foo<double>(^)
|
|
|
|
}
|
2018-08-16 11:41:19 +00:00
|
|
|
)cpp";
|
|
|
|
|
|
|
|
EXPECT_THAT(signatures(Sig3).signatures,
|
2018-08-14 09:36:32 +00:00
|
|
|
ElementsAre(Sig("foo(T, U) -> void", {"T", "U"})));
|
|
|
|
}
|
|
|
|
|
2018-08-17 09:32:30 +00:00
|
|
|
TEST(SignatureHelpTest, IndexDocumentation) {
|
|
|
|
Symbol Foo0 = sym("foo", index::SymbolKind::Function, "@F@\\0#");
|
2018-08-31 13:55:01 +00:00
|
|
|
Foo0.Documentation = "Doc from the index";
|
2018-08-17 09:32:30 +00:00
|
|
|
Symbol Foo1 = sym("foo", index::SymbolKind::Function, "@F@\\0#I#");
|
2018-08-31 13:55:01 +00:00
|
|
|
Foo1.Documentation = "Doc from the index";
|
2018-08-17 09:32:30 +00:00
|
|
|
Symbol Foo2 = sym("foo", index::SymbolKind::Function, "@F@\\0#I#I#");
|
|
|
|
|
2018-08-17 10:40:05 +00:00
|
|
|
StringRef Sig0 = R"cpp(
|
2018-08-17 09:32:30 +00:00
|
|
|
int foo();
|
|
|
|
int foo(double);
|
|
|
|
|
|
|
|
void test() {
|
|
|
|
foo(^);
|
|
|
|
}
|
2018-08-17 10:40:05 +00:00
|
|
|
)cpp";
|
|
|
|
|
|
|
|
EXPECT_THAT(
|
|
|
|
signatures(Sig0, {Foo0}).signatures,
|
2018-08-17 09:32:30 +00:00
|
|
|
ElementsAre(AllOf(Sig("foo() -> int", {}), SigDoc("Doc from the index")),
|
|
|
|
AllOf(Sig("foo(double) -> int", {"double"}), SigDoc(""))));
|
|
|
|
|
2018-08-17 10:40:05 +00:00
|
|
|
StringRef Sig1 = R"cpp(
|
2018-08-17 09:32:30 +00:00
|
|
|
int foo();
|
|
|
|
// Overriden doc from sema
|
|
|
|
int foo(int);
|
|
|
|
// Doc from sema
|
|
|
|
int foo(int, int);
|
|
|
|
|
|
|
|
void test() {
|
|
|
|
foo(^);
|
|
|
|
}
|
2018-08-17 10:40:05 +00:00
|
|
|
)cpp";
|
|
|
|
|
|
|
|
EXPECT_THAT(
|
|
|
|
signatures(Sig1, {Foo0, Foo1, Foo2}).signatures,
|
2018-08-17 09:32:30 +00:00
|
|
|
ElementsAre(AllOf(Sig("foo() -> int", {}), SigDoc("Doc from the index")),
|
|
|
|
AllOf(Sig("foo(int) -> int", {"int"}),
|
|
|
|
SigDoc("Overriden doc from sema")),
|
|
|
|
AllOf(Sig("foo(int, int) -> int", {"int", "int"}),
|
|
|
|
SigDoc("Doc from sema"))));
|
|
|
|
}
|
|
|
|
|
2019-01-09 13:42:03 +00:00
|
|
|
TEST(SignatureHelpTest, DynamicIndexDocumentation) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer::Options Opts = ClangdServer::optsForTest();
|
|
|
|
Opts.BuildDynamicSymbolIndex = true;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, Opts);
|
|
|
|
|
|
|
|
FS.Files[testPath("foo.h")] = R"cpp(
|
|
|
|
struct Foo {
|
|
|
|
// Member doc
|
|
|
|
int foo();
|
|
|
|
};
|
|
|
|
)cpp";
|
|
|
|
Annotations FileContent(R"cpp(
|
|
|
|
#include "foo.h"
|
|
|
|
void test() {
|
|
|
|
Foo f;
|
|
|
|
f.foo(^);
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
auto File = testPath("test.cpp");
|
|
|
|
Server.addDocument(File, FileContent.code());
|
|
|
|
// Wait for the dynamic index being built.
|
|
|
|
ASSERT_TRUE(Server.blockUntilIdleForTest());
|
|
|
|
EXPECT_THAT(
|
|
|
|
llvm::cantFail(runSignatureHelp(Server, File, FileContent.point()))
|
|
|
|
.signatures,
|
|
|
|
ElementsAre(AllOf(Sig("foo() -> int", {}), SigDoc("Member doc"))));
|
|
|
|
}
|
|
|
|
|
2018-08-23 12:19:39 +00:00
|
|
|
TEST(CompletionTest, CompletionFunctionArgsDisabled) {
|
2018-08-17 15:42:54 +00:00
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.EnableSnippets = true;
|
|
|
|
Opts.EnableFunctionArgSnippets = false;
|
2018-09-26 05:45:31 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
2018-08-23 12:19:39 +00:00
|
|
|
void xfoo();
|
|
|
|
void xfoo(int x, int y);
|
2018-09-26 05:45:31 +00:00
|
|
|
void f() { xfo^ })cpp",
|
|
|
|
{}, Opts);
|
2018-08-23 12:19:39 +00:00
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Named("xfoo"), SnippetSuffix("()")),
|
|
|
|
AllOf(Named("xfoo"), SnippetSuffix("($0)"))));
|
2018-08-17 15:42:54 +00:00
|
|
|
}
|
|
|
|
{
|
2018-09-26 05:45:31 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
void xbar();
|
|
|
|
void f() { xba^ })cpp",
|
|
|
|
{}, Opts);
|
2018-08-23 12:19:39 +00:00
|
|
|
EXPECT_THAT(Results.Completions, UnorderedElementsAre(AllOf(
|
|
|
|
Named("xbar"), SnippetSuffix("()"))));
|
2018-08-17 15:42:54 +00:00
|
|
|
}
|
|
|
|
{
|
2018-08-23 12:19:39 +00:00
|
|
|
Opts.BundleOverloads = true;
|
2018-09-26 05:45:31 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
void xfoo();
|
|
|
|
void xfoo(int x, int y);
|
|
|
|
void f() { xfo^ })cpp",
|
|
|
|
{}, Opts);
|
2018-08-23 12:19:39 +00:00
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Named("xfoo"), SnippetSuffix("($0)"))));
|
2018-08-17 15:42:54 +00:00
|
|
|
}
|
2018-09-26 05:45:31 +00:00
|
|
|
{
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
template <class T, class U>
|
|
|
|
void xfoo(int a, U b);
|
|
|
|
void f() { xfo^ })cpp",
|
|
|
|
{}, Opts);
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Named("xfoo"), SnippetSuffix("<$1>($0)"))));
|
|
|
|
}
|
|
|
|
{
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
template <class T>
|
|
|
|
class foo_class{};
|
|
|
|
template <class T>
|
|
|
|
using foo_alias = T**;
|
|
|
|
void f() { foo_^ })cpp",
|
|
|
|
{}, Opts);
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Named("foo_class"), SnippetSuffix("<$0>")),
|
|
|
|
AllOf(Named("foo_alias"), SnippetSuffix("<$0>"))));
|
|
|
|
}
|
2018-08-17 15:42:54 +00:00
|
|
|
}
|
|
|
|
|
2018-08-23 13:14:50 +00:00
|
|
|
TEST(CompletionTest, SuggestOverrides) {
|
|
|
|
constexpr const char *const Text(R"cpp(
|
|
|
|
class A {
|
|
|
|
public:
|
|
|
|
virtual void vfunc(bool param);
|
|
|
|
virtual void vfunc(bool param, int p);
|
|
|
|
void func(bool param);
|
|
|
|
};
|
|
|
|
class B : public A {
|
|
|
|
virtual void ttt(bool param) const;
|
|
|
|
void vfunc(bool param, int p) override;
|
|
|
|
};
|
|
|
|
class C : public B {
|
|
|
|
public:
|
|
|
|
void vfunc(bool param) override;
|
|
|
|
^
|
|
|
|
};
|
|
|
|
)cpp");
|
|
|
|
const auto Results = completions(Text);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
AllOf(Contains(Labeled("void vfunc(bool param, int p) override")),
|
|
|
|
Contains(Labeled("void ttt(bool param) const override")),
|
|
|
|
Not(Contains(Labeled("void vfunc(bool param) override")))));
|
|
|
|
}
|
|
|
|
|
2018-09-03 15:25:27 +00:00
|
|
|
TEST(CompletionTest, OverridesNonIdentName) {
|
|
|
|
// Check the completions call does not crash.
|
|
|
|
completions(R"cpp(
|
|
|
|
struct Base {
|
|
|
|
virtual ~Base() = 0;
|
|
|
|
virtual operator int() = 0;
|
|
|
|
virtual Base& operator+(Base&) = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Derived : Base {
|
|
|
|
^
|
|
|
|
};
|
|
|
|
)cpp");
|
|
|
|
}
|
|
|
|
|
[clangd] Speculative code completion index request before Sema is run.
Summary:
For index-based code completion, send an asynchronous speculative index
request, based on the index request for the last code completion on the same
file and the filter text typed before the cursor, before sema code completion
is invoked. This can reduce the code completion latency (by roughly latency of
sema code completion) if the speculative request is the same as the one
generated for the ongoing code completion from sema. As a sequence of code
completions often have the same scopes and proximity paths etc, this should be
effective for a number of code completions.
Trace with speculative index request:{F6997544}
Reviewers: hokein, ilya-biryukov
Reviewed By: ilya-biryukov
Subscribers: javed.absar, jfb, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D50962
llvm-svn: 340604
2018-08-24 11:23:56 +00:00
|
|
|
TEST(SpeculateCompletionFilter, Filters) {
|
|
|
|
Annotations F(R"cpp($bof^
|
|
|
|
$bol^
|
|
|
|
ab$ab^
|
|
|
|
x.ab$dot^
|
|
|
|
x.$dotempty^
|
|
|
|
x::ab$scoped^
|
|
|
|
x::$scopedempty^
|
|
|
|
|
|
|
|
)cpp");
|
|
|
|
auto speculate = [&](StringRef PointName) {
|
|
|
|
auto Filter = speculateCompletionFilter(F.code(), F.point(PointName));
|
|
|
|
assert(Filter);
|
|
|
|
return *Filter;
|
|
|
|
};
|
|
|
|
EXPECT_EQ(speculate("bof"), "");
|
|
|
|
EXPECT_EQ(speculate("bol"), "");
|
|
|
|
EXPECT_EQ(speculate("ab"), "ab");
|
|
|
|
EXPECT_EQ(speculate("dot"), "ab");
|
|
|
|
EXPECT_EQ(speculate("dotempty"), "");
|
|
|
|
EXPECT_EQ(speculate("scoped"), "ab");
|
|
|
|
EXPECT_EQ(speculate("scopedempty"), "");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, EnableSpeculativeIndexRequest) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
|
|
|
|
auto File = testPath("foo.cpp");
|
|
|
|
Annotations Test(R"cpp(
|
|
|
|
namespace ns1 { int abc; }
|
|
|
|
namespace ns2 { int abc; }
|
|
|
|
void f() { ns1::ab$1^; ns1::ab$2^; }
|
2018-12-21 09:32:49 +00:00
|
|
|
void f2() { ns2::ab$3^; }
|
[clangd] Speculative code completion index request before Sema is run.
Summary:
For index-based code completion, send an asynchronous speculative index
request, based on the index request for the last code completion on the same
file and the filter text typed before the cursor, before sema code completion
is invoked. This can reduce the code completion latency (by roughly latency of
sema code completion) if the speculative request is the same as the one
generated for the ongoing code completion from sema. As a sequence of code
completions often have the same scopes and proximity paths etc, this should be
effective for a number of code completions.
Trace with speculative index request:{F6997544}
Reviewers: hokein, ilya-biryukov
Reviewed By: ilya-biryukov
Subscribers: javed.absar, jfb, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D50962
llvm-svn: 340604
2018-08-24 11:23:56 +00:00
|
|
|
)cpp");
|
|
|
|
runAddDocument(Server, File, Test.code());
|
|
|
|
clangd::CodeCompleteOptions Opts = {};
|
|
|
|
|
|
|
|
IndexRequestCollector Requests;
|
|
|
|
Opts.Index = &Requests;
|
|
|
|
Opts.SpeculativeIndexRequest = true;
|
|
|
|
|
|
|
|
auto CompleteAtPoint = [&](StringRef P) {
|
|
|
|
cantFail(runCodeComplete(Server, File, Test.point(P), Opts));
|
|
|
|
// Sleep for a while to make sure asynchronous call (if applicable) is also
|
|
|
|
// triggered before callback is invoked.
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
};
|
|
|
|
|
|
|
|
CompleteAtPoint("1");
|
|
|
|
auto Reqs1 = Requests.consumeRequests();
|
|
|
|
ASSERT_EQ(Reqs1.size(), 1u);
|
|
|
|
EXPECT_THAT(Reqs1[0].Scopes, UnorderedElementsAre("ns1::"));
|
|
|
|
|
|
|
|
CompleteAtPoint("2");
|
|
|
|
auto Reqs2 = Requests.consumeRequests();
|
|
|
|
// Speculation succeeded. Used speculative index result.
|
|
|
|
ASSERT_EQ(Reqs2.size(), 1u);
|
|
|
|
EXPECT_EQ(Reqs2[0], Reqs1[0]);
|
|
|
|
|
|
|
|
CompleteAtPoint("3");
|
|
|
|
// Speculation failed. Sent speculative index request and the new index
|
|
|
|
// request after sema.
|
|
|
|
auto Reqs3 = Requests.consumeRequests();
|
|
|
|
ASSERT_EQ(Reqs3.size(), 2u);
|
|
|
|
}
|
|
|
|
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
TEST(CompletionTest, InsertTheMostPopularHeader) {
|
[clangd] Cleanup: stop passing around list of supported URI schemes.
Summary:
Instead of passing around a list of supported URI schemes in clangd, we
expose an interface to convert a path to URI using any compatible scheme
that has been registered. It favors customized schemes and falls
back to "file" when no other scheme works.
Changes in this patch are:
- URI::create(AbsPath, URISchemes) -> URI::create(AbsPath). The new API finds a
compatible scheme from the registry.
- Remove URISchemes option everywhere (ClangdServer, SymbolCollecter, FileIndex etc).
- Unit tests will use "unittest" by default.
- Move "test" scheme from ClangdLSPServer to ClangdMain.cpp, and only
register the test scheme when lit-test or enable-lit-scheme is set.
(The new flag is added to make lit protocol.test work; I wonder if there
is alternative here.)
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D54800
llvm-svn: 347467
2018-11-22 15:02:05 +00:00
|
|
|
std::string DeclFile = URI::create(testPath("foo")).toString();
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
Symbol sym = func("Func");
|
2018-11-14 11:55:45 +00:00
|
|
|
sym.CanonicalDeclaration.FileURI = DeclFile.c_str();
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
sym.IncludeHeaders.emplace_back("\"foo.h\"", 2);
|
|
|
|
sym.IncludeHeaders.emplace_back("\"bar.h\"", 1000);
|
|
|
|
|
|
|
|
auto Results = completions("Fun^", {sym}).Completions;
|
|
|
|
assert(!Results.empty());
|
|
|
|
EXPECT_THAT(Results[0], AllOf(Named("Func"), InsertInclude("\"bar.h\"")));
|
|
|
|
EXPECT_EQ(Results[0].Includes.size(), 2u);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, NoInsertIncludeIfOnePresent) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
|
|
|
|
std::string FooHeader = testPath("foo.h");
|
|
|
|
FS.Files[FooHeader] = "";
|
|
|
|
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
|
[clangd] Cleanup: stop passing around list of supported URI schemes.
Summary:
Instead of passing around a list of supported URI schemes in clangd, we
expose an interface to convert a path to URI using any compatible scheme
that has been registered. It favors customized schemes and falls
back to "file" when no other scheme works.
Changes in this patch are:
- URI::create(AbsPath, URISchemes) -> URI::create(AbsPath). The new API finds a
compatible scheme from the registry.
- Remove URISchemes option everywhere (ClangdServer, SymbolCollecter, FileIndex etc).
- Unit tests will use "unittest" by default.
- Move "test" scheme from ClangdLSPServer to ClangdMain.cpp, and only
register the test scheme when lit-test or enable-lit-scheme is set.
(The new flag is added to make lit protocol.test work; I wonder if there
is alternative here.)
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D54800
llvm-svn: 347467
2018-11-22 15:02:05 +00:00
|
|
|
std::string DeclFile = URI::create(testPath("foo")).toString();
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
Symbol sym = func("Func");
|
2018-11-14 11:55:45 +00:00
|
|
|
sym.CanonicalDeclaration.FileURI = DeclFile.c_str();
|
[clangd] Support multiple #include headers in one symbol.
Summary:
Currently, a symbol can have only one #include header attached, which
might not work well if the symbol can be imported via different #includes depending
on where it's used. This patch stores multiple #include headers (with # references)
for each symbol, so that CodeCompletion can decide which include to insert.
In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Differential Revision: https://reviews.llvm.org/D51291
llvm-svn: 341304
2018-09-03 10:18:21 +00:00
|
|
|
sym.IncludeHeaders.emplace_back("\"foo.h\"", 2);
|
|
|
|
sym.IncludeHeaders.emplace_back("\"bar.h\"", 1000);
|
|
|
|
|
|
|
|
EXPECT_THAT(
|
|
|
|
completions(Server, "#include \"foo.h\"\nFun^", {sym}).Completions,
|
|
|
|
UnorderedElementsAre(
|
|
|
|
AllOf(Named("Func"), HasInclude("\"foo.h\""), Not(InsertInclude()))));
|
|
|
|
}
|
|
|
|
|
2018-09-06 09:59:37 +00:00
|
|
|
TEST(CompletionTest, MergeMacrosFromIndexAndSema) {
|
|
|
|
Symbol Sym;
|
|
|
|
Sym.Name = "Clangd_Macro_Test";
|
|
|
|
Sym.ID = SymbolID("c:foo.cpp@8@macro@Clangd_Macro_Test");
|
|
|
|
Sym.SymInfo.Kind = index::SymbolKind::Macro;
|
2018-09-06 18:52:26 +00:00
|
|
|
Sym.Flags |= Symbol::IndexedForCodeCompletion;
|
2018-09-06 09:59:37 +00:00
|
|
|
EXPECT_THAT(completions("#define Clangd_Macro_Test\nClangd_Macro_T^", {Sym})
|
|
|
|
.Completions,
|
|
|
|
UnorderedElementsAre(Named("Clangd_Macro_Test")));
|
|
|
|
}
|
|
|
|
|
2018-09-19 09:35:04 +00:00
|
|
|
TEST(CompletionTest, NoMacroFromPreambleIfIndexIsSet) {
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(#define CLANGD_PREAMBLE x
|
|
|
|
|
|
|
|
int x = 0;
|
|
|
|
#define CLANGD_MAIN x
|
|
|
|
void f() { CLANGD_^ }
|
|
|
|
)cpp",
|
|
|
|
{func("CLANGD_INDEX")});
|
|
|
|
// Index is overriden in code completion options, so the preamble symbol is
|
|
|
|
// not seen.
|
|
|
|
EXPECT_THAT(Results.Completions, UnorderedElementsAre(Named("CLANGD_MAIN"),
|
|
|
|
Named("CLANGD_INDEX")));
|
|
|
|
}
|
|
|
|
|
2018-09-06 18:52:26 +00:00
|
|
|
TEST(CompletionTest, DeprecatedResults) {
|
|
|
|
std::string Body = R"cpp(
|
|
|
|
void TestClangd();
|
|
|
|
void TestClangc() __attribute__((deprecated("", "")));
|
|
|
|
)cpp";
|
|
|
|
|
|
|
|
EXPECT_THAT(
|
|
|
|
completions(Body + "int main() { TestClang^ }").Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Named("TestClangd"), Not(Deprecated())),
|
|
|
|
AllOf(Named("TestClangc"), Deprecated())));
|
|
|
|
}
|
|
|
|
|
2018-09-10 14:22:42 +00:00
|
|
|
TEST(SignatureHelpTest, InsideArgument) {
|
|
|
|
{
|
|
|
|
const auto Results = signatures(R"cpp(
|
|
|
|
void foo(int x);
|
|
|
|
void foo(int x, int y);
|
|
|
|
int main() { foo(1+^); }
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.signatures,
|
|
|
|
ElementsAre(Sig("foo(int x) -> void", {"int x"}),
|
|
|
|
Sig("foo(int x, int y) -> void", {"int x", "int y"})));
|
|
|
|
EXPECT_EQ(0, Results.activeParameter);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Results = signatures(R"cpp(
|
|
|
|
void foo(int x);
|
|
|
|
void foo(int x, int y);
|
|
|
|
int main() { foo(1^); }
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.signatures,
|
|
|
|
ElementsAre(Sig("foo(int x) -> void", {"int x"}),
|
|
|
|
Sig("foo(int x, int y) -> void", {"int x", "int y"})));
|
|
|
|
EXPECT_EQ(0, Results.activeParameter);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Results = signatures(R"cpp(
|
|
|
|
void foo(int x);
|
|
|
|
void foo(int x, int y);
|
|
|
|
int main() { foo(1^0); }
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.signatures,
|
|
|
|
ElementsAre(Sig("foo(int x) -> void", {"int x"}),
|
|
|
|
Sig("foo(int x, int y) -> void", {"int x", "int y"})));
|
|
|
|
EXPECT_EQ(0, Results.activeParameter);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Results = signatures(R"cpp(
|
|
|
|
void foo(int x);
|
|
|
|
void foo(int x, int y);
|
|
|
|
int bar(int x, int y);
|
|
|
|
int main() { bar(foo(2, 3^)); }
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(Results.signatures, ElementsAre(Sig("foo(int x, int y) -> void",
|
|
|
|
{"int x", "int y"})));
|
|
|
|
EXPECT_EQ(1, Results.activeParameter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-11 15:12:10 +00:00
|
|
|
TEST(SignatureHelpTest, ConstructorInitializeFields) {
|
|
|
|
{
|
|
|
|
const auto Results = signatures(R"cpp(
|
|
|
|
struct A {
|
|
|
|
A(int);
|
|
|
|
};
|
|
|
|
struct B {
|
|
|
|
B() : a_elem(^) {}
|
|
|
|
A a_elem;
|
|
|
|
};
|
|
|
|
)cpp");
|
2019-01-03 13:28:05 +00:00
|
|
|
EXPECT_THAT(Results.signatures,
|
|
|
|
UnorderedElementsAre(Sig("A(int)", {"int"}),
|
|
|
|
Sig("A(A &&)", {"A &&"}),
|
|
|
|
Sig("A(const A &)", {"const A &"})));
|
2018-09-11 15:12:10 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Results = signatures(R"cpp(
|
|
|
|
struct A {
|
|
|
|
A(int);
|
|
|
|
};
|
|
|
|
struct C {
|
|
|
|
C(int);
|
|
|
|
C(A);
|
|
|
|
};
|
|
|
|
struct B {
|
|
|
|
B() : c_elem(A(1^)) {}
|
|
|
|
C c_elem;
|
|
|
|
};
|
|
|
|
)cpp");
|
2019-01-03 13:28:05 +00:00
|
|
|
EXPECT_THAT(Results.signatures,
|
|
|
|
UnorderedElementsAre(Sig("A(int)", {"int"}),
|
|
|
|
Sig("A(A &&)", {"A &&"}),
|
|
|
|
Sig("A(const A &)", {"const A &"})));
|
2018-09-11 15:12:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-27 14:21:07 +00:00
|
|
|
TEST(CompletionTest, IncludedCompletionKinds) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
std::string Subdir = testPath("sub");
|
2018-10-20 15:30:37 +00:00
|
|
|
std::string SearchDirArg = (Twine("-I") + Subdir).str();
|
2018-09-27 14:21:07 +00:00
|
|
|
CDB.ExtraClangFlags = {SearchDirArg.c_str()};
|
|
|
|
std::string BarHeader = testPath("sub/bar.h");
|
|
|
|
FS.Files[BarHeader] = "";
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
auto Results = completions(Server,
|
2019-01-03 13:28:05 +00:00
|
|
|
R"cpp(
|
2018-09-27 14:21:07 +00:00
|
|
|
#include "^"
|
2019-01-03 13:28:05 +00:00
|
|
|
)cpp");
|
2018-09-27 14:21:07 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
AllOf(Has("sub/", CompletionItemKind::Folder),
|
|
|
|
Has("bar.h\"", CompletionItemKind::File)));
|
|
|
|
}
|
|
|
|
|
2018-10-02 14:46:08 +00:00
|
|
|
TEST(CompletionTest, NoCrashAtNonAlphaIncludeHeader) {
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
#include "./^"
|
2019-01-03 13:28:05 +00:00
|
|
|
)cpp");
|
2018-10-02 14:46:08 +00:00
|
|
|
EXPECT_TRUE(Results.Completions.empty());
|
|
|
|
}
|
|
|
|
|
2018-09-27 18:46:00 +00:00
|
|
|
TEST(CompletionTest, NoAllScopesCompletionWhenQualified) {
|
|
|
|
clangd::CodeCompleteOptions Opts = {};
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
void f() { na::Clangd^ }
|
|
|
|
)cpp",
|
|
|
|
{cls("na::ClangdA"), cls("nx::ClangdX"), cls("Clangd3")}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(
|
|
|
|
AllOf(Qualifier(""), Scope("na::"), Named("ClangdA"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, AllScopesCompletion) {
|
|
|
|
clangd::CodeCompleteOptions Opts = {};
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
namespace na {
|
|
|
|
void f() { Clangd^ }
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
{cls("nx::Clangd1"), cls("ny::Clangd2"), cls("Clangd3"),
|
|
|
|
cls("na::nb::Clangd4")},
|
|
|
|
Opts);
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Qualifier("nx::"), Named("Clangd1")),
|
|
|
|
AllOf(Qualifier("ny::"), Named("Clangd2")),
|
|
|
|
AllOf(Qualifier(""), Scope(""), Named("Clangd3")),
|
|
|
|
AllOf(Qualifier("nb::"), Named("Clangd4"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, NoQualifierIfShadowed) {
|
|
|
|
clangd::CodeCompleteOptions Opts = {};
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
namespace nx { class Clangd1 {}; }
|
|
|
|
using nx::Clangd1;
|
|
|
|
void f() { Clangd^ }
|
|
|
|
)cpp",
|
|
|
|
{cls("nx::Clangd1"), cls("nx::Clangd2")}, Opts);
|
|
|
|
// Although Clangd1 is from another namespace, Sema tells us it's in-scope and
|
|
|
|
// needs no qualifier.
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Qualifier(""), Named("Clangd1")),
|
|
|
|
AllOf(Qualifier("nx::"), Named("Clangd2"))));
|
|
|
|
}
|
2018-09-27 14:21:07 +00:00
|
|
|
|
2018-10-24 15:24:29 +00:00
|
|
|
TEST(CompletionTest, NoCompletionsForNewNames) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
void f() { int n^ }
|
|
|
|
)cpp",
|
|
|
|
{cls("naber"), cls("nx::naber")}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions, UnorderedElementsAre());
|
|
|
|
}
|
2018-11-14 09:05:19 +00:00
|
|
|
|
|
|
|
TEST(CompletionTest, ObjectiveCMethodNoArguments) {
|
|
|
|
auto Results = completions(R"objc(
|
|
|
|
@interface Foo
|
|
|
|
@property(nonatomic, setter=setXToIgnoreComplete:) int value;
|
|
|
|
@end
|
|
|
|
Foo *foo = [Foo new]; int y = [foo v^]
|
|
|
|
)objc",
|
2019-01-03 13:28:05 +00:00
|
|
|
/*IndexSymbols=*/{},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
2018-11-14 09:05:19 +00:00
|
|
|
|
|
|
|
auto C = Results.Completions;
|
|
|
|
EXPECT_THAT(C, ElementsAre(Named("value")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(Kind(CompletionItemKind::Method)));
|
|
|
|
EXPECT_THAT(C, ElementsAre(ReturnType("int")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(Signature("")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(SnippetSuffix("")));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, ObjectiveCMethodOneArgument) {
|
|
|
|
auto Results = completions(R"objc(
|
|
|
|
@interface Foo
|
|
|
|
- (int)valueForCharacter:(char)c;
|
|
|
|
@end
|
|
|
|
Foo *foo = [Foo new]; int y = [foo v^]
|
|
|
|
)objc",
|
2019-01-03 13:28:05 +00:00
|
|
|
/*IndexSymbols=*/{},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
2018-11-14 09:05:19 +00:00
|
|
|
|
|
|
|
auto C = Results.Completions;
|
|
|
|
EXPECT_THAT(C, ElementsAre(Named("valueForCharacter:")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(Kind(CompletionItemKind::Method)));
|
|
|
|
EXPECT_THAT(C, ElementsAre(ReturnType("int")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(Signature("(char)")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(SnippetSuffix("${1:(char)}")));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, ObjectiveCMethodTwoArgumentsFromBeginning) {
|
|
|
|
auto Results = completions(R"objc(
|
|
|
|
@interface Foo
|
|
|
|
+ (id)fooWithValue:(int)value fooey:(unsigned int)fooey;
|
|
|
|
@end
|
|
|
|
id val = [Foo foo^]
|
|
|
|
)objc",
|
2019-01-03 13:28:05 +00:00
|
|
|
/*IndexSymbols=*/{},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
2018-11-14 09:05:19 +00:00
|
|
|
|
|
|
|
auto C = Results.Completions;
|
|
|
|
EXPECT_THAT(C, ElementsAre(Named("fooWithValue:")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(Kind(CompletionItemKind::Method)));
|
|
|
|
EXPECT_THAT(C, ElementsAre(ReturnType("id")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(Signature("(int) fooey:(unsigned int)")));
|
2019-01-03 13:28:05 +00:00
|
|
|
EXPECT_THAT(
|
|
|
|
C, ElementsAre(SnippetSuffix("${1:(int)} fooey:${2:(unsigned int)}")));
|
2018-11-14 09:05:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, ObjectiveCMethodTwoArgumentsFromMiddle) {
|
|
|
|
auto Results = completions(R"objc(
|
|
|
|
@interface Foo
|
|
|
|
+ (id)fooWithValue:(int)value fooey:(unsigned int)fooey;
|
|
|
|
@end
|
|
|
|
id val = [Foo fooWithValue:10 f^]
|
|
|
|
)objc",
|
2019-01-03 13:28:05 +00:00
|
|
|
/*IndexSymbols=*/{},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
2018-11-14 09:05:19 +00:00
|
|
|
|
|
|
|
auto C = Results.Completions;
|
|
|
|
EXPECT_THAT(C, ElementsAre(Named("fooey:")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(Kind(CompletionItemKind::Method)));
|
|
|
|
EXPECT_THAT(C, ElementsAre(ReturnType("id")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(Signature("(unsigned int)")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(SnippetSuffix("${1:(unsigned int)}")));
|
|
|
|
}
|
|
|
|
|
2019-01-24 10:41:43 +00:00
|
|
|
TEST(CompletionTest, WorksWithNullType) {
|
|
|
|
auto R = completions(R"cpp(
|
|
|
|
int main() {
|
|
|
|
for (auto [loopVar] : y ) { // y has to be unresolved.
|
|
|
|
int z = loopV^;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(R.Completions, ElementsAre(Named("loopVar")));
|
|
|
|
}
|
|
|
|
|
2017-12-05 07:20:26 +00:00
|
|
|
} // namespace
|
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|