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"
|
2019-01-28 14:01:55 +00:00
|
|
|
#include "TestIndex.h"
|
2019-02-26 14:23:47 +00:00
|
|
|
#include "TestTU.h"
|
2019-04-26 07:45:49 +00:00
|
|
|
#include "index/Index.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"
|
2019-04-11 09:36:36 +00:00
|
|
|
#include "clang/Tooling/CompilationDatabase.h"
|
2018-05-24 14:49:23 +00:00
|
|
|
#include "llvm/Support/Error.h"
|
2019-04-24 09:42:53 +00:00
|
|
|
#include "llvm/Support/Path.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;
|
|
|
|
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; }
|
2019-05-24 10:18:39 +00:00
|
|
|
MATCHER_P(NameStartsWith, Prefix, "") {
|
|
|
|
return llvm::StringRef(arg.Name).startswith(Prefix);
|
|
|
|
}
|
2018-07-02 11:13:16 +00:00
|
|
|
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-04-11 09:36:36 +00:00
|
|
|
// Builds a server and runs code completion.
|
|
|
|
// If IndexSymbols is non-empty, an index will be built and passed to opts.
|
|
|
|
CodeCompleteResult completionsNoCompile(llvm::StringRef Text,
|
|
|
|
std::vector<Symbol> IndexSymbols = {},
|
|
|
|
clangd::CodeCompleteOptions Opts = {},
|
|
|
|
PathRef FilePath = "foo.cpp") {
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
MockFSProvider FS;
|
|
|
|
Annotations Test(Text);
|
|
|
|
return codeComplete(FilePath, tooling::CompileCommand(), /*Preamble=*/nullptr,
|
|
|
|
Test.code(), Test.point(), FS.getFileSystem(), Opts);
|
|
|
|
}
|
|
|
|
|
2018-05-03 14:53:02 +00:00
|
|
|
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
|
|
|
};
|
2019-05-06 10:25:10 +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
|
|
|
|
2019-05-06 12:03:26 +00:00
|
|
|
EXPECT_TRUE(Results.RanParser);
|
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
|
|
|
|
2019-05-06 12:03:26 +00:00
|
|
|
EXPECT_TRUE(Results.RanParser);
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-06 10:25:10 +00:00
|
|
|
TEST(CompletionTest, Accessible) {
|
2017-12-08 15:00:59 +00:00
|
|
|
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,
|
2019-05-06 10:25:10 +00:00
|
|
|
AllOf(Has("priv"), Has("prot"), Has("pub")));
|
2017-12-08 15:00:59 +00:00
|
|
|
|
|
|
|
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));
|
2019-05-29 10:11:14 +00:00
|
|
|
|
|
|
|
// Members of anonymous unions are of kind 'field'.
|
|
|
|
Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
struct X{
|
|
|
|
union {
|
|
|
|
void *a;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
auto u = X().^
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Named("a"), Kind(CompletionItemKind::Field))));
|
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
|
|
|
}
|
|
|
|
|
2019-05-06 10:25:10 +00:00
|
|
|
TEST(CompletionTest, ContextWords) {
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
enum class Color { RED, YELLOW, BLUE };
|
|
|
|
|
|
|
|
// (blank lines so the definition above isn't "context")
|
|
|
|
|
|
|
|
// "It was a yellow car," he said. "Big yellow car, new."
|
|
|
|
auto Finish = Color::^
|
|
|
|
)cpp");
|
|
|
|
// Yellow would normally sort last (alphabetic).
|
|
|
|
// But the recent mention shuold bump it up.
|
|
|
|
ASSERT_THAT(Results.Completions,
|
|
|
|
HasSubsequence(Named("YELLOW"), Named("BLUE")));
|
|
|
|
}
|
|
|
|
|
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\""))));
|
2019-04-10 12:15:35 +00:00
|
|
|
// Can be disabled via option.
|
|
|
|
CodeCompleteOptions NoInsertion;
|
|
|
|
NoInsertion.InsertIncludes = CodeCompleteOptions::NeverInsert;
|
|
|
|
Results = completions(Server,
|
|
|
|
R"cpp(
|
|
|
|
int main() { ns::^ }
|
|
|
|
)cpp",
|
|
|
|
{Sym}, NoInsertion);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
ElementsAre(AllOf(Named("X"), Not(InsertInclude()))));
|
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
|
|
|
|
2019-02-04 16:19:57 +00:00
|
|
|
TEST(CompletionTest, DynamicIndexIncludeInsertion) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer::Options Opts = ClangdServer::optsForTest();
|
|
|
|
Opts.BuildDynamicSymbolIndex = true;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, Opts);
|
|
|
|
|
|
|
|
FS.Files[testPath("foo_header.h")] = R"cpp(
|
[clangd] Include insertion: require header guards, drop other heuristics, treat .def like .inc.
Summary:
We do have some reports of include insertion behaving badly in some
codebases. Requiring header guards both makes sense in principle, and is
likely to disable this "nice-to-have" feature in codebases where headers don't
follow the expected pattern.
With this we can drop some other heuristics, such as looking at file
extensions to detect known non-headers - implementation files have no guards.
One wrinkle here is #import - objc headers may not have guards because
they're intended to be used via #import. If the header is the main file
or is #included, we won't collect locations - merge should take care of
this if we see the file #imported somewhere. Seems likely to be OK.
Headers which have a canonicalization (stdlib, IWYU) are exempt from this check.
*.inc files continue to be handled by looking up to the including file.
This patch also adds *.def here - tablegen wants this pattern too.
In terms of code structure, the division between SymbolCollector and
CanonicalIncludes has shifted: SymbolCollector is responsible for more.
This is because SymbolCollector has all the SourceManager/HeaderSearch access
needed for checking for guards, and we interleave these checks with the *.def
checks in a loop (potentially).
We could hand all the info into CanonicalIncludes and put the logic there
if that's preferable.
Reviewers: ioeric
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D60316
llvm-svn: 358571
2019-04-17 10:36:02 +00:00
|
|
|
#pragma once
|
2019-02-04 16:19:57 +00:00
|
|
|
struct Foo {
|
|
|
|
// Member doc
|
|
|
|
int foo();
|
|
|
|
};
|
|
|
|
)cpp";
|
|
|
|
const std::string FileContent(R"cpp(
|
|
|
|
#include "foo_header.h"
|
|
|
|
int Foo::foo() {
|
|
|
|
return 42;
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
Server.addDocument(testPath("foo_impl.cpp"), FileContent);
|
|
|
|
// Wait for the dynamic index being built.
|
|
|
|
ASSERT_TRUE(Server.blockUntilIdleForTest());
|
2019-04-24 09:42:53 +00:00
|
|
|
EXPECT_THAT(completions(Server, "Foo^ foo;").Completions,
|
|
|
|
ElementsAre(AllOf(Named("Foo"),
|
|
|
|
HasInclude('"' +
|
|
|
|
llvm::sys::path::convert_to_slash(
|
|
|
|
testPath("foo_header.h")) +
|
|
|
|
'"'),
|
|
|
|
InsertInclude())));
|
2019-02-04 16:19:57 +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");
|
|
|
|
|
[clangd] Improve global code completion when scope specifier is unresolved.
Summary:
Suppose `clangd::` is unresolved in the following example. Currently,
we simply use "clangd::" as the query scope. We can do better by combining with
accessible scopes in the context. The query scopes can be `{clangd::, clang::clangd::}`.
```
namespace clang { clangd::^ }
```
Reviewers: ilya-biryukov, sammccall, hokein, kadircet
Reviewed By: kadircet
Subscribers: MaskRay, jkorous, arphaman, kadircet, jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58448
llvm-svn: 354963
2019-02-27 11:42:37 +00:00
|
|
|
EXPECT_THAT(Requests,
|
|
|
|
ElementsAre(Field(
|
|
|
|
&FuzzyFindRequest::Scopes,
|
|
|
|
UnorderedElementsAre("a::bar::", "ns::bar::", "bar::"))));
|
2018-01-23 11:37:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
2019-04-15 12:32:28 +00:00
|
|
|
runAddDocument(Server, 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);
|
2019-05-24 10:18:39 +00:00
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
AllOf(Contains(AllOf(Labeled("void vfunc(bool param, int p) override"),
|
|
|
|
NameStartsWith("vfunc"))),
|
|
|
|
Contains(AllOf(Labeled("void ttt(bool param) const override"),
|
|
|
|
NameStartsWith("ttt"))),
|
|
|
|
Not(Contains(Labeled("void vfunc(bool param) override")))));
|
2018-08-23 13:14:50 +00:00
|
|
|
}
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2019-04-10 11:50:40 +00:00
|
|
|
TEST(GuessCompletionPrefix, Filters) {
|
|
|
|
for (llvm::StringRef Case : {
|
|
|
|
"[[scope::]][[ident]]^",
|
|
|
|
"[[]][[]]^",
|
|
|
|
"\n[[]][[]]^",
|
|
|
|
"[[]][[ab]]^",
|
|
|
|
"x.[[]][[ab]]^",
|
|
|
|
"x.[[]][[]]^",
|
|
|
|
"[[x::]][[ab]]^",
|
|
|
|
"[[x::]][[]]^",
|
|
|
|
"[[::x::]][[ab]]^",
|
|
|
|
"some text [[scope::more::]][[identif]]^ier",
|
|
|
|
"some text [[scope::]][[mor]]^e::identifier",
|
|
|
|
"weird case foo::[[::bar::]][[baz]]^",
|
|
|
|
}) {
|
|
|
|
Annotations F(Case);
|
|
|
|
auto Offset = cantFail(positionToOffset(F.code(), F.point()));
|
|
|
|
auto ToStringRef = [&](Range R) {
|
|
|
|
return F.code().slice(cantFail(positionToOffset(F.code(), R.start)),
|
|
|
|
cantFail(positionToOffset(F.code(), R.end)));
|
|
|
|
};
|
|
|
|
auto WantQualifier = ToStringRef(F.ranges()[0]),
|
|
|
|
WantName = ToStringRef(F.ranges()[1]);
|
|
|
|
|
|
|
|
auto Prefix = guessCompletionPrefix(F.code(), Offset);
|
|
|
|
// Even when components are empty, check their offsets are correct.
|
|
|
|
EXPECT_EQ(WantQualifier, Prefix.Qualifier) << Case;
|
|
|
|
EXPECT_EQ(WantQualifier.begin(), Prefix.Qualifier.begin()) << Case;
|
|
|
|
EXPECT_EQ(WantName, Prefix.Name) << Case;
|
|
|
|
EXPECT_EQ(WantName.begin(), Prefix.Name.begin()) << Case;
|
|
|
|
}
|
[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(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")));
|
|
|
|
}
|
|
|
|
|
2019-05-02 16:12:36 +00:00
|
|
|
TEST(CompletionTest, MacroFromPreamble) {
|
|
|
|
MockFSProvider FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
std::string FooHeader = testPath("foo.h");
|
|
|
|
FS.Files[FooHeader] = "#define CLANGD_PREAMBLE_HEADER x\n";
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
2018-09-19 09:35:04 +00:00
|
|
|
auto Results = completions(
|
2019-05-02 16:12:36 +00:00
|
|
|
R"cpp(#include "foo.h"
|
|
|
|
#define CLANGD_PREAMBLE_MAIN x
|
2018-09-19 09:35:04 +00:00
|
|
|
|
|
|
|
int x = 0;
|
|
|
|
#define CLANGD_MAIN x
|
|
|
|
void f() { CLANGD_^ }
|
|
|
|
)cpp",
|
|
|
|
{func("CLANGD_INDEX")});
|
2019-05-02 16:12:36 +00:00
|
|
|
// We should get results from the main file, including the preamble section.
|
|
|
|
// However no results from included files (the index should cover them).
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(Named("CLANGD_PREAMBLE_MAIN"),
|
|
|
|
Named("CLANGD_MAIN"),
|
|
|
|
Named("CLANGD_INDEX")));
|
2018-09-19 09:35:04 +00:00
|
|
|
}
|
|
|
|
|
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-05-28 15:33:37 +00:00
|
|
|
TEST(CompletionTest, CursorInSnippets) {
|
|
|
|
clangd::CodeCompleteOptions Options;
|
|
|
|
Options.EnableSnippets = true;
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
void while_foo(int a, int b);
|
|
|
|
void test() {
|
|
|
|
whil^
|
|
|
|
})cpp",
|
|
|
|
/*IndexSymbols=*/{}, Options);
|
|
|
|
|
|
|
|
// Last placeholder in code patterns should be $0 to put the cursor there.
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
Contains(AllOf(Named("while"),
|
2019-05-28 16:28:27 +00:00
|
|
|
SnippetSuffix("(${1:condition}){\n${0:statements}\n}"))));
|
2019-05-28 15:33:37 +00:00
|
|
|
// However, snippets for functions must *not* end with $0.
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
Contains(AllOf(Named("while_foo"),
|
|
|
|
SnippetSuffix("(${1:int a}, ${2:int b})"))));
|
|
|
|
}
|
|
|
|
|
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")));
|
|
|
|
}
|
|
|
|
|
2019-02-26 14:23:47 +00:00
|
|
|
TEST(CompletionTest, UsingDecl) {
|
|
|
|
const char *Header(R"cpp(
|
|
|
|
void foo(int);
|
|
|
|
namespace std {
|
|
|
|
using ::foo;
|
|
|
|
})cpp");
|
|
|
|
const char *Source(R"cpp(
|
|
|
|
void bar() {
|
|
|
|
std::^;
|
|
|
|
})cpp");
|
|
|
|
auto Index = TestTU::withHeaderCode(Header).index();
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.Index = Index.get();
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
auto R = completions(Source, {}, Opts);
|
|
|
|
EXPECT_THAT(R.Completions,
|
|
|
|
ElementsAre(AllOf(Scope("std::"), Named("foo"),
|
|
|
|
Kind(CompletionItemKind::Reference))));
|
|
|
|
}
|
|
|
|
|
[clangd] Improve global code completion when scope specifier is unresolved.
Summary:
Suppose `clangd::` is unresolved in the following example. Currently,
we simply use "clangd::" as the query scope. We can do better by combining with
accessible scopes in the context. The query scopes can be `{clangd::, clang::clangd::}`.
```
namespace clang { clangd::^ }
```
Reviewers: ilya-biryukov, sammccall, hokein, kadircet
Reviewed By: kadircet
Subscribers: MaskRay, jkorous, arphaman, kadircet, jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D58448
llvm-svn: 354963
2019-02-27 11:42:37 +00:00
|
|
|
TEST(CompletionTest, ScopeIsUnresolved) {
|
|
|
|
clangd::CodeCompleteOptions Opts = {};
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
namespace a {
|
|
|
|
void f() { b::X^ }
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
{cls("a::b::XYZ")}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Qualifier(""), Named("XYZ"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, NestedScopeIsUnresolved) {
|
|
|
|
clangd::CodeCompleteOptions Opts = {};
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
namespace a {
|
|
|
|
namespace b {}
|
|
|
|
void f() { b::c::X^ }
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
{cls("a::b::c::XYZ")}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Qualifier(""), Named("XYZ"))));
|
|
|
|
}
|
|
|
|
|
2019-04-10 15:16:54 +00:00
|
|
|
// Clang parser gets confused here and doesn't report the ns:: prefix.
|
|
|
|
// Naive behavior is to insert it again. We examine the source and recover.
|
|
|
|
TEST(CompletionTest, NamespaceDoubleInsertion) {
|
2019-04-10 11:50:40 +00:00
|
|
|
clangd::CodeCompleteOptions Opts = {};
|
|
|
|
|
|
|
|
auto Results = completions(R"cpp(
|
2019-04-10 15:16:54 +00:00
|
|
|
namespace foo {
|
2019-04-10 11:50:40 +00:00
|
|
|
namespace ns {}
|
|
|
|
#define M(X) < X
|
|
|
|
M(ns::ABC^
|
2019-04-10 15:16:54 +00:00
|
|
|
}
|
2019-04-10 11:50:40 +00:00
|
|
|
)cpp",
|
2019-04-10 15:16:54 +00:00
|
|
|
{cls("foo::ns::ABCDE")}, Opts);
|
2019-04-10 11:50:40 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Qualifier(""), Named("ABCDE"))));
|
|
|
|
}
|
|
|
|
|
2019-04-11 09:36:36 +00:00
|
|
|
TEST(NoCompileCompletionTest, Basic) {
|
|
|
|
auto Results = completionsNoCompile(R"cpp(
|
|
|
|
void func() {
|
|
|
|
int xyz;
|
|
|
|
int abc;
|
|
|
|
^
|
|
|
|
}
|
|
|
|
)cpp");
|
2019-05-06 12:03:26 +00:00
|
|
|
EXPECT_FALSE(Results.RanParser);
|
2019-04-11 09:36:36 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(Named("void"), Named("func"), Named("int"),
|
|
|
|
Named("xyz"), Named("abc")));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(NoCompileCompletionTest, WithFilter) {
|
|
|
|
auto Results = completionsNoCompile(R"cpp(
|
|
|
|
void func() {
|
|
|
|
int sym1;
|
|
|
|
int sym2;
|
|
|
|
int xyz1;
|
|
|
|
int xyz2;
|
|
|
|
sy^
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(Named("sym1"), Named("sym2")));
|
|
|
|
}
|
|
|
|
|
2019-04-26 07:45:49 +00:00
|
|
|
TEST(NoCompileCompletionTest, WithIndex) {
|
|
|
|
std::vector<Symbol> Syms = {func("xxx"), func("a::xxx"), func("ns::b::xxx"),
|
|
|
|
func("c::xxx"), func("ns::d::xxx")};
|
|
|
|
auto Results = completionsNoCompile(
|
|
|
|
R"cpp(
|
|
|
|
// Current-scopes, unqualified completion.
|
|
|
|
using namespace a;
|
|
|
|
namespace ns {
|
|
|
|
using namespace b;
|
|
|
|
void foo() {
|
|
|
|
xx^
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
Syms);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Qualifier(""), Scope("")),
|
|
|
|
AllOf(Qualifier(""), Scope("a::")),
|
|
|
|
AllOf(Qualifier(""), Scope("ns::b::"))));
|
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
Results = completionsNoCompile(
|
|
|
|
R"cpp(
|
|
|
|
// All-scopes unqualified completion.
|
|
|
|
using namespace a;
|
|
|
|
namespace ns {
|
|
|
|
using namespace b;
|
|
|
|
void foo() {
|
|
|
|
xx^
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
Syms, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(Qualifier(""), Scope("")),
|
|
|
|
AllOf(Qualifier(""), Scope("a::")),
|
|
|
|
AllOf(Qualifier(""), Scope("ns::b::")),
|
|
|
|
AllOf(Qualifier("c::"), Scope("c::")),
|
|
|
|
AllOf(Qualifier("d::"), Scope("ns::d::"))));
|
|
|
|
Results = completionsNoCompile(
|
|
|
|
R"cpp(
|
|
|
|
// Qualified completion.
|
|
|
|
using namespace a;
|
|
|
|
namespace ns {
|
|
|
|
using namespace b;
|
|
|
|
void foo() {
|
|
|
|
b::xx^
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
Syms, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
ElementsAre(AllOf(Qualifier(""), Scope("ns::b::"))));
|
|
|
|
Results = completionsNoCompile(
|
|
|
|
R"cpp(
|
|
|
|
// Absolutely qualified completion.
|
|
|
|
using namespace a;
|
|
|
|
namespace ns {
|
|
|
|
using namespace b;
|
|
|
|
void foo() {
|
|
|
|
::a::xx^
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
Syms, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
ElementsAre(AllOf(Qualifier(""), Scope("a::"))));
|
|
|
|
}
|
|
|
|
|
2017-12-05 07:20:26 +00:00
|
|
|
} // namespace
|
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|