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
|
|
|
|
2021-01-10 16:32:00 +01:00
|
|
|
#include "ASTSignals.h"
|
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"
|
2022-12-07 13:50:47 +01:00
|
|
|
#include "Feature.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"
|
2022-10-28 16:30:22 +02:00
|
|
|
#include "index/SymbolOrigin.h"
|
[clangd] Move non-clang base pieces into separate support/ lib. NFCI
Summary:
This enforces layering, reduces a sprawling clangd/ directory, and makes life
easier for embedders.
Reviewers: kbobyrev
Subscribers: mgorny, ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, jfb, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D79014
2020-04-28 17:49:17 +02:00
|
|
|
#include "support/Threading.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"
|
2021-12-09 09:36:04 +01:00
|
|
|
#include "llvm/ADT/StringRef.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"
|
2023-01-12 13:40:47 -08:00
|
|
|
#include "llvm/Testing/Annotations/Annotations.h"
|
2018-05-28 12:11:37 +00:00
|
|
|
#include "llvm/Testing/Support/Error.h"
|
2022-10-28 16:30:22 +02:00
|
|
|
#include "llvm/Testing/Support/SupportHelpers.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"
|
2019-10-07 17:12:18 +00:00
|
|
|
#include <condition_variable>
|
2020-03-04 13:25:02 +01:00
|
|
|
#include <functional>
|
2019-10-07 17:12:18 +00:00
|
|
|
#include <mutex>
|
2020-03-04 13:25:02 +01:00
|
|
|
#include <vector>
|
2017-12-05 07:20:26 +00:00
|
|
|
|
|
|
|
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;
|
2020-09-19 10:07:34 +02:00
|
|
|
using ContextKind = CodeCompletionContext::Kind;
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2017-12-05 20:11:29 +00:00
|
|
|
// GMock helpers for matching completion items.
|
2022-02-01 10:14:07 +00:00
|
|
|
MATCHER_P(named, Name, "") { return arg.Name == Name; }
|
|
|
|
MATCHER_P(mainFileRefs, Refs, "") { return arg.MainFileRefs == Refs; }
|
|
|
|
MATCHER_P(scopeRefs, Refs, "") { return arg.ScopeRefsInFile == Refs; }
|
|
|
|
MATCHER_P(nameStartsWith, Prefix, "") {
|
2019-05-24 10:18:39 +00:00
|
|
|
return llvm::StringRef(arg.Name).startswith(Prefix);
|
|
|
|
}
|
2022-04-28 15:13:21 -04:00
|
|
|
MATCHER_P(filterText, F, "") { return arg.FilterText == F; }
|
2022-02-01 10:14:07 +00:00
|
|
|
MATCHER_P(scope, S, "") { return arg.Scope == S; }
|
|
|
|
MATCHER_P(qualifier, Q, "") { return arg.RequiredQualifier == Q; }
|
|
|
|
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
|
|
|
}
|
2022-02-01 10:14:07 +00:00
|
|
|
MATCHER_P(sigHelpLabeled, Label, "") { return arg.label == Label; }
|
|
|
|
MATCHER_P(kind, K, "") { return arg.Kind == K; }
|
|
|
|
MATCHER_P(doc, D, "") {
|
2020-04-30 10:49:32 +02:00
|
|
|
return arg.Documentation && arg.Documentation->asPlainText() == D;
|
|
|
|
}
|
2022-02-01 10:14:07 +00:00
|
|
|
MATCHER_P(returnType, D, "") { return arg.ReturnType == D; }
|
|
|
|
MATCHER_P(hasInclude, 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;
|
|
|
|
}
|
2022-02-01 10:14:07 +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);
|
|
|
|
}
|
2022-12-02 10:04:31 -05:00
|
|
|
MATCHER_P(insertIncludeText, InsertedText, "") {
|
|
|
|
return !arg.Includes.empty() && arg.Includes[0].Insertion &&
|
|
|
|
arg.Includes[0].Insertion->newText == InsertedText;
|
|
|
|
}
|
2022-02-01 10:14:07 +00:00
|
|
|
MATCHER(insertInclude, "") {
|
[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() && bool(arg.Includes[0].Insertion);
|
2017-12-08 15:00:59 +00:00
|
|
|
}
|
2022-02-01 10:14:07 +00:00
|
|
|
MATCHER_P(snippetSuffix, Text, "") { return arg.SnippetSuffix == Text; }
|
|
|
|
MATCHER_P(origin, OriginSet, "") { return arg.Origin == OriginSet; }
|
|
|
|
MATCHER_P(signature, S, "") { return arg.Signature == S; }
|
2022-10-28 16:30:22 +02:00
|
|
|
MATCHER_P(replacesRange, Range, "") {
|
|
|
|
return arg.CompletionTokenRange == Range;
|
|
|
|
}
|
2018-05-15 15:29:32 +00:00
|
|
|
|
2022-02-01 10:14:07 +00:00
|
|
|
// Shorthand for Contains(named(Name)).
|
|
|
|
Matcher<const std::vector<CodeCompletion> &> has(std::string Name) {
|
|
|
|
return Contains(named(std::move(Name)));
|
2017-12-05 07:20:26 +00:00
|
|
|
}
|
2022-02-01 10:14:07 +00:00
|
|
|
Matcher<const std::vector<CodeCompletion> &> has(std::string Name,
|
2017-12-08 15:00:59 +00:00
|
|
|
CompletionItemKind K) {
|
2022-02-01 10:14:07 +00:00
|
|
|
return Contains(AllOf(named(std::move(Name)), kind(K)));
|
2017-12-05 20:11:29 +00:00
|
|
|
}
|
2022-06-25 11:55:33 -07:00
|
|
|
MATCHER(isDocumented, "") { return arg.Documentation.has_value(); }
|
2022-02-01 10:14:07 +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);
|
2019-06-15 02:26:47 +00:00
|
|
|
return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
|
2018-01-18 09:27:56 +00:00
|
|
|
}
|
|
|
|
|
2020-03-31 23:09:28 +02:00
|
|
|
// Runs code completion.
|
|
|
|
// If IndexSymbols is non-empty, an index will be built and passed to opts.
|
|
|
|
CodeCompleteResult completions(const TestTU &TU, Position Point,
|
2018-08-08 08:59:29 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2020-06-04 18:26:52 +02:00
|
|
|
auto Inputs = TU.inputs(FS);
|
2020-03-31 23:09:28 +02:00
|
|
|
IgnoreDiagnostics Diags;
|
|
|
|
auto CI = buildCompilerInvocation(Inputs, Diags);
|
|
|
|
if (!CI) {
|
|
|
|
ADD_FAILURE() << "Couldn't build CompilerInvocation";
|
|
|
|
return {};
|
2018-01-18 09:27:56 +00:00
|
|
|
}
|
2020-03-15 21:43:00 +01:00
|
|
|
auto Preamble = buildPreamble(testPath(TU.Filename), *CI, Inputs,
|
|
|
|
/*InMemory=*/true, /*Callback=*/nullptr);
|
2020-06-04 18:26:52 +02:00
|
|
|
return codeComplete(testPath(TU.Filename), Point, Preamble.get(), Inputs,
|
2020-06-03 10:34:05 +02:00
|
|
|
Opts);
|
2017-12-05 20:11:29 +00:00
|
|
|
}
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2020-03-31 23:09:28 +02:00
|
|
|
// Runs code completion.
|
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") {
|
2020-03-31 23:09:28 +02:00
|
|
|
Annotations Test(Text);
|
|
|
|
auto TU = TestTU::withCode(Test.code());
|
2019-11-20 15:51:18 +01:00
|
|
|
// To make sure our tests for completiopns inside templates work on Windows.
|
2020-03-31 23:09:28 +02:00
|
|
|
TU.Filename = FilePath.str();
|
|
|
|
return completions(TU, Test.point(), std::move(IndexSymbols),
|
|
|
|
std::move(Opts));
|
2018-05-15 15:29:32 +00:00
|
|
|
}
|
|
|
|
|
2020-03-31 23:09:28 +02:00
|
|
|
// Runs code completion without the clang parser.
|
2019-04-11 09:36:36 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2019-04-11 09:36:36 +00:00
|
|
|
Annotations Test(Text);
|
2020-06-04 18:26:52 +02:00
|
|
|
ParseInputs ParseInput{tooling::CompileCommand(), &FS, Test.code().str()};
|
2020-06-03 10:34:05 +02:00
|
|
|
return codeComplete(FilePath, Test.point(), /*Preamble=*/nullptr, ParseInput,
|
|
|
|
Opts);
|
2019-04-11 09:36:36 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2022-12-07 13:50:47 +01:00
|
|
|
#if CLANGD_DECISION_FOREST
|
[clangd] Use Decision Forest to score code completions.
By default clangd will score a code completion item using heuristics model.
Scoring can be done by Decision Forest model by passing `--ranking_model=decision_forest` to
clangd.
Features omitted from the model:
- `NameMatch` is excluded because the final score must be multiplicative in `NameMatch` to allow rescoring by the editor.
- `NeedsFixIts` is excluded because the generating dataset that needs 'fixits' is non-trivial.
There are multiple ways (heuristics) to combine the above two features with the prediction of the DF:
- `NeedsFixIts` is used as is with a penalty of `0.5`.
Various alternatives of combining NameMatch `N` and Decision forest Prediction `P`
- N * scale(P, 0, 1): Linearly scale the output of model to range [0, 1]
- N * a^P:
- More natural: Prediction of each Decision Tree can be considered as a multiplicative boost (like NameMatch)
- Ordering is independent of the absolute value of P. Order of two items is proportional to `a^{difference in model prediction score}`. Higher `a` gives higher weightage to model output as compared to NameMatch score.
Baseline MRR = 0.619
MRR for various combinations:
N * P = 0.6346, advantage%=2.5768
N * 1.1^P = 0.6600, advantage%=6.6853
N * **1.2**^P = 0.6669, advantage%=**7.8005**
N * **1.3**^P = 0.6668, advantage%=**7.7795**
N * **1.4**^P = 0.6659, advantage%=**7.6270**
N * 1.5^P = 0.6646, advantage%=7.4200
N * 1.6^P = 0.6636, advantage%=7.2671
N * 1.7^P = 0.6629, advantage%=7.1450
N * 2^P = 0.6612, advantage%=6.8673
N * 2.5^P = 0.6598, advantage%=6.6491
N * 3^P = 0.6590, advantage%=6.5242
N * scaled[0, 1] = 0.6465, advantage%=4.5054
Differential Revision: https://reviews.llvm.org/D88281
2020-09-22 07:56:08 +02:00
|
|
|
TEST(DecisionForestRankingModel, NameMatchSanityTest) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.RankingModel = CodeCompleteOptions::DecisionForest;
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
struct MemberAccess {
|
|
|
|
int ABG();
|
|
|
|
int AlphaBetaGamma();
|
|
|
|
};
|
|
|
|
int func() { MemberAccess().ABG^ }
|
|
|
|
)cpp",
|
|
|
|
/*IndexSymbols=*/{}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(named("ABG"), named("AlphaBetaGamma")));
|
[clangd] Use Decision Forest to score code completions.
By default clangd will score a code completion item using heuristics model.
Scoring can be done by Decision Forest model by passing `--ranking_model=decision_forest` to
clangd.
Features omitted from the model:
- `NameMatch` is excluded because the final score must be multiplicative in `NameMatch` to allow rescoring by the editor.
- `NeedsFixIts` is excluded because the generating dataset that needs 'fixits' is non-trivial.
There are multiple ways (heuristics) to combine the above two features with the prediction of the DF:
- `NeedsFixIts` is used as is with a penalty of `0.5`.
Various alternatives of combining NameMatch `N` and Decision forest Prediction `P`
- N * scale(P, 0, 1): Linearly scale the output of model to range [0, 1]
- N * a^P:
- More natural: Prediction of each Decision Tree can be considered as a multiplicative boost (like NameMatch)
- Ordering is independent of the absolute value of P. Order of two items is proportional to `a^{difference in model prediction score}`. Higher `a` gives higher weightage to model output as compared to NameMatch score.
Baseline MRR = 0.619
MRR for various combinations:
N * P = 0.6346, advantage%=2.5768
N * 1.1^P = 0.6600, advantage%=6.6853
N * **1.2**^P = 0.6669, advantage%=**7.8005**
N * **1.3**^P = 0.6668, advantage%=**7.7795**
N * **1.4**^P = 0.6659, advantage%=**7.6270**
N * 1.5^P = 0.6646, advantage%=7.4200
N * 1.6^P = 0.6636, advantage%=7.2671
N * 1.7^P = 0.6629, advantage%=7.1450
N * 2^P = 0.6612, advantage%=6.8673
N * 2.5^P = 0.6598, advantage%=6.6491
N * 3^P = 0.6590, advantage%=6.5242
N * scaled[0, 1] = 0.6465, advantage%=4.5054
Differential Revision: https://reviews.llvm.org/D88281
2020-09-22 07:56:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(DecisionForestRankingModel, ReferencesAffectRanking) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.RankingModel = CodeCompleteOptions::DecisionForest;
|
|
|
|
constexpr int NumReferences = 100000;
|
|
|
|
EXPECT_THAT(
|
|
|
|
completions("int main() { clang^ }",
|
|
|
|
{ns("clangA"), withReferences(NumReferences, func("clangD"))},
|
|
|
|
Opts)
|
|
|
|
.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(named("clangD"), named("clangA")));
|
[clangd] Use Decision Forest to score code completions.
By default clangd will score a code completion item using heuristics model.
Scoring can be done by Decision Forest model by passing `--ranking_model=decision_forest` to
clangd.
Features omitted from the model:
- `NameMatch` is excluded because the final score must be multiplicative in `NameMatch` to allow rescoring by the editor.
- `NeedsFixIts` is excluded because the generating dataset that needs 'fixits' is non-trivial.
There are multiple ways (heuristics) to combine the above two features with the prediction of the DF:
- `NeedsFixIts` is used as is with a penalty of `0.5`.
Various alternatives of combining NameMatch `N` and Decision forest Prediction `P`
- N * scale(P, 0, 1): Linearly scale the output of model to range [0, 1]
- N * a^P:
- More natural: Prediction of each Decision Tree can be considered as a multiplicative boost (like NameMatch)
- Ordering is independent of the absolute value of P. Order of two items is proportional to `a^{difference in model prediction score}`. Higher `a` gives higher weightage to model output as compared to NameMatch score.
Baseline MRR = 0.619
MRR for various combinations:
N * P = 0.6346, advantage%=2.5768
N * 1.1^P = 0.6600, advantage%=6.6853
N * **1.2**^P = 0.6669, advantage%=**7.8005**
N * **1.3**^P = 0.6668, advantage%=**7.7795**
N * **1.4**^P = 0.6659, advantage%=**7.6270**
N * 1.5^P = 0.6646, advantage%=7.4200
N * 1.6^P = 0.6636, advantage%=7.2671
N * 1.7^P = 0.6629, advantage%=7.1450
N * 2^P = 0.6612, advantage%=6.8673
N * 2.5^P = 0.6598, advantage%=6.6491
N * 3^P = 0.6590, advantage%=6.5242
N * scaled[0, 1] = 0.6465, advantage%=4.5054
Differential Revision: https://reviews.llvm.org/D88281
2020-09-22 07:56:08 +02:00
|
|
|
EXPECT_THAT(
|
|
|
|
completions("int main() { clang^ }",
|
|
|
|
{withReferences(NumReferences, ns("clangA")), func("clangD")},
|
|
|
|
Opts)
|
|
|
|
.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(named("clangA"), named("clangD")));
|
2020-09-19 10:07:34 +02:00
|
|
|
}
|
2022-12-07 13:50:47 +01:00
|
|
|
#endif // CLANGD_DECISION_FOREST
|
2020-09-19 10:07:34 +02:00
|
|
|
|
2020-10-23 10:19:53 +02:00
|
|
|
TEST(DecisionForestRankingModel, DecisionForestScorerCallbackTest) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
2020-10-30 11:36:27 +00:00
|
|
|
constexpr float MagicNumber = 1234.5678f;
|
2020-10-23 10:19:53 +02:00
|
|
|
Opts.RankingModel = CodeCompleteOptions::DecisionForest;
|
2020-10-30 11:36:27 +00:00
|
|
|
Opts.DecisionForestScorer = [&](const SymbolQualitySignals &,
|
|
|
|
const SymbolRelevanceSignals &, float Base) {
|
2020-10-23 10:19:53 +02:00
|
|
|
DecisionForestScores Scores;
|
|
|
|
Scores.Total = MagicNumber;
|
|
|
|
Scores.ExcludingName = MagicNumber;
|
|
|
|
return Scores;
|
|
|
|
};
|
|
|
|
llvm::StringRef Code = "int func() { int xyz; xy^ }";
|
|
|
|
auto Results = completions(Code,
|
|
|
|
/*IndexSymbols=*/{}, Opts);
|
|
|
|
ASSERT_EQ(Results.Completions.size(), 1u);
|
|
|
|
EXPECT_EQ(Results.Completions[0].Score.Total, MagicNumber);
|
|
|
|
EXPECT_EQ(Results.Completions[0].Score.ExcludingName, MagicNumber);
|
|
|
|
|
|
|
|
// Do not use DecisionForestScorer for heuristics model.
|
|
|
|
Opts.RankingModel = CodeCompleteOptions::Heuristics;
|
|
|
|
Results = completions(Code,
|
|
|
|
/*IndexSymbols=*/{}, Opts);
|
|
|
|
ASSERT_EQ(Results.Completions.size(), 1u);
|
|
|
|
EXPECT_NE(Results.Completions[0].Score.Total, MagicNumber);
|
|
|
|
EXPECT_NE(Results.Completions[0].Score.ExcludingName, MagicNumber);
|
|
|
|
}
|
|
|
|
|
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);
|
2022-02-01 10:14:07 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(has("FooBar"), has("FooBaz"), Not(has("Qux"))));
|
2017-12-05 07:20:26 +00:00
|
|
|
|
2020-08-11 13:32:00 +02:00
|
|
|
// Macros require prefix match, either from index or AST.
|
|
|
|
Symbol Sym = var("MotorCarIndex");
|
|
|
|
Sym.SymInfo.Kind = index::SymbolKind::Macro;
|
|
|
|
EXPECT_THAT(
|
|
|
|
completions(Body + "int main() { C^ }", {Sym}).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(has("Car"), Not(has("MotorCar")), Not(has("MotorCarIndex"))));
|
2020-08-11 13:32:00 +02:00
|
|
|
EXPECT_THAT(completions(Body + "int main() { M^ }", {Sym}).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(has("MotorCar"), has("MotorCarIndex")));
|
2017-12-05 20:11:29 +00:00
|
|
|
}
|
|
|
|
|
2019-10-24 15:40:23 +02: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 {
|
2022-02-01 10:14:07 +00:00
|
|
|
/// doc for method.
|
2017-12-08 15:00:59 +00:00
|
|
|
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
|
|
|
|
2022-02-01 10:14:07 +00:00
|
|
|
/// doc for local_var.
|
2017-12-08 15:00:59 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(has("method"), has("field"), Not(has("ClassWithMembers")),
|
|
|
|
Not(has("operator=")), Not(has("~ClassWithMembers"))));
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_IFF(Opts.IncludeIneligibleResults, Results.Completions,
|
2022-02-01 10:14:07 +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,
|
2022-02-01 10:14:07 +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,
|
2022-02-01 10:14:07 +00:00
|
|
|
Not(Contains(kind(CompletionItemKind::Snippet))));
|
2017-12-05 20:11:29 +00:00
|
|
|
// Check documentation.
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Results.Completions, Contains(isDocumented()));
|
2017-12-05 20:11:29 +00:00
|
|
|
}
|
|
|
|
|
2019-10-24 15:40:23 +02: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 {
|
2022-02-01 10:14:07 +00:00
|
|
|
/// doc for method.
|
2017-12-08 15:00:59 +00:00
|
|
|
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
|
|
|
|
2022-02-01 10:14:07 +00:00
|
|
|
/// doc for local_var.
|
2017-12-08 15:00:59 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
Not(AnyOf(has("method"), has("method()"), has("field"))));
|
2017-12-05 20:11:29 +00:00
|
|
|
// Global items.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(has("global_var"), has("index_var"), has("global_func"),
|
|
|
|
has("index_func" /* our fake symbol doesn't include () */),
|
|
|
|
has("GlobalClass"), has("IndexClass")));
|
2017-12-05 20:11:29 +00:00
|
|
|
// A macro.
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(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,
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(has("local_var"), has("LocalClass"),
|
|
|
|
Contains(kind(CompletionItemKind::Snippet))));
|
2017-12-05 20:11:29 +00:00
|
|
|
// Check documentation.
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(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) {
|
2019-10-24 15:40:23 +02:00
|
|
|
testAfterDotCompletion(Opts);
|
|
|
|
testGlobalScopeCompletion(Opts);
|
2018-01-16 12:21:24 +00:00
|
|
|
};
|
|
|
|
// 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::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,
|
2022-02-01 10:14:07 +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,
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(has("pub"), Not(has("prot")), Not(has("priv"))));
|
2022-06-15 10:51:12 +02:00
|
|
|
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
struct Foo {
|
|
|
|
public: void pub();
|
|
|
|
protected: void prot();
|
|
|
|
private: void priv();
|
|
|
|
};
|
|
|
|
struct Bar : public Foo {
|
|
|
|
private: using Foo::pub;
|
|
|
|
};
|
|
|
|
void test() {
|
|
|
|
Bar B;
|
|
|
|
B.^
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
AllOf(Not(has("priv")), Not(has("prot")), Not(has("pub"))));
|
2017-12-08 15:00:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(qualifier(""), named("bar"))));
|
2018-10-24 13:51:44 +00:00
|
|
|
// Hidden members are not shown.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Not(Contains(AllOf(qualifier("Foo::"), named("foo")))));
|
2018-10-24 13:51:44 +00:00
|
|
|
// Private members are not shown.
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Not(Contains(AllOf(qualifier(""), named("foo")))));
|
2017-12-08 15:00:59 +00:00
|
|
|
}
|
|
|
|
|
2023-01-29 17:02:56 +01:00
|
|
|
// https://github.com/clangd/clangd/issues/1451
|
|
|
|
TEST(CompletionTest, QualificationWithInlineNamespace) {
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
namespace a { inline namespace b {} }
|
|
|
|
using namespace a::b;
|
|
|
|
void f() { Foo^ }
|
|
|
|
)cpp",
|
|
|
|
{cls("a::Foo")});
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(qualifier("a::"), named("Foo"))));
|
|
|
|
}
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
Not(has("X")));
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(completions("struct X{ void foo(){ this->^ } };").Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Not(has("X")));
|
2018-06-07 12:49:17 +00:00
|
|
|
// ...but accessible in other, more useful cases.
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(completions("struct X{ void foo(){ ^ } };").Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
has("X"));
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(
|
|
|
|
completions("struct Y{}; struct X:Y{ void foo(){ ^ } };").Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
has("Y"));
|
2018-06-07 12:49:17 +00:00
|
|
|
// 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,
|
2022-02-01 10:14:07 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(named("X"), named("~X")));
|
2018-11-30 11:12:40 +00:00
|
|
|
}
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
HasSubsequence(named("a"),
|
|
|
|
snippetSuffix("(${1:int i}, ${2:const float f})")));
|
2017-12-08 15:00:59 +00:00
|
|
|
}
|
|
|
|
|
2022-11-17 00:37:15 +01:00
|
|
|
TEST(CompletionTest, HeuristicsForMemberFunctionCompletion) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.EnableSnippets = true;
|
|
|
|
|
|
|
|
Annotations Code(R"cpp(
|
|
|
|
struct Foo {
|
|
|
|
static int staticMethod();
|
|
|
|
int method() const;
|
|
|
|
Foo() {
|
|
|
|
this->$keepSnippet^
|
|
|
|
$keepSnippet^
|
|
|
|
Foo::$keepSnippet^
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Derived : Foo {
|
|
|
|
Derived() {
|
|
|
|
Foo::$keepSnippet^
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct OtherClass {
|
|
|
|
OtherClass() {
|
|
|
|
Foo f;
|
|
|
|
f.$keepSnippet^
|
|
|
|
&Foo::$noSnippet^
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
Foo f;
|
|
|
|
f.$keepSnippet^
|
|
|
|
&Foo::$noSnippet^
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
auto TU = TestTU::withCode(Code.code());
|
|
|
|
|
|
|
|
for (const auto &P : Code.points("noSnippet")) {
|
|
|
|
auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
Contains(AllOf(named("method"), snippetSuffix(""))));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &P : Code.points("keepSnippet")) {
|
|
|
|
auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
Contains(AllOf(named("method"), snippetSuffix("()"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
// static method will always keep the snippet
|
|
|
|
for (const auto &P : Code.points()) {
|
|
|
|
auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
Contains(AllOf(named("staticMethod"), snippetSuffix("()"))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 09:34:21 +01:00
|
|
|
TEST(CompletionTest, NoSnippetsInUsings) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.EnableSnippets = true;
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
namespace ns {
|
|
|
|
int func(int a, int b);
|
|
|
|
}
|
|
|
|
|
|
|
|
using ns::^;
|
|
|
|
)cpp",
|
|
|
|
/*IndexSymbols=*/{}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(AllOf(named("func"), labeled("func(int a, int b)"),
|
|
|
|
snippetSuffix(""))));
|
2019-10-28 09:34:21 +01:00
|
|
|
|
|
|
|
// Check index completions too.
|
|
|
|
auto Func = func("ns::func");
|
|
|
|
Func.CompletionSnippetSuffix = "(${1:int a}, ${2: int b})";
|
|
|
|
Func.Signature = "(int a, int b)";
|
|
|
|
Func.ReturnType = "void";
|
|
|
|
|
|
|
|
Results = completions(R"cpp(
|
|
|
|
namespace ns {}
|
|
|
|
using ns::^;
|
|
|
|
)cpp",
|
|
|
|
/*IndexSymbols=*/{Func}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(AllOf(named("func"), labeled("func(int a, int b)"),
|
|
|
|
snippetSuffix(""))));
|
2019-10-28 09:34:21 +01:00
|
|
|
|
|
|
|
// Check all-scopes completions too.
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
Results = completions(R"cpp(
|
|
|
|
using ^;
|
|
|
|
)cpp",
|
|
|
|
/*IndexSymbols=*/{Func}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(named("func"), labeled("ns::func(int a, int b)"),
|
|
|
|
snippetSuffix(""))));
|
2019-10-28 09:34:21 +01:00
|
|
|
}
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(has("function", CompletionItemKind::Function),
|
|
|
|
has("variable", CompletionItemKind::Variable),
|
|
|
|
has("int", CompletionItemKind::Keyword),
|
|
|
|
has("Struct", CompletionItemKind::Struct),
|
2023-02-24 01:17:44 -05:00
|
|
|
has("MACRO", CompletionItemKind::Constant),
|
2022-02-01 10:14:07 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(named("a"), kind(CompletionItemKind::Field))));
|
2019-05-29 17:49:30 +00:00
|
|
|
|
|
|
|
// Completion kinds for templates should not be unknown.
|
|
|
|
Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
template <class T> struct complete_class {};
|
|
|
|
template <class T> void complete_function();
|
|
|
|
template <class T> using complete_type_alias = int;
|
|
|
|
template <class T> int complete_variable = 10;
|
|
|
|
|
|
|
|
struct X {
|
|
|
|
template <class T> static int complete_static_member = 10;
|
|
|
|
|
|
|
|
static auto x = complete_^
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
UnorderedElementsAre(
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(named("complete_class"), kind(CompletionItemKind::Class)),
|
|
|
|
AllOf(named("complete_function"), kind(CompletionItemKind::Function)),
|
|
|
|
AllOf(named("complete_type_alias"),
|
|
|
|
kind(CompletionItemKind::Interface)),
|
|
|
|
AllOf(named("complete_variable"), kind(CompletionItemKind::Variable)),
|
|
|
|
AllOf(named("complete_static_member"),
|
|
|
|
kind(CompletionItemKind::Property))));
|
2020-02-14 14:55:51 +01:00
|
|
|
|
2020-03-15 21:43:00 +01:00
|
|
|
Results = completions(
|
2020-02-14 14:55:51 +01:00
|
|
|
R"cpp(
|
|
|
|
enum Color {
|
|
|
|
Red
|
|
|
|
};
|
|
|
|
Color u = ^
|
|
|
|
)cpp");
|
2020-03-15 21:43:00 +01:00
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(named("Red"), kind(CompletionItemKind::EnumMember))));
|
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'.
|
2022-02-01 10:14:07 +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,
|
2022-02-01 10:14:07 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
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")});
|
2022-02-01 10:14:07 +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) {
|
2021-03-02 16:36:11 +01:00
|
|
|
EXPECT_THAT(completions("int main() { abs^ }", {func("absA"), func("absB")})
|
|
|
|
.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
HasSubsequence(named("absA"), named("absB")));
|
2021-03-02 16:36:11 +01:00
|
|
|
EXPECT_THAT(completions("int main() { abs^ }",
|
|
|
|
{func("absA"), withReferences(1000, func("absB"))})
|
|
|
|
.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
HasSubsequence(named("absB"), named("absA")));
|
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).
|
2020-04-05 15:28:11 +09:00
|
|
|
// But the recent mention should bump it up.
|
2019-05-06 10:25:10 +00:00
|
|
|
ASSERT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
HasSubsequence(named("YELLOW"), named("BLUE")));
|
2019-05-06 10:25:10 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 14:34:02 +00:00
|
|
|
TEST(CompletionTest, GlobalQualified) {
|
2018-01-18 09:27:56 +00:00
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
void f() { ::^ }
|
|
|
|
)cpp",
|
|
|
|
{cls("XYZ")});
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
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(
|
2022-02-01 10:14:07 +00:00
|
|
|
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) {
|
2020-03-31 23:09:28 +02:00
|
|
|
TestTU TU;
|
|
|
|
TU.ExtraArgs.push_back("-I" + testPath("sub"));
|
|
|
|
TU.AdditionalFiles["sub/bar.h"] = "";
|
|
|
|
auto BarURI = URI::create(testPath("sub/bar.h")).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();
|
2022-12-02 10:04:31 -05:00
|
|
|
Sym.IncludeHeaders.emplace_back(BarURI, 1, Symbol::Include);
|
2020-04-05 15:28:11 +09:00
|
|
|
// Shorten include path based on search directory and insert.
|
2020-03-31 23:09:28 +02:00
|
|
|
Annotations Test("int main() { ns::^ }");
|
|
|
|
TU.Code = Test.code().str();
|
|
|
|
auto Results = completions(TU, Test.point(), {Sym});
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
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;
|
2020-03-31 23:09:28 +02:00
|
|
|
Results = completions(TU, Test.point(), {Sym}, NoInsertion);
|
2019-04-10 12:15:35 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(AllOf(named("X"), Not(insertInclude()))));
|
2018-05-15 15:29:32 +00:00
|
|
|
// Duplicate based on inclusions in preamble.
|
2020-03-31 23:09:28 +02:00
|
|
|
Test = Annotations(R"cpp(
|
2018-05-15 15:29:32 +00:00
|
|
|
#include "sub/bar.h" // not shortest, so should only match resolved.
|
|
|
|
int main() { ns::^ }
|
2020-03-31 23:09:28 +02:00
|
|
|
)cpp");
|
|
|
|
TU.Code = Test.code().str();
|
|
|
|
Results = completions(TU, Test.point(), {Sym});
|
2022-02-01 10:14:07 +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) {
|
|
|
|
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();
|
2022-12-02 10:04:31 -05:00
|
|
|
SymX.IncludeHeaders.emplace_back("<bar>", 1, Symbol::Include);
|
|
|
|
SymY.IncludeHeaders.emplace_back("<bar>", 1, Symbol::Include);
|
2020-04-05 15:28:11 +09:00
|
|
|
// Shorten include path based on search directory and insert.
|
2020-03-31 23:09:28 +02:00
|
|
|
auto Results = completions(R"cpp(
|
2018-05-30 09:03:39 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
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) {
|
|
|
|
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");
|
2020-03-31 23:09:28 +02:00
|
|
|
auto TU = TestTU::withCode(Test.code());
|
|
|
|
TU.AdditionalFiles["bar.h"] =
|
|
|
|
R"cpp(namespace ns { struct preamble { int member; }; })cpp";
|
2018-01-12 18:30:08 +00:00
|
|
|
|
2020-03-31 23:09:28 +02: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();
|
2020-03-31 23:09:28 +02:00
|
|
|
auto WithIndex = completions(TU, Test.point(), {}, Opts);
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(WithIndex.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(named("local"), named("index")));
|
2020-03-31 23:09:28 +02:00
|
|
|
auto ClassFromPreamble = completions(TU, Test.point("2"), {}, Opts);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(ClassFromPreamble.Completions, Contains(named("member")));
|
2018-02-13 08:59:23 +00:00
|
|
|
|
|
|
|
Opts.Index = nullptr;
|
2020-03-31 23:09:28 +02:00
|
|
|
auto WithoutIndex = completions(TU, Test.point(), {}, Opts);
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(WithoutIndex.Completions,
|
2022-02-01 10:14:07 +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;
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Results, ElementsAre(named("ifndef")));
|
2018-09-14 19:42:37 +00:00
|
|
|
}
|
2018-09-14 12:36:06 +00:00
|
|
|
|
2020-06-03 10:34:05 +02:00
|
|
|
TEST(CompletionTest, CompletionRecoveryASTType) {
|
2020-05-19 15:21:50 +02:00
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
struct S { int member; };
|
|
|
|
S overloaded(int);
|
|
|
|
void foo() {
|
|
|
|
// No overload matches, but we have recovery-expr with the correct type.
|
|
|
|
overloaded().^
|
|
|
|
})cpp")
|
|
|
|
.Completions;
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Results, ElementsAre(named("member")));
|
2020-05-19 15:21:50 +02:00
|
|
|
}
|
|
|
|
|
2019-02-04 16:19:57 +00:00
|
|
|
TEST(CompletionTest, DynamicIndexIncludeInsertion) {
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2019-02-04 16:19:57 +00:00
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
ClangdServer::Options Opts = ClangdServer::optsForTest();
|
|
|
|
Opts.BuildDynamicSymbolIndex = true;
|
2020-01-24 14:08:56 +01:00
|
|
|
ClangdServer Server(CDB, FS, Opts);
|
2019-02-04 16:19:57 +00:00
|
|
|
|
|
|
|
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());
|
2020-03-31 23:09:28 +02:00
|
|
|
|
|
|
|
auto File = testPath("foo.cpp");
|
|
|
|
Annotations Test("Foo^ foo;");
|
|
|
|
runAddDocument(Server, File, Test.code());
|
|
|
|
auto CompletionList =
|
|
|
|
llvm::cantFail(runCodeComplete(Server, File, Test.point(), {}));
|
|
|
|
|
|
|
|
EXPECT_THAT(CompletionList.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(AllOf(named("Foo"), hasInclude("\"foo_header.h\""),
|
|
|
|
insertInclude())));
|
2019-02-04 16:19:57 +00:00
|
|
|
}
|
|
|
|
|
2018-01-18 09:27:56 +00:00
|
|
|
TEST(CompletionTest, DynamicIndexMultiFile) {
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2017-12-19 18:00:37 +00:00
|
|
|
MockCompilationDatabase CDB;
|
2018-03-05 17:28:54 +00:00
|
|
|
auto Opts = ClangdServer::optsForTest();
|
|
|
|
Opts.BuildDynamicSymbolIndex = true;
|
2020-01-24 14:08:56 +01:00
|
|
|
ClangdServer Server(CDB, FS, 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.
|
2022-02-01 10:14:07 +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));
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
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.
|
2021-08-13 00:15:03 +02:00
|
|
|
__attribute__((annotate("custom_annotation"))) int foo();
|
2018-05-16 12:32:49 +00:00
|
|
|
/// 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,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(
|
|
|
|
named("foo"),
|
|
|
|
doc("Annotation: custom_annotation\nNon-doxygen comment."))));
|
2018-05-16 12:32:49 +00:00
|
|
|
EXPECT_THAT(
|
2018-07-02 11:13:16 +00:00
|
|
|
Results.Completions,
|
2022-02-01 10:14:07 +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,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(named("baz"), doc("Multi-line block comment"))));
|
2018-05-16 12:32:49 +00:00
|
|
|
}
|
|
|
|
|
2019-11-07 09:59:36 +01:00
|
|
|
TEST(CompletionTest, CommentsFromSystemHeaders) {
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2019-11-07 09:59:36 +01:00
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
|
|
|
|
auto Opts = ClangdServer::optsForTest();
|
|
|
|
Opts.BuildDynamicSymbolIndex = true;
|
|
|
|
|
2020-01-24 14:08:56 +01:00
|
|
|
ClangdServer Server(CDB, FS, Opts);
|
2019-11-07 09:59:36 +01:00
|
|
|
|
|
|
|
FS.Files[testPath("foo.h")] = R"cpp(
|
|
|
|
#pragma GCC system_header
|
|
|
|
|
|
|
|
// This comment should be retained!
|
|
|
|
int foo();
|
|
|
|
)cpp";
|
|
|
|
|
2020-03-31 23:09:28 +02:00
|
|
|
auto File = testPath("foo.cpp");
|
|
|
|
Annotations Test(R"cpp(
|
2019-11-07 09:59:36 +01:00
|
|
|
#include "foo.h"
|
|
|
|
int x = foo^
|
|
|
|
)cpp");
|
2020-03-31 23:09:28 +02:00
|
|
|
runAddDocument(Server, File, Test.code());
|
|
|
|
auto CompletionList =
|
|
|
|
llvm::cantFail(runCodeComplete(Server, File, Test.point(), {}));
|
|
|
|
|
2019-11-07 09:59:36 +01:00
|
|
|
EXPECT_THAT(
|
2020-03-31 23:09:28 +02:00
|
|
|
CompletionList.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(named("foo"), doc("This comment should be retained!"))));
|
2019-11-07 09:59:36 +01:00
|
|
|
}
|
|
|
|
|
[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");
|
|
|
|
|
2022-02-01 10:14:07 +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
|
|
|
}
|
|
|
|
|
2022-09-18 18:48:11 +02:00
|
|
|
TEST(CompletionTests, EmptySnippetDoesNotCrash) {
|
|
|
|
// See https://github.com/clangd/clangd/issues/1216
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
int main() {
|
|
|
|
auto w = [&](auto &&f) { return f(f); };
|
|
|
|
auto f = w([&](auto &&f) {
|
|
|
|
return [&](auto &&n) {
|
|
|
|
if (n == 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return n * ^(f)(n - 1);
|
|
|
|
};
|
|
|
|
})(10);
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
}
|
|
|
|
|
2022-12-20 04:31:33 -05:00
|
|
|
TEST(CompletionTest, Issue1427Crash) {
|
|
|
|
// Need to provide main file signals to ensure that the branch in
|
|
|
|
// SymbolRelevanceSignals::computeASTSignals() that tries to
|
|
|
|
// compute a symbol ID is taken.
|
|
|
|
ASTSignals MainFileSignals;
|
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.MainFileSignals = &MainFileSignals;
|
|
|
|
completions(R"cpp(
|
|
|
|
auto f = []() {
|
|
|
|
1.0_^
|
|
|
|
};
|
|
|
|
)cpp",
|
|
|
|
{}, Opts);
|
|
|
|
}
|
|
|
|
|
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");
|
|
|
|
|
2022-02-01 10:14:07 +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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(named("X"), named("Y")));
|
2018-05-24 11:20:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
2022-02-01 10:14:07 +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
|
2020-04-05 15:28:11 +09:00
|
|
|
// In recovery mode, "param_in_foo" will also be suggested among many other
|
2018-07-11 13:15:31 +00:00
|
|
|
// 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-08-07 16:52:21 +00:00
|
|
|
|
|
|
|
TEST(CompletionTest, DefaultArgs) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
std::string Context = R"cpp(
|
|
|
|
int X(int A = 0);
|
|
|
|
int Y(int A, int B = 0);
|
|
|
|
int Z(int A, int B = 0, int C = 0, int D = 0);
|
|
|
|
)cpp";
|
|
|
|
EXPECT_THAT(completions(Context + "int y = X^", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(labeled("X(int A = 0)")));
|
2019-08-07 16:52:21 +00:00
|
|
|
EXPECT_THAT(completions(Context + "int y = Y^", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(labeled("Y(int A, int B = 0)"),
|
|
|
|
snippetSuffix("(${1:int A})"))));
|
2019-08-07 16:52:21 +00:00
|
|
|
EXPECT_THAT(completions(Context + "int y = Z^", {}, Opts).Completions,
|
|
|
|
UnorderedElementsAre(
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(labeled("Z(int A, int B = 0, int C = 0, int D = 0)"),
|
|
|
|
snippetSuffix("(${1:int A})"))));
|
2019-08-07 16:52:21 +00:00
|
|
|
}
|
|
|
|
|
2019-11-20 15:51:18 +01:00
|
|
|
TEST(CompletionTest, NoCrashWithTemplateParamsAndPreferredTypes) {
|
|
|
|
auto Completions = completions(R"cpp(
|
|
|
|
template <template <class> class TT> int foo() {
|
|
|
|
int a = ^
|
|
|
|
}
|
|
|
|
)cpp")
|
|
|
|
.Completions;
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Completions, Contains(named("TT")));
|
2019-11-20 15:51:18 +01:00
|
|
|
}
|
|
|
|
|
2021-02-10 00:11:35 +01:00
|
|
|
TEST(CompletionTest, NestedTemplateHeuristics) {
|
|
|
|
auto Completions = completions(R"cpp(
|
|
|
|
struct Plain { int xxx; };
|
|
|
|
template <typename T> class Templ { Plain ppp; };
|
|
|
|
template <typename T> void foo(Templ<T> &t) {
|
|
|
|
// Formally ppp has DependentTy, because Templ may be specialized.
|
|
|
|
// However we sholud be able to see into it using the primary template.
|
|
|
|
t.ppp.^
|
|
|
|
}
|
|
|
|
)cpp")
|
|
|
|
.Completions;
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Completions, Contains(named("xxx")));
|
2021-02-10 00:11:35 +01:00
|
|
|
}
|
|
|
|
|
2020-03-04 13:25:02 +01:00
|
|
|
TEST(CompletionTest, RecordCCResultCallback) {
|
|
|
|
std::vector<CodeCompletion> RecordedCompletions;
|
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.RecordCCResult = [&RecordedCompletions](const CodeCompletion &CC,
|
|
|
|
const SymbolQualitySignals &,
|
|
|
|
const SymbolRelevanceSignals &,
|
|
|
|
float Score) {
|
|
|
|
RecordedCompletions.push_back(CC);
|
|
|
|
};
|
|
|
|
|
|
|
|
completions("int xy1, xy2; int a = xy^", /*IndexSymbols=*/{}, Opts);
|
|
|
|
EXPECT_THAT(RecordedCompletions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(named("xy1"), named("xy2")));
|
2020-03-04 13:25:02 +01:00
|
|
|
}
|
|
|
|
|
2021-01-10 16:32:00 +01:00
|
|
|
TEST(CompletionTest, ASTSignals) {
|
|
|
|
struct Completion {
|
|
|
|
std::string Name;
|
|
|
|
unsigned MainFileRefs;
|
|
|
|
unsigned ScopeRefsInFile;
|
|
|
|
};
|
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
std::vector<Completion> RecordedCompletions;
|
|
|
|
Opts.RecordCCResult = [&RecordedCompletions](const CodeCompletion &CC,
|
|
|
|
const SymbolQualitySignals &,
|
|
|
|
const SymbolRelevanceSignals &R,
|
|
|
|
float Score) {
|
|
|
|
RecordedCompletions.push_back({CC.Name, R.MainFileRefs, R.ScopeRefsInFile});
|
|
|
|
};
|
|
|
|
ASTSignals MainFileSignals;
|
|
|
|
MainFileSignals.ReferencedSymbols[var("xy1").ID] = 3;
|
|
|
|
MainFileSignals.ReferencedSymbols[var("xy2").ID] = 1;
|
|
|
|
MainFileSignals.ReferencedSymbols[var("xyindex").ID] = 10;
|
|
|
|
MainFileSignals.RelatedNamespaces["tar::"] = 5;
|
|
|
|
MainFileSignals.RelatedNamespaces["bar::"] = 3;
|
|
|
|
Opts.MainFileSignals = &MainFileSignals;
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
completions(
|
|
|
|
R"cpp(
|
|
|
|
int xy1;
|
|
|
|
int xy2;
|
|
|
|
namespace bar {
|
|
|
|
int xybar = 1;
|
|
|
|
int a = xy^
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
/*IndexSymbols=*/{var("xyindex"), var("tar::xytar"), var("bar::xybar")},
|
|
|
|
Opts);
|
|
|
|
EXPECT_THAT(RecordedCompletions,
|
|
|
|
UnorderedElementsAre(
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(named("xy1"), mainFileRefs(3u), scopeRefs(0u)),
|
|
|
|
AllOf(named("xy2"), mainFileRefs(1u), scopeRefs(0u)),
|
|
|
|
AllOf(named("xyindex"), mainFileRefs(10u), scopeRefs(0u)),
|
|
|
|
AllOf(named("xytar"), mainFileRefs(0u), scopeRefs(5u)),
|
|
|
|
AllOf(/*both from sema and index*/ named("xybar"),
|
|
|
|
mainFileRefs(0u), scopeRefs(3u))));
|
2021-01-10 16:32:00 +01:00
|
|
|
}
|
|
|
|
|
2021-12-09 09:36:04 +01:00
|
|
|
SignatureHelp
|
|
|
|
signatures(llvm::StringRef Text, Position Point,
|
|
|
|
std::vector<Symbol> IndexSymbols = {},
|
|
|
|
MarkupKind DocumentationFormat = MarkupKind::PlainText) {
|
2018-08-17 09:32:30 +00:00
|
|
|
std::unique_ptr<SymbolIndex> Index;
|
|
|
|
if (!IndexSymbols.empty())
|
|
|
|
Index = memIndex(IndexSymbols);
|
|
|
|
|
2020-03-31 23:09:28 +02:00
|
|
|
auto TU = TestTU::withCode(Text);
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2020-06-04 18:26:52 +02:00
|
|
|
auto Inputs = TU.inputs(FS);
|
|
|
|
Inputs.Index = Index.get();
|
2020-03-31 23:09:28 +02:00
|
|
|
IgnoreDiagnostics Diags;
|
|
|
|
auto CI = buildCompilerInvocation(Inputs, Diags);
|
|
|
|
if (!CI) {
|
|
|
|
ADD_FAILURE() << "Couldn't build CompilerInvocation";
|
|
|
|
return {};
|
|
|
|
}
|
2020-03-15 21:43:00 +01:00
|
|
|
auto Preamble = buildPreamble(testPath(TU.Filename), *CI, Inputs,
|
|
|
|
/*InMemory=*/true, /*Callback=*/nullptr);
|
2020-04-01 11:31:53 +02:00
|
|
|
if (!Preamble) {
|
|
|
|
ADD_FAILURE() << "Couldn't build Preamble";
|
|
|
|
return {};
|
|
|
|
}
|
2021-12-09 09:36:04 +01:00
|
|
|
return signatureHelp(testPath(TU.Filename), Point, *Preamble, Inputs,
|
|
|
|
DocumentationFormat);
|
2018-08-30 13:14:31 +00:00
|
|
|
}
|
|
|
|
|
2021-12-09 09:36:04 +01:00
|
|
|
SignatureHelp
|
|
|
|
signatures(llvm::StringRef Text, std::vector<Symbol> IndexSymbols = {},
|
|
|
|
MarkupKind DocumentationFormat = MarkupKind::PlainText) {
|
2018-01-18 09:27:56 +00:00
|
|
|
Annotations Test(Text);
|
2021-12-09 09:36:04 +01:00
|
|
|
return signatures(Test.code(), Test.point(), std::move(IndexSymbols),
|
|
|
|
DocumentationFormat);
|
2018-01-18 09:27:56 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 09:36:59 +00:00
|
|
|
struct ExpectedParameter {
|
|
|
|
std::string Text;
|
|
|
|
std::pair<unsigned, unsigned> Offsets;
|
|
|
|
};
|
2021-12-27 20:42:11 +01:00
|
|
|
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
|
|
|
|
const ExpectedParameter &P) {
|
|
|
|
return OS << P.Text;
|
|
|
|
}
|
2022-02-01 10:14:07 +00:00
|
|
|
MATCHER_P(paramsAre, P, "") {
|
2018-01-18 09:27:56 +00:00
|
|
|
if (P.size() != arg.parameters.size())
|
|
|
|
return false;
|
2019-06-04 09:36:59 +00:00
|
|
|
for (unsigned I = 0; I < P.size(); ++I) {
|
|
|
|
if (P[I].Text != arg.parameters[I].labelString ||
|
|
|
|
P[I].Offsets != arg.parameters[I].labelOffsets)
|
2018-01-18 09:27:56 +00:00
|
|
|
return false;
|
2019-06-04 09:36:59 +00:00
|
|
|
}
|
2018-01-18 09:27:56 +00:00
|
|
|
return true;
|
|
|
|
}
|
2022-02-01 10:14:07 +00:00
|
|
|
MATCHER_P(sigDoc, doc, "") { return arg.documentation.value == doc; }
|
2018-01-18 09:27:56 +00:00
|
|
|
|
2019-06-04 09:36:59 +00:00
|
|
|
/// \p AnnotatedLabel is a signature label with ranges marking parameters, e.g.
|
|
|
|
/// foo([[int p1]], [[double p2]]) -> void
|
2022-02-01 10:14:07 +00:00
|
|
|
Matcher<SignatureInformation> sig(llvm::StringRef AnnotatedLabel) {
|
2019-06-04 09:36:59 +00:00
|
|
|
llvm::Annotations A(AnnotatedLabel);
|
2020-01-28 20:23:46 +01:00
|
|
|
std::string Label = std::string(A.code());
|
2019-06-04 09:36:59 +00:00
|
|
|
std::vector<ExpectedParameter> Parameters;
|
|
|
|
for (auto Range : A.ranges()) {
|
|
|
|
Parameters.emplace_back();
|
|
|
|
|
|
|
|
ExpectedParameter &P = Parameters.back();
|
|
|
|
P.Text = Label.substr(Range.Begin, Range.End - Range.Begin);
|
|
|
|
P.Offsets.first = lspLength(llvm::StringRef(Label).substr(0, Range.Begin));
|
|
|
|
P.Offsets.second = lspLength(llvm::StringRef(Label).substr(1, Range.End));
|
|
|
|
}
|
2022-02-01 10:14:07 +00:00
|
|
|
return AllOf(sigHelpLabeled(Label), paramsAre(Parameters));
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(sig("foo([[float x]], [[float y]]) -> void"),
|
|
|
|
sig("foo([[float x]], [[int y]]) -> void"),
|
|
|
|
sig("foo([[int x]], [[float y]]) -> void"),
|
|
|
|
sig("foo([[int x]], [[int y]]) -> void")));
|
2018-01-18 09:27:56 +00:00
|
|
|
// We always prefer the first signature.
|
|
|
|
EXPECT_EQ(0, Results.activeSignature);
|
|
|
|
EXPECT_EQ(0, Results.activeParameter);
|
|
|
|
}
|
|
|
|
|
2022-07-19 20:02:24 -04:00
|
|
|
TEST(SignatureHelpTest, FunctionPointers) {
|
|
|
|
auto FunctionPointerResults = signatures(R"cpp(
|
|
|
|
void (*foo)(int x, int y);
|
|
|
|
int main() { foo(^); }
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(FunctionPointerResults.signatures,
|
|
|
|
UnorderedElementsAre(sig("([[int x]], [[int y]]) -> void")));
|
|
|
|
|
|
|
|
auto FunctionPointerTypedefResults = signatures(R"cpp(
|
|
|
|
typedef void (*fn)(int x, int y);
|
|
|
|
fn foo;
|
|
|
|
int main() { foo(^); }
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(FunctionPointerTypedefResults.signatures,
|
|
|
|
UnorderedElementsAre(sig("([[int x]], [[int y]]) -> void")));
|
|
|
|
}
|
|
|
|
|
2021-12-27 20:42:11 +01:00
|
|
|
TEST(SignatureHelpTest, Constructors) {
|
|
|
|
std::string Top = R"cpp(
|
|
|
|
struct S {
|
|
|
|
S(int);
|
|
|
|
S(const S &) = delete;
|
|
|
|
};
|
|
|
|
)cpp";
|
|
|
|
|
|
|
|
auto CheckParenInit = [&](std::string Init) {
|
|
|
|
EXPECT_THAT(signatures(Top + Init).signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(sig("S([[int]])")))
|
2021-12-27 20:42:11 +01:00
|
|
|
<< Init;
|
|
|
|
};
|
|
|
|
CheckParenInit("S s(^);");
|
|
|
|
CheckParenInit("auto s = S(^);");
|
|
|
|
CheckParenInit("auto s = new S(^);");
|
|
|
|
|
|
|
|
auto CheckBracedInit = [&](std::string Init) {
|
|
|
|
EXPECT_THAT(signatures(Top + Init).signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(sig("S{[[int]]}")))
|
2021-12-27 20:42:11 +01:00
|
|
|
<< Init;
|
|
|
|
};
|
|
|
|
CheckBracedInit("S s{^};");
|
|
|
|
CheckBracedInit("S s = {^};");
|
|
|
|
CheckBracedInit("auto s = S{^};");
|
|
|
|
// FIXME: doesn't work: no ExpectedType set in ParseCXXNewExpression.
|
|
|
|
// CheckBracedInit("auto s = new S{^};");
|
|
|
|
CheckBracedInit("int x(S); int i = x({^});");
|
|
|
|
}
|
|
|
|
|
2021-12-28 03:58:13 +01:00
|
|
|
TEST(SignatureHelpTest, Aggregates) {
|
|
|
|
std::string Top = R"cpp(
|
|
|
|
struct S {
|
|
|
|
int a, b, c, d;
|
|
|
|
};
|
|
|
|
)cpp";
|
2022-02-01 10:14:07 +00:00
|
|
|
auto AggregateSig = sig("S{[[int a]], [[int b]], [[int c]], [[int d]]}");
|
2021-12-28 03:58:13 +01:00
|
|
|
EXPECT_THAT(signatures(Top + "S s{^}").signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AggregateSig, sig("S{}"),
|
|
|
|
sig("S{[[const S &]]}"),
|
|
|
|
sig("S{[[S &&]]}")));
|
2021-12-28 03:58:13 +01:00
|
|
|
EXPECT_THAT(signatures(Top + "S s{1,^}").signatures,
|
|
|
|
ElementsAre(AggregateSig));
|
|
|
|
EXPECT_EQ(signatures(Top + "S s{1,^}").activeParameter, 1);
|
|
|
|
EXPECT_THAT(signatures(Top + "S s{.c=3,^}").signatures,
|
|
|
|
ElementsAre(AggregateSig));
|
|
|
|
EXPECT_EQ(signatures(Top + "S s{.c=3,^}").activeParameter, 3);
|
|
|
|
}
|
|
|
|
|
2021-03-12 12:00:54 +01:00
|
|
|
TEST(SignatureHelpTest, OverloadInitListRegression) {
|
|
|
|
auto Results = signatures(R"cpp(
|
|
|
|
struct A {int x;};
|
|
|
|
struct B {B(A);};
|
|
|
|
void f();
|
|
|
|
int main() {
|
|
|
|
B b({1});
|
|
|
|
f(^);
|
|
|
|
}
|
|
|
|
)cpp");
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Results.signatures, UnorderedElementsAre(sig("f() -> void")));
|
2021-03-12 12:00:54 +01:00
|
|
|
}
|
|
|
|
|
2018-01-18 09:27:56 +00:00
|
|
|
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(
|
2022-02-01 10:14:07 +00:00
|
|
|
sig("bar([[int x]], [[int y = 0]]) -> void"),
|
|
|
|
sig("bar([[float x = 0]], [[int y = 42]]) -> void")));
|
2018-01-18 09:27:56 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(sig("baz([[int a]], [[int b]], [[int c]]) -> int")));
|
2018-01-18 09:27:56 +00:00
|
|
|
EXPECT_EQ(0, Results.activeSignature);
|
|
|
|
EXPECT_EQ(1, Results.activeParameter);
|
|
|
|
}
|
|
|
|
|
2018-08-30 13:14:31 +00:00
|
|
|
TEST(SignatureHelpTest, OpeningParen) {
|
2020-08-11 21:10:57 +02:00
|
|
|
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
|
|
|
|
// FIXME: figure out why ID(foo (foo(10), )) doesn't work when preserving
|
|
|
|
// the recovery expression.
|
|
|
|
ID(foo $p^( 10, ^ ))
|
|
|
|
})cpp",
|
|
|
|
// Dependent args.
|
|
|
|
R"cpp(
|
|
|
|
int foo(int a, int b);
|
|
|
|
template <typename T> void bar(T t) {
|
|
|
|
foo$p^(t, ^t);
|
|
|
|
})cpp",
|
|
|
|
// Dependent args on templated func.
|
|
|
|
R"cpp(
|
|
|
|
template <typename T>
|
|
|
|
int foo(T, T);
|
|
|
|
template <typename T> void bar(T t) {
|
|
|
|
foo$p^(t, ^t);
|
|
|
|
})cpp",
|
|
|
|
// Dependent args on member.
|
|
|
|
R"cpp(
|
|
|
|
struct Foo { int foo(int, int); };
|
|
|
|
template <typename T> void bar(T t) {
|
|
|
|
Foo f;
|
|
|
|
f.foo$p^(t, ^t);
|
|
|
|
})cpp",
|
|
|
|
// Dependent args on templated member.
|
|
|
|
R"cpp(
|
|
|
|
struct Foo { template <typename T> int foo(T, T); };
|
|
|
|
template <typename T> void bar(T t) {
|
|
|
|
Foo f;
|
|
|
|
f.foo$p^(t, ^t);
|
|
|
|
})cpp",
|
|
|
|
};
|
2018-08-30 13:14:31 +00:00
|
|
|
|
|
|
|
for (auto Test : Tests) {
|
|
|
|
Annotations Code(Test);
|
|
|
|
EXPECT_EQ(signatures(Code.code(), Code.point()).argListStart,
|
|
|
|
Code.point("p"))
|
|
|
|
<< "Test source:" << Test;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-02 10:53:23 +02:00
|
|
|
TEST(SignatureHelpTest, StalePreamble) {
|
|
|
|
TestTU TU;
|
|
|
|
TU.Code = "";
|
|
|
|
IgnoreDiagnostics Diags;
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2020-06-04 18:26:52 +02:00
|
|
|
auto Inputs = TU.inputs(FS);
|
2020-04-02 10:53:23 +02:00
|
|
|
auto CI = buildCompilerInvocation(Inputs, Diags);
|
|
|
|
ASSERT_TRUE(CI);
|
|
|
|
auto EmptyPreamble = buildPreamble(testPath(TU.Filename), *CI, Inputs,
|
|
|
|
/*InMemory=*/true, /*Callback=*/nullptr);
|
|
|
|
ASSERT_TRUE(EmptyPreamble);
|
|
|
|
|
|
|
|
TU.AdditionalFiles["a.h"] = "int foo(int x);";
|
|
|
|
const Annotations Test(R"cpp(
|
|
|
|
#include "a.h"
|
|
|
|
void bar() { foo(^2); })cpp");
|
|
|
|
TU.Code = Test.code().str();
|
2021-12-09 09:36:04 +01:00
|
|
|
auto Results =
|
|
|
|
signatureHelp(testPath(TU.Filename), Test.point(), *EmptyPreamble,
|
|
|
|
TU.inputs(FS), MarkupKind::PlainText);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Results.signatures, ElementsAre(sig("foo([[int x]]) -> int")));
|
2020-04-02 10:53:23 +02:00
|
|
|
EXPECT_EQ(0, Results.activeSignature);
|
|
|
|
EXPECT_EQ(0, Results.activeParameter);
|
|
|
|
}
|
|
|
|
|
2018-01-23 11:37:26 +00:00
|
|
|
class IndexRequestCollector : public SymbolIndex {
|
|
|
|
public:
|
2022-08-31 13:33:09 -04:00
|
|
|
IndexRequestCollector(std::vector<Symbol> Syms = {}) : Symbols(Syms) {}
|
|
|
|
|
2019-01-07 15:45:19 +00:00
|
|
|
bool
|
|
|
|
fuzzyFind(const FuzzyFindRequest &Req,
|
|
|
|
llvm::function_ref<void(const Symbol &)> Callback) const override {
|
2019-10-07 17:12:18 +00:00
|
|
|
std::unique_lock<std::mutex> Lock(Mut);
|
2018-01-23 11:37:26 +00:00
|
|
|
Requests.push_back(Req);
|
2019-10-07 17:12:18 +00:00
|
|
|
ReceivedRequestCV.notify_one();
|
2022-08-31 13:33:09 -04:00
|
|
|
for (const auto &Sym : Symbols)
|
|
|
|
Callback(Sym);
|
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
|
|
|
|
2019-11-13 14:42:26 +01:00
|
|
|
bool refs(const RefsRequest &,
|
|
|
|
llvm::function_ref<void(const Ref &)>) const override {
|
|
|
|
return false;
|
|
|
|
}
|
2018-08-06 13:14:32 +00:00
|
|
|
|
2019-06-15 02:26:47 +00:00
|
|
|
void relations(const RelationsRequest &,
|
|
|
|
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
|
|
|
|
const override {}
|
|
|
|
|
2021-02-05 12:32:47 +03:00
|
|
|
llvm::unique_function<IndexContents(llvm::StringRef) const>
|
2020-12-18 15:14:15 +03:00
|
|
|
indexedFiles() const override {
|
2021-02-05 12:32:47 +03:00
|
|
|
return [](llvm::StringRef) { return IndexContents::None; };
|
2020-12-18 15:14:15 +03: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; }
|
|
|
|
|
2019-10-07 17:12:18 +00:00
|
|
|
const std::vector<FuzzyFindRequest> consumeRequests(size_t Num) const {
|
|
|
|
std::unique_lock<std::mutex> Lock(Mut);
|
2019-10-08 13:54:03 +00:00
|
|
|
EXPECT_TRUE(wait(Lock, ReceivedRequestCV, timeoutSeconds(30),
|
2019-10-07 17:12:18 +00:00
|
|
|
[this, Num] { return Requests.size() == Num; }));
|
[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:
|
2022-08-31 13:33:09 -04:00
|
|
|
std::vector<Symbol> Symbols;
|
2018-09-06 11:04:56 +00:00
|
|
|
// We need a mutex to handle async fuzzy find requests.
|
2019-10-07 17:12:18 +00:00
|
|
|
mutable std::condition_variable ReceivedRequestCV;
|
2018-09-06 11:04:56 +00:00
|
|
|
mutable std::mutex Mut;
|
2018-01-23 11:37:26 +00:00
|
|
|
mutable std::vector<FuzzyFindRequest> Requests;
|
|
|
|
};
|
|
|
|
|
2019-10-07 17:12:18 +00:00
|
|
|
// Clients have to consume exactly Num requests.
|
|
|
|
std::vector<FuzzyFindRequest> captureIndexRequests(llvm::StringRef Code,
|
|
|
|
size_t Num = 1) {
|
2018-01-23 11:37:26 +00:00
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
IndexRequestCollector Requests;
|
|
|
|
Opts.Index = &Requests;
|
|
|
|
completions(Code, {}, Opts);
|
2019-10-07 17:12:18 +00:00
|
|
|
const auto Reqs = Requests.consumeRequests(Num);
|
|
|
|
EXPECT_EQ(Reqs.size(), Num);
|
|
|
|
return Reqs;
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(Contains(labeled("SomeNameOfField")),
|
|
|
|
Contains(labeled("SomeNameOfTypedefField")),
|
|
|
|
Not(Contains(labeled("SomeNameInTheIndex")))));
|
2018-05-14 10:50:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
Not(Contains(labeled("SomeNameInTheIndex"))));
|
2018-05-14 10:50:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
Not(Contains(labeled("SomeNameInTheIndex"))));
|
2018-05-14 10:50:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
Not(Contains(labeled("SomeNameInTheIndex"))));
|
2018-05-14 10:50:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[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
|
2021-03-02 22:26:29 +01:00
|
|
|
int a(int) __attribute__((deprecated("", "")));
|
[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
|
|
|
// Overload with bool
|
|
|
|
int a(bool);
|
|
|
|
int b(float);
|
2021-11-12 15:03:23 +01:00
|
|
|
|
|
|
|
X(int);
|
|
|
|
X(float);
|
[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
|
|
|
};
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(labeled("a(…)"), labeled("b(float)")));
|
[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
|
|
|
|
2021-11-12 15:03:23 +01:00
|
|
|
// Constructor completions are bundled.
|
|
|
|
EXPECT_THAT(completions(Context + "X z = X^", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(labeled("X"), labeled("X(…)")));
|
2021-11-12 15:03:23 +01:00
|
|
|
|
[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
|
|
|
// 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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(labeled("GFuncC(…)"), 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
|
|
|
|
|
|
|
// 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();
|
2022-12-02 10:04:31 -05:00
|
|
|
NoArgsGFunc.IncludeHeaders.emplace_back("<foo>", 1, Symbol::Include);
|
[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,
|
2022-02-01 10:14:07 +00:00
|
|
|
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.
|
2020-04-30 10:49:32 +02:00
|
|
|
ASSERT_TRUE(A.Documentation);
|
2021-03-02 22:26:29 +01:00
|
|
|
ASSERT_FALSE(A.Deprecated); // Not all overloads deprecated.
|
2020-04-30 10:49:32 +02:00
|
|
|
EXPECT_THAT(
|
|
|
|
A.Documentation->asPlainText(),
|
|
|
|
AnyOf(HasSubstr("Overload with int"), 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
|
|
|
}
|
|
|
|
|
2020-12-02 18:55:32 +01:00
|
|
|
TEST(CompletionTest, OverloadBundlingSameFileDifferentURI) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.BundleOverloads = true;
|
|
|
|
|
|
|
|
Symbol SymX = sym("ns::X", index::SymbolKind::Function, "@F@\\0#");
|
|
|
|
Symbol SymY = sym("ns::X", index::SymbolKind::Function, "@F@\\0#I#");
|
|
|
|
std::string BarHeader = testPath("bar.h");
|
|
|
|
auto BarURI = URI::create(BarHeader).toString();
|
|
|
|
SymX.CanonicalDeclaration.FileURI = BarURI.c_str();
|
|
|
|
SymY.CanonicalDeclaration.FileURI = BarURI.c_str();
|
|
|
|
// The include header is different, but really it's the same file.
|
2022-12-02 10:04:31 -05:00
|
|
|
SymX.IncludeHeaders.emplace_back("\"bar.h\"", 1, Symbol::Include);
|
|
|
|
SymY.IncludeHeaders.emplace_back(BarURI.c_str(), 1, Symbol::Include);
|
2020-12-02 18:55:32 +01:00
|
|
|
|
|
|
|
auto Results = completions("void f() { ::ns::^ }", {SymX, SymY}, Opts);
|
|
|
|
// Expect both results are bundled, despite the different-but-same
|
|
|
|
// IncludeHeader.
|
|
|
|
ASSERT_EQ(1u, Results.Completions.size());
|
|
|
|
const auto &R = Results.Completions.front();
|
|
|
|
EXPECT_EQ("X", R.Name);
|
|
|
|
EXPECT_EQ(2u, R.BundleSize);
|
|
|
|
}
|
|
|
|
|
2018-05-28 09:54:51 +00:00
|
|
|
TEST(CompletionTest, DocumentationFromChangedFileCrash) {
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2018-05-24 14:49:23 +00:00
|
|
|
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;
|
2020-01-24 14:08:56 +01:00
|
|
|
ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
|
2018-05-24 14:49:23 +00:00
|
|
|
|
|
|
|
Annotations Source(R"cpp(
|
|
|
|
#include "foo.h"
|
|
|
|
int func() {
|
|
|
|
// This makes sure we have func from header in the AST.
|
|
|
|
}
|
|
|
|
int a = fun^
|
|
|
|
)cpp");
|
[clangd] Track document versions, include them with diags, enhance logs
Summary:
This ties to an LSP feature (diagnostic versioning) but really a lot
of the value is in being able to log what's happening with file versions
and queues more descriptively and clearly.
As such it's fairly invasive, for a logging patch :-\
Key decisions:
- at the LSP layer, we don't reqire the client to provide versions (LSP
makes it mandatory but we never enforced it). If not provided,
versions start at 0 and increment. DraftStore handles this.
- don't propagate magically using contexts, but rather manually:
addDocument -> ParseInputs -> (ParsedAST, Preamble, various callbacks)
Context-propagation would hide the versions from ClangdServer, which
would make producing good log messages hard
- within ClangdServer, treat versions as opaque and unordered.
std::string is a convenient type for this, and allows richer versions
for embedders. They're "mandatory" but "null" is a reasonable default.
Subscribers: ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D75582
2020-03-04 00:33:29 +01:00
|
|
|
Server.addDocument(FooCpp, Source.code(), "null", WantDiagnostics::Yes);
|
2018-05-24 14:49:23 +00:00
|
|
|
// 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;
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(Not(isDocumented()), named("func"))));
|
2018-05-24 14:49:23 +00:00
|
|
|
}
|
|
|
|
|
2018-06-15 08:31:17 +00:00
|
|
|
TEST(CompletionTest, NonDocComments) {
|
2020-03-31 23:09:28 +02:00
|
|
|
const char *Text = 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^;
|
|
|
|
}
|
2020-03-31 23:09:28 +02:00
|
|
|
)cpp";
|
2018-06-15 08:31:17 +00:00
|
|
|
|
|
|
|
// We should not get any of those comments in completion.
|
|
|
|
EXPECT_THAT(
|
2020-03-31 23:09:28 +02:00
|
|
|
completions(Text).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(Not(isDocumented()), named("comments_foo")),
|
|
|
|
AllOf(isDocumented(), named("comments_baz")),
|
|
|
|
AllOf(isDocumented(), named("comments_quux")),
|
|
|
|
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.
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(isDocumented(), named("comments_bar")),
|
|
|
|
AllOf(isDocumented(), named("comments_qux"))));
|
2018-06-15 08:31:17 +00:00
|
|
|
}
|
|
|
|
|
2018-05-28 12:11:37 +00:00
|
|
|
TEST(CompletionTest, CompleteOnInvalidLine) {
|
|
|
|
auto FooCpp = testPath("foo.cpp");
|
|
|
|
|
|
|
|
MockCompilationDatabase CDB;
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2018-05-28 12:11:37 +00:00
|
|
|
FS.Files[FooCpp] = "// empty file";
|
|
|
|
|
2020-01-24 14:08:56 +01:00
|
|
|
ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
|
2018-05-28 12:11:37 +00:00
|
|
|
// 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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(scope("ns::"), scope("ns::"), scope("ns::")));
|
2018-07-02 11:13:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, Render) {
|
|
|
|
CodeCompletion C;
|
|
|
|
C.Name = "x";
|
2022-04-28 15:13:21 -04:00
|
|
|
C.FilterText = "x";
|
2018-07-02 11:13:16 +00:00
|
|
|
C.Signature = "(bool) const";
|
|
|
|
C.SnippetSuffix = "(${0:bool})";
|
|
|
|
C.ReturnType = "int";
|
|
|
|
C.RequiredQualifier = "Foo::";
|
|
|
|
C.Scope = "ns::Foo::";
|
2020-04-30 10:49:32 +02:00
|
|
|
C.Documentation.emplace();
|
|
|
|
C.Documentation->addParagraph().appendText("This is ").appendCode("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;
|
2020-02-13 14:17:30 +01:00
|
|
|
C.Score.ExcludingName = .5;
|
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);
|
2023-05-23 22:39:55 +02:00
|
|
|
EXPECT_EQ(R.label, "Foo::x");
|
|
|
|
EXPECT_EQ(R.labelDetails->detail, "(bool) const");
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_EQ(R.insertText, "Foo::x");
|
|
|
|
EXPECT_EQ(R.insertTextFormat, InsertTextFormat::PlainText);
|
|
|
|
EXPECT_EQ(R.filterText, "x");
|
2020-04-29 20:10:50 +02:00
|
|
|
EXPECT_EQ(R.detail, "int");
|
|
|
|
EXPECT_EQ(R.documentation->value, "From \"foo.h\"\nThis is x()");
|
2018-07-02 11:13:16 +00:00
|
|
|
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);
|
2020-02-13 14:17:30 +01:00
|
|
|
EXPECT_EQ(R.score, .5f);
|
2018-07-02 11:13:16 +00:00
|
|
|
|
2022-04-28 15:13:21 -04:00
|
|
|
C.FilterText = "xtra";
|
|
|
|
R = C.render(Opts);
|
|
|
|
EXPECT_EQ(R.filterText, "xtra");
|
|
|
|
EXPECT_EQ(R.sortText, sortText(1.0, "xtra"));
|
|
|
|
|
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);
|
|
|
|
|
2021-11-12 18:42:54 +01:00
|
|
|
C.SnippetSuffix = "";
|
|
|
|
R = C.render(Opts);
|
|
|
|
EXPECT_EQ(R.insertText, "Foo::x");
|
|
|
|
EXPECT_EQ(R.insertTextFormat, InsertTextFormat::PlainText);
|
|
|
|
|
[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);
|
2023-05-23 22:39:55 +02:00
|
|
|
EXPECT_EQ(R.label, "^Foo::x");
|
|
|
|
EXPECT_EQ(R.labelDetails->detail, "(bool) const");
|
2018-07-02 11:13:16 +00:00
|
|
|
EXPECT_THAT(R.additionalTextEdits, Not(IsEmpty()));
|
|
|
|
|
2018-07-05 06:20:41 +00:00
|
|
|
Opts.ShowOrigins = true;
|
|
|
|
R = C.render(Opts);
|
2023-05-23 22:39:55 +02:00
|
|
|
EXPECT_EQ(R.label, "^[AS]Foo::x");
|
|
|
|
EXPECT_EQ(R.labelDetails->detail, "(bool) const");
|
2018-07-05 06:20:41 +00:00
|
|
|
|
2018-07-02 11:13:16 +00:00
|
|
|
C.BundleSize = 2;
|
|
|
|
R = C.render(Opts);
|
2020-04-29 20:10:50 +02:00
|
|
|
EXPECT_EQ(R.detail, "[2 overloads]");
|
|
|
|
EXPECT_EQ(R.documentation->value, "From \"foo.h\"\nThis is x()");
|
2018-09-06 18:52:26 +00:00
|
|
|
|
|
|
|
C.Deprecated = true;
|
|
|
|
R = C.render(Opts);
|
|
|
|
EXPECT_TRUE(R.deprecated);
|
2020-04-30 10:49:32 +02:00
|
|
|
|
|
|
|
Opts.DocumentationFormat = MarkupKind::Markdown;
|
|
|
|
R = C.render(Opts);
|
2020-04-29 20:10:50 +02:00
|
|
|
EXPECT_EQ(R.documentation->value, "From `\"foo.h\"` \nThis is `x()`");
|
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");
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Results.Completions, UnorderedElementsAre(named("NotRecovered")));
|
2018-07-11 13:15:31 +00:00
|
|
|
}
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(scope("ns::X::"), named("x_"))));
|
2018-07-18 15:31:14 +00:00
|
|
|
}
|
|
|
|
|
2021-12-27 04:08:01 +01:00
|
|
|
// Like other class members, constructor init lists have to parse what's below,
|
|
|
|
// after the completion point.
|
|
|
|
// But recovering from an incomplete constructor init list is particularly
|
|
|
|
// tricky because the bulk of the list is not surrounded by brackets.
|
|
|
|
TEST(CompletionTest, ConstructorInitListIncomplete) {
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
namespace ns {
|
|
|
|
struct X {
|
|
|
|
X() : x^
|
|
|
|
int xyz_;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
)cpp");
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Results.Completions, ElementsAre(named("xyz_")));
|
2021-12-27 04:08:01 +01:00
|
|
|
|
|
|
|
Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
int foo();
|
|
|
|
|
|
|
|
namespace ns {
|
|
|
|
struct X {
|
|
|
|
X() : xyz_(fo^
|
|
|
|
int xyz_;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
)cpp");
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Results.Completions, ElementsAre(named("foo")));
|
2021-12-27 04:08:01 +01:00
|
|
|
}
|
|
|
|
|
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) {
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2018-08-08 08:59:29 +00:00
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
|
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.IncludeFixIts = true;
|
2020-03-15 21:43:00 +01:00
|
|
|
const char *Code =
|
2018-08-08 08:59:29 +00:00
|
|
|
R"cpp(
|
|
|
|
class Auxilary {
|
|
|
|
public:
|
|
|
|
void AuxFunction();
|
|
|
|
};
|
|
|
|
class ClassWithPtr {
|
|
|
|
public:
|
|
|
|
void MemberFunction();
|
|
|
|
Auxilary* operator->() const;
|
|
|
|
Auxilary* Aux;
|
|
|
|
};
|
|
|
|
void f() {
|
|
|
|
ClassWithPtr x;
|
|
|
|
x[[->]]^;
|
|
|
|
}
|
2020-03-31 23:09:28 +02:00
|
|
|
)cpp";
|
|
|
|
auto Results = completions(Code, {}, Opts);
|
2018-08-08 08:59:29 +00:00
|
|
|
EXPECT_EQ(Results.Completions.size(), 3u);
|
|
|
|
|
|
|
|
TextEdit ReplacementEdit;
|
2020-03-31 23:09:28 +02:00
|
|
|
ReplacementEdit.range = Annotations(Code).range();
|
2018-08-08 08:59:29 +00:00
|
|
|
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) {
|
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.IncludeFixIts = true;
|
2020-03-15 21:43:00 +01:00
|
|
|
const char *Code =
|
2018-08-08 08:59:29 +00:00
|
|
|
R"cpp(
|
|
|
|
class Auxilary {
|
|
|
|
public:
|
|
|
|
void AuxFunction();
|
|
|
|
};
|
|
|
|
class ClassWithPtr {
|
|
|
|
public:
|
|
|
|
void MemberFunction();
|
|
|
|
Auxilary* operator->() const;
|
|
|
|
Auxilary* Aux;
|
|
|
|
};
|
|
|
|
void f() {
|
|
|
|
ClassWithPtr x;
|
|
|
|
x[[.]]^;
|
|
|
|
}
|
2020-03-31 23:09:28 +02:00
|
|
|
)cpp";
|
|
|
|
auto Results = completions(Code, {}, Opts);
|
2018-08-08 08:59:29 +00:00
|
|
|
EXPECT_EQ(Results.Completions.size(), 3u);
|
|
|
|
|
|
|
|
TextEdit ReplacementEdit;
|
2020-03-31 23:09:28 +02:00
|
|
|
ReplacementEdit.range = Annotations(Code).range();
|
2018-08-08 08:59:29 +00:00
|
|
|
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) {
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2018-08-13 08:23:01 +00:00
|
|
|
MockCompilationDatabase CDB;
|
2020-03-31 23:09:28 +02:00
|
|
|
TestTU TU;
|
|
|
|
TU.AdditionalFiles["foo/abc/foo.h"] = "";
|
2018-08-13 08:23:01 +00:00
|
|
|
|
|
|
|
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.[[]]^;
|
|
|
|
}
|
2020-03-25 14:17:23 +01:00
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
#include "foo/[[a^/]]foo.h"
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
#include "foo/abc/[[fo^o.h"]]
|
|
|
|
)cpp",
|
2020-03-15 21:43:00 +01:00
|
|
|
};
|
2018-08-13 08:23:01 +00:00
|
|
|
for (const auto &Text : TestCodes) {
|
|
|
|
Annotations TestCode(Text);
|
2020-03-31 23:09:28 +02:00
|
|
|
TU.Code = TestCode.code().str();
|
|
|
|
auto Results = completions(TU, TestCode.point());
|
2019-11-26 13:45:04 +01:00
|
|
|
if (Results.Completions.size() != 1) {
|
2020-03-31 23:09:28 +02:00
|
|
|
ADD_FAILURE() << "Results.Completions.size() != 1" << Text;
|
2019-11-26 13:45:04 +01:00
|
|
|
continue;
|
|
|
|
}
|
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");
|
2019-06-04 09:36:59 +00:00
|
|
|
EXPECT_THAT(Results.signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(sig("foo([[int x]]) -> void"),
|
|
|
|
sig("foo([[int x]], [[int y = 0]]) -> void"),
|
|
|
|
sig("foo([[float x]], [[int y]]) -> void"),
|
|
|
|
sig("foo([[int x]], [[float y]]) -> void"),
|
|
|
|
sig("foo([[float x]], [[float y]]) -> void")));
|
2018-08-13 08:40:05 +00:00
|
|
|
// 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,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(sig("foo([[T]], [[T]], [[T]]) -> void")));
|
2018-08-14 09:36:32 +00:00
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(sig("foo([[T]], [[T]], [[T]]) -> void")));
|
2018-08-14 09:36:32 +00:00
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(sig("foo([[T...]]) -> void")));
|
2018-08-14 09:36:32 +00:00
|
|
|
|
|
|
|
// 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,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(sig("foo([[T]], [[U]]) -> void")));
|
2018-08-14 09:36:32 +00:00
|
|
|
}
|
|
|
|
|
2018-08-17 09:32:30 +00:00
|
|
|
TEST(SignatureHelpTest, IndexDocumentation) {
|
|
|
|
Symbol Foo0 = sym("foo", index::SymbolKind::Function, "@F@\\0#");
|
2022-02-01 10:14:07 +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#");
|
2022-02-01 10:14:07 +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,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(AllOf(sig("foo() -> int"), sigDoc("doc from the index")),
|
|
|
|
AllOf(sig("foo([[double]]) -> int"), sigDoc(""))));
|
2018-08-17 09:32:30 +00:00
|
|
|
|
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);
|
2022-02-01 10:14:07 +00:00
|
|
|
// doc from sema
|
2018-08-17 09:32:30 +00:00
|
|
|
int foo(int, int);
|
|
|
|
|
|
|
|
void test() {
|
|
|
|
foo(^);
|
|
|
|
}
|
2018-08-17 10:40:05 +00:00
|
|
|
)cpp";
|
|
|
|
|
|
|
|
EXPECT_THAT(
|
|
|
|
signatures(Sig1, {Foo0, Foo1, Foo2}).signatures,
|
2019-06-04 09:36:59 +00:00
|
|
|
ElementsAre(
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(sig("foo() -> int"), sigDoc("doc from the index")),
|
|
|
|
AllOf(sig("foo([[int]]) -> int"), sigDoc("Overriden doc from sema")),
|
|
|
|
AllOf(sig("foo([[int]], [[int]]) -> int"), sigDoc("doc from sema"))));
|
2018-08-17 09:32:30 +00:00
|
|
|
}
|
|
|
|
|
2019-01-09 13:42:03 +00:00
|
|
|
TEST(SignatureHelpTest, DynamicIndexDocumentation) {
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2019-01-09 13:42:03 +00:00
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
ClangdServer::Options Opts = ClangdServer::optsForTest();
|
|
|
|
Opts.BuildDynamicSymbolIndex = true;
|
2020-01-24 14:08:56 +01:00
|
|
|
ClangdServer Server(CDB, FS, Opts);
|
2019-01-09 13:42:03 +00:00
|
|
|
|
|
|
|
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());
|
2021-12-09 09:36:04 +01:00
|
|
|
EXPECT_THAT(llvm::cantFail(runSignatureHelp(Server, File, FileContent.point(),
|
|
|
|
MarkupKind::PlainText))
|
|
|
|
.signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(AllOf(sig("foo() -> int"), sigDoc("Member doc"))));
|
2019-01-09 13:42:03 +00:00
|
|
|
}
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
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(
|
2022-02-01 10:14:07 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(named("xfoo"), snippetSuffix("<$1>($0)"))));
|
2018-09-26 05:45:31 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(named("foo_class"), snippetSuffix("<$0>")),
|
|
|
|
AllOf(named("foo_alias"), snippetSuffix("<$0>"))));
|
2018-09-26 05:45:31 +00:00
|
|
|
}
|
2021-11-12 15:03:23 +01:00
|
|
|
{
|
|
|
|
auto Results = completions(
|
|
|
|
R"cpp(
|
|
|
|
#define FOO(x, y) x##f
|
|
|
|
FO^ )cpp",
|
|
|
|
{}, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions, UnorderedElementsAre(AllOf(
|
2022-02-01 10:14:07 +00:00
|
|
|
named("FOO"), snippetSuffix("($0)"))));
|
2021-11-12 15:03:23 +01:00
|
|
|
}
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2022-01-17 10:35:29 +01:00
|
|
|
TEST(CompletionTest, NoCrashOnMissingNewLineAtEOF) {
|
|
|
|
auto FooCpp = testPath("foo.cpp");
|
|
|
|
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
MockFS FS;
|
|
|
|
Annotations F("#pragma ^ // no new line");
|
|
|
|
FS.Files[FooCpp] = F.code().str();
|
|
|
|
ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
|
|
|
|
runAddDocument(Server, FooCpp, F.code());
|
|
|
|
// Run completion outside the file range.
|
|
|
|
EXPECT_THAT(cantFail(runCodeComplete(Server, FooCpp, F.point(),
|
|
|
|
clangd::CodeCompleteOptions()))
|
|
|
|
.Completions,
|
|
|
|
IsEmpty());
|
|
|
|
EXPECT_THAT(cantFail(runSignatureHelp(Server, FooCpp, F.point(),
|
|
|
|
MarkupKind::PlainText))
|
|
|
|
.signatures,
|
|
|
|
IsEmpty());
|
|
|
|
}
|
|
|
|
|
2019-04-10 11:50:40 +00:00
|
|
|
TEST(GuessCompletionPrefix, Filters) {
|
|
|
|
for (llvm::StringRef Case : {
|
2019-06-15 02:26:47 +00:00
|
|
|
"[[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]]^",
|
2020-02-10 11:52:42 +01:00
|
|
|
"/* [[]][[]]^ */",
|
2019-06-15 02:26:47 +00:00
|
|
|
}) {
|
2019-04-10 11:50:40 +00:00
|
|
|
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) {
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
[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
|
|
|
MockCompilationDatabase CDB;
|
2020-01-24 14:08:56 +01:00
|
|
|
ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
|
[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 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;
|
|
|
|
|
|
|
|
auto CompleteAtPoint = [&](StringRef P) {
|
2022-09-08 11:19:40 +02:00
|
|
|
auto CCR = cantFail(runCodeComplete(Server, File, Test.point(P), Opts));
|
|
|
|
EXPECT_TRUE(CCR.HasMore);
|
[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
|
|
|
};
|
|
|
|
|
|
|
|
CompleteAtPoint("1");
|
2019-10-07 17:12:18 +00:00
|
|
|
auto Reqs1 = Requests.consumeRequests(1);
|
[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
|
|
|
ASSERT_EQ(Reqs1.size(), 1u);
|
|
|
|
EXPECT_THAT(Reqs1[0].Scopes, UnorderedElementsAre("ns1::"));
|
|
|
|
|
|
|
|
CompleteAtPoint("2");
|
2019-10-07 17:12:18 +00:00
|
|
|
auto Reqs2 = Requests.consumeRequests(1);
|
[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
|
|
|
// 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.
|
2019-10-07 17:12:18 +00:00
|
|
|
auto Reqs3 = Requests.consumeRequests(2);
|
[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
|
|
|
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();
|
2019-10-24 15:40:23 +02:00
|
|
|
Symbol Sym = func("Func");
|
|
|
|
Sym.CanonicalDeclaration.FileURI = DeclFile.c_str();
|
2022-12-02 10:04:31 -05:00
|
|
|
Sym.IncludeHeaders.emplace_back("\"foo.h\"", 2, Symbol::Include);
|
|
|
|
Sym.IncludeHeaders.emplace_back("\"bar.h\"", 1000, Symbol::Include);
|
[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
|
|
|
|
2019-10-24 15:40:23 +02:00
|
|
|
auto Results = completions("Fun^", {Sym}).Completions;
|
[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
|
|
|
assert(!Results.empty());
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Results[0], AllOf(named("Func"), insertInclude("\"bar.h\"")));
|
[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
|
|
|
EXPECT_EQ(Results[0].Includes.size(), 2u);
|
|
|
|
}
|
|
|
|
|
2022-12-02 10:04:31 -05:00
|
|
|
TEST(CompletionTest, InsertIncludeOrImport) {
|
|
|
|
std::string DeclFile = URI::create(testPath("foo")).toString();
|
|
|
|
Symbol Sym = func("Func");
|
|
|
|
Sym.CanonicalDeclaration.FileURI = DeclFile.c_str();
|
|
|
|
Sym.IncludeHeaders.emplace_back("\"bar.h\"", 1000,
|
|
|
|
Symbol::Include | Symbol::Import);
|
[clangd] Full support for #import insertions
These are still disabled by default, but will work in ObjC code if you
enable the `-import-insertions` flag.
Completion requires ASTSignals to be available; before ASTSignals are
available, we will always use #include. Once they are available, the
behavior varies as follows:
- For source files, use #import if the ObjC language flag is enabled
- For header files:
- If the ObjC language flag is disabled, use #include
- If the header file contains any #imports, use #import
- If the header file references any ObjC decls, use #import
- Otherwise, use #include
IncludeFixer support is similar, but it does not rely upon ASTSignals,
instead it does the above checks excluding the scan for ObjC symbols.
Differential Revision: https://reviews.llvm.org/D139458
2022-12-06 16:33:12 -05:00
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
// Should only take effect in import contexts.
|
|
|
|
Opts.ImportInsertions = true;
|
|
|
|
auto Results = completions("Fun^", {Sym}, Opts).Completions;
|
2022-12-02 10:04:31 -05:00
|
|
|
assert(!Results.empty());
|
|
|
|
EXPECT_THAT(Results[0],
|
|
|
|
AllOf(named("Func"), insertIncludeText("#include \"bar.h\"\n")));
|
|
|
|
|
[clangd] Full support for #import insertions
These are still disabled by default, but will work in ObjC code if you
enable the `-import-insertions` flag.
Completion requires ASTSignals to be available; before ASTSignals are
available, we will always use #include. Once they are available, the
behavior varies as follows:
- For source files, use #import if the ObjC language flag is enabled
- For header files:
- If the ObjC language flag is disabled, use #include
- If the header file contains any #imports, use #import
- If the header file references any ObjC decls, use #import
- Otherwise, use #include
IncludeFixer support is similar, but it does not rely upon ASTSignals,
instead it does the above checks excluding the scan for ObjC symbols.
Differential Revision: https://reviews.llvm.org/D139458
2022-12-06 16:33:12 -05:00
|
|
|
ASTSignals Signals;
|
|
|
|
Signals.InsertionDirective = Symbol::IncludeDirective::Import;
|
|
|
|
Opts.MainFileSignals = &Signals;
|
|
|
|
Results = completions("Fun^", {Sym}, Opts, "Foo.m").Completions;
|
2022-12-02 10:04:31 -05:00
|
|
|
assert(!Results.empty());
|
|
|
|
EXPECT_THAT(Results[0],
|
[clangd] Full support for #import insertions
These are still disabled by default, but will work in ObjC code if you
enable the `-import-insertions` flag.
Completion requires ASTSignals to be available; before ASTSignals are
available, we will always use #include. Once they are available, the
behavior varies as follows:
- For source files, use #import if the ObjC language flag is enabled
- For header files:
- If the ObjC language flag is disabled, use #include
- If the header file contains any #imports, use #import
- If the header file references any ObjC decls, use #import
- Otherwise, use #include
IncludeFixer support is similar, but it does not rely upon ASTSignals,
instead it does the above checks excluding the scan for ObjC symbols.
Differential Revision: https://reviews.llvm.org/D139458
2022-12-06 16:33:12 -05:00
|
|
|
AllOf(named("Func"), insertIncludeText("#import \"bar.h\"\n")));
|
2022-12-02 10:04:31 -05:00
|
|
|
|
|
|
|
Sym.IncludeHeaders[0].SupportedDirectives = Symbol::Import;
|
|
|
|
Results = completions("Fun^", {Sym}).Completions;
|
|
|
|
assert(!Results.empty());
|
|
|
|
EXPECT_THAT(Results[0], AllOf(named("Func"), Not(insertInclude())));
|
|
|
|
}
|
|
|
|
|
[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, NoInsertIncludeIfOnePresent) {
|
2020-03-31 23:09:28 +02:00
|
|
|
Annotations Test(R"cpp(
|
|
|
|
#include "foo.h"
|
|
|
|
Fun^
|
|
|
|
)cpp");
|
|
|
|
auto TU = TestTU::withCode(Test.code());
|
|
|
|
TU.AdditionalFiles["foo.h"] = "";
|
[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
|
|
|
|
[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();
|
2019-10-24 15:40:23 +02:00
|
|
|
Symbol Sym = func("Func");
|
|
|
|
Sym.CanonicalDeclaration.FileURI = DeclFile.c_str();
|
2022-12-02 10:04:31 -05:00
|
|
|
Sym.IncludeHeaders.emplace_back("\"foo.h\"", 2, Symbol::Include);
|
|
|
|
Sym.IncludeHeaders.emplace_back("\"bar.h\"", 1000, Symbol::Include);
|
[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
|
|
|
|
2020-03-15 21:43:00 +01:00
|
|
|
EXPECT_THAT(completions(TU, Test.point(), {Sym}).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(named("Func"), hasInclude("\"foo.h\""),
|
|
|
|
Not(insertInclude()))));
|
[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
|
|
|
}
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(named("Clangd_Macro_Test")));
|
2018-09-06 09:59:37 +00:00
|
|
|
}
|
|
|
|
|
2019-05-02 16:12:36 +00:00
|
|
|
TEST(CompletionTest, MacroFromPreamble) {
|
2020-03-31 23:09:28 +02:00
|
|
|
Annotations Test(R"cpp(#define CLANGD_PREAMBLE_MAIN x
|
2018-09-19 09:35:04 +00:00
|
|
|
|
|
|
|
int x = 0;
|
|
|
|
#define CLANGD_MAIN x
|
|
|
|
void f() { CLANGD_^ }
|
2020-03-31 23:09:28 +02:00
|
|
|
)cpp");
|
|
|
|
auto TU = TestTU::withCode(Test.code());
|
|
|
|
TU.HeaderCode = "#define CLANGD_PREAMBLE_HEADER x";
|
|
|
|
auto Results = completions(TU, Test.point(), {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,
|
2022-02-01 10:14:07 +00:00
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(named("TestClangd"), Not(deprecated())),
|
|
|
|
AllOf(named("TestClangc"), deprecated())));
|
2018-09-06 18:52:26 +00:00
|
|
|
}
|
|
|
|
|
2020-01-13 09:00:19 +01:00
|
|
|
TEST(SignatureHelpTest, PartialSpec) {
|
|
|
|
const auto Results = signatures(R"cpp(
|
|
|
|
template <typename T> struct Foo {};
|
|
|
|
template <typename T> struct Foo<T*> { Foo(T); };
|
|
|
|
Foo<int*> F(^);)cpp");
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Results.signatures, Contains(sig("Foo([[T]])")));
|
2020-01-13 09:00:19 +01:00
|
|
|
EXPECT_EQ(0, Results.activeParameter);
|
|
|
|
}
|
|
|
|
|
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");
|
2019-06-04 09:36:59 +00:00
|
|
|
EXPECT_THAT(Results.signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(sig("foo([[int x]]) -> void"),
|
|
|
|
sig("foo([[int x]], [[int y]]) -> void")));
|
2018-09-10 14:22:42 +00:00
|
|
|
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");
|
2019-06-04 09:36:59 +00:00
|
|
|
EXPECT_THAT(Results.signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(sig("foo([[int x]]) -> void"),
|
|
|
|
sig("foo([[int x]], [[int y]]) -> void")));
|
2018-09-10 14:22:42 +00:00
|
|
|
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");
|
2019-06-04 09:36:59 +00:00
|
|
|
EXPECT_THAT(Results.signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(sig("foo([[int x]]) -> void"),
|
|
|
|
sig("foo([[int x]], [[int y]]) -> void")));
|
2018-09-10 14:22:42 +00:00
|
|
|
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");
|
2019-06-04 09:36:59 +00:00
|
|
|
EXPECT_THAT(Results.signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(sig("foo([[int x]], [[int y]]) -> void")));
|
2018-09-10 14:22:42 +00:00
|
|
|
EXPECT_EQ(1, Results.activeParameter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-11 15:12:10 +00:00
|
|
|
TEST(SignatureHelpTest, ConstructorInitializeFields) {
|
|
|
|
{
|
|
|
|
const auto Results = signatures(R"cpp(
|
2021-12-27 04:08:01 +01:00
|
|
|
struct A { A(int); };
|
2018-09-11 15:12:10 +00:00
|
|
|
struct B {
|
|
|
|
B() : a_elem(^) {}
|
|
|
|
A a_elem;
|
|
|
|
};
|
|
|
|
)cpp");
|
2019-01-03 13:28:05 +00:00
|
|
|
EXPECT_THAT(Results.signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(sig("A([[int]])"), sig("A([[A &&]])"),
|
|
|
|
sig("A([[const A &]])")));
|
2018-09-11 15:12:10 +00:00
|
|
|
}
|
2021-12-27 04:08:01 +01:00
|
|
|
{
|
|
|
|
const auto Results = signatures(R"cpp(
|
|
|
|
struct A { A(int); };
|
|
|
|
struct B {
|
|
|
|
B() : a_elem(^
|
|
|
|
A a_elem;
|
|
|
|
};
|
|
|
|
)cpp");
|
|
|
|
// FIXME: currently the parser skips over the decl of a_elem as part of the
|
|
|
|
// (broken) init list, so we don't get signatures for the first member.
|
|
|
|
EXPECT_THAT(Results.signatures, IsEmpty());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Results = signatures(R"cpp(
|
|
|
|
struct A { A(int); };
|
|
|
|
struct B {
|
|
|
|
B() : a_elem(^
|
|
|
|
int dummy_elem;
|
|
|
|
A a_elem;
|
|
|
|
};
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(Results.signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(sig("A([[int]])"), sig("A([[A &&]])"),
|
|
|
|
sig("A([[const A &]])")));
|
2021-12-27 04:08:01 +01:00
|
|
|
}
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(sig("A([[int]])"), sig("A([[A &&]])"),
|
|
|
|
sig("A([[const A &]])")));
|
2018-09-11 15:12:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-06 18:50:13 +02:00
|
|
|
TEST(SignatureHelpTest, Variadic) {
|
|
|
|
const std::string Header = R"cpp(
|
|
|
|
void fun(int x, ...) {}
|
|
|
|
void test() {)cpp";
|
|
|
|
const std::string ExpectedSig = "fun([[int x]], [[...]]) -> void";
|
|
|
|
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "fun(^);}");
|
|
|
|
EXPECT_EQ(0, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "fun(1, ^);}");
|
|
|
|
EXPECT_EQ(1, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "fun(1, 2, ^);}");
|
|
|
|
EXPECT_EQ(1, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(SignatureHelpTest, VariadicTemplate) {
|
|
|
|
const std::string Header = R"cpp(
|
|
|
|
template<typename T, typename ...Args>
|
|
|
|
void fun(T t, Args ...args) {}
|
|
|
|
void test() {)cpp";
|
|
|
|
const std::string ExpectedSig = "fun([[T t]], [[Args args...]]) -> void";
|
|
|
|
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "fun(^);}");
|
|
|
|
EXPECT_EQ(0, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "fun(1, ^);}");
|
|
|
|
EXPECT_EQ(1, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "fun(1, 2, ^);}");
|
|
|
|
EXPECT_EQ(1, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(SignatureHelpTest, VariadicMethod) {
|
|
|
|
const std::string Header = R"cpp(
|
|
|
|
class C {
|
|
|
|
template<typename T, typename ...Args>
|
|
|
|
void fun(T t, Args ...args) {}
|
|
|
|
};
|
|
|
|
void test() {C c; )cpp";
|
|
|
|
const std::string ExpectedSig = "fun([[T t]], [[Args args...]]) -> void";
|
|
|
|
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "c.fun(^);}");
|
|
|
|
EXPECT_EQ(0, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "c.fun(1, ^);}");
|
|
|
|
EXPECT_EQ(1, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "c.fun(1, 2, ^);}");
|
|
|
|
EXPECT_EQ(1, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(SignatureHelpTest, VariadicType) {
|
|
|
|
const std::string Header = R"cpp(
|
|
|
|
void fun(int x, ...) {}
|
|
|
|
auto get_fun() { return fun; }
|
|
|
|
void test() {
|
|
|
|
)cpp";
|
|
|
|
const std::string ExpectedSig = "([[int]], [[...]]) -> void";
|
|
|
|
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "get_fun()(^);}");
|
|
|
|
EXPECT_EQ(0, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "get_fun()(1, ^);}");
|
|
|
|
EXPECT_EQ(1, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto Result = signatures(Header + "get_fun()(1, 2, ^);}");
|
|
|
|
EXPECT_EQ(1, Result.activeParameter);
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Result.signatures, UnorderedElementsAre(sig(ExpectedSig)));
|
2021-10-06 18:50:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-27 14:21:07 +00:00
|
|
|
TEST(CompletionTest, IncludedCompletionKinds) {
|
2021-01-26 07:08:53 +01:00
|
|
|
Annotations Test(R"cpp(#include "^)cpp");
|
2020-03-31 23:09:28 +02:00
|
|
|
auto TU = TestTU::withCode(Test.code());
|
|
|
|
TU.AdditionalFiles["sub/bar.h"] = "";
|
|
|
|
TU.ExtraArgs.push_back("-I" + testPath("sub"));
|
|
|
|
|
|
|
|
auto Results = completions(TU, Test.point());
|
2018-09-27 14:21:07 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(has("sub/", CompletionItemKind::Folder),
|
|
|
|
has("bar.h\"", CompletionItemKind::File)));
|
2018-09-27 14:21:07 +00:00
|
|
|
}
|
|
|
|
|
2018-10-02 14:46:08 +00:00
|
|
|
TEST(CompletionTest, NoCrashAtNonAlphaIncludeHeader) {
|
2020-03-31 23:09:28 +02:00
|
|
|
completions(
|
2018-10-02 14:46:08 +00:00
|
|
|
R"cpp(
|
|
|
|
#include "./^"
|
2019-01-03 13:28:05 +00:00
|
|
|
)cpp");
|
2018-10-02 14:46:08 +00:00
|
|
|
}
|
|
|
|
|
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(
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(qualifier(""), scope("na::"), named("ClangdA"))));
|
2018-09-27 18:46:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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"),
|
2022-11-02 12:50:50 +01:00
|
|
|
cls("na::nb::Clangd4"), enmConstant("na::C::Clangd5")},
|
2018-09-27 18:46:00 +00:00
|
|
|
Opts);
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
2022-11-02 12:50:50 +01:00
|
|
|
UnorderedElementsAre(AllOf(qualifier("nx::"), named("Clangd1"),
|
|
|
|
kind(CompletionItemKind::Class)),
|
|
|
|
AllOf(qualifier("ny::"), named("Clangd2"),
|
|
|
|
kind(CompletionItemKind::Class)),
|
|
|
|
AllOf(qualifier(""), scope(""), named("Clangd3"),
|
|
|
|
kind(CompletionItemKind::Class)),
|
|
|
|
AllOf(qualifier("nb::"), named("Clangd4"),
|
|
|
|
kind(CompletionItemKind::Class)),
|
|
|
|
AllOf(qualifier("C::"), named("Clangd5"),
|
|
|
|
kind(CompletionItemKind::EnumMember))));
|
2018-09-27 18:46:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(qualifier(""), named("Clangd1")),
|
|
|
|
AllOf(qualifier("nx::"), named("Clangd2"))));
|
2018-09-27 18:46:00 +00:00
|
|
|
}
|
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
|
|
|
|
2019-11-22 12:48:06 +01:00
|
|
|
TEST(CompletionTest, Lambda) {
|
|
|
|
clangd::CodeCompleteOptions Opts = {};
|
|
|
|
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
void function() {
|
|
|
|
auto Lambda = [](int a, const double &b) {return 1.f;};
|
|
|
|
Lam^
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
{}, Opts);
|
|
|
|
|
|
|
|
ASSERT_EQ(Results.Completions.size(), 1u);
|
|
|
|
const auto &A = Results.Completions.front();
|
|
|
|
EXPECT_EQ(A.Name, "Lambda");
|
|
|
|
EXPECT_EQ(A.Signature, "(int a, const double &b) const");
|
|
|
|
EXPECT_EQ(A.Kind, CompletionItemKind::Variable);
|
|
|
|
EXPECT_EQ(A.ReturnType, "float");
|
|
|
|
EXPECT_EQ(A.SnippetSuffix, "(${1:int a}, ${2:const double &b})");
|
|
|
|
}
|
|
|
|
|
2020-10-19 16:45:32 +02:00
|
|
|
TEST(CompletionTest, StructuredBinding) {
|
|
|
|
clangd::CodeCompleteOptions Opts = {};
|
|
|
|
|
|
|
|
auto Results = completions(R"cpp(
|
|
|
|
struct S {
|
|
|
|
using Float = float;
|
|
|
|
int x;
|
|
|
|
Float y;
|
|
|
|
};
|
|
|
|
void function() {
|
|
|
|
const auto &[xxx, yyy] = S{};
|
|
|
|
yyy^
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
{}, Opts);
|
|
|
|
|
|
|
|
ASSERT_EQ(Results.Completions.size(), 1u);
|
|
|
|
const auto &A = Results.Completions.front();
|
|
|
|
EXPECT_EQ(A.Name, "yyy");
|
|
|
|
EXPECT_EQ(A.Kind, CompletionItemKind::Variable);
|
|
|
|
EXPECT_EQ(A.ReturnType, "const Float");
|
|
|
|
}
|
|
|
|
|
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;
|
2022-02-01 10:14:07 +00:00
|
|
|
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("")));
|
2018-11-14 09:05:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2022-02-01 10:14:07 +00:00
|
|
|
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)}")));
|
2018-11-14 09:05:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2022-02-01 10:14:07 +00:00
|
|
|
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(
|
2022-02-01 10:14:07 +00:00
|
|
|
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;
|
2022-02-01 10:14:07 +00:00
|
|
|
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)}")));
|
2018-11-14 09:05:19 +00:00
|
|
|
}
|
|
|
|
|
2022-04-28 15:13:21 -04:00
|
|
|
TEST(CompletionTest, ObjectiveCMethodFilterOnEntireSelector) {
|
|
|
|
auto Results = completions(R"objc(
|
|
|
|
@interface Foo
|
|
|
|
+ (id)player:(id)player willRun:(id)run;
|
|
|
|
@end
|
|
|
|
id val = [Foo wi^]
|
|
|
|
)objc",
|
|
|
|
/*IndexSymbols=*/{},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
|
|
|
|
|
|
|
auto C = Results.Completions;
|
|
|
|
EXPECT_THAT(C, ElementsAre(named("player:")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(filterText("player:willRun:")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(kind(CompletionItemKind::Method)));
|
|
|
|
EXPECT_THAT(C, ElementsAre(returnType("id")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(signature("(id) willRun:(id)")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(snippetSuffix("${1:(id)} willRun:${2:(id)}")));
|
|
|
|
}
|
|
|
|
|
2021-04-19 16:38:37 -04:00
|
|
|
TEST(CompletionTest, ObjectiveCSimpleMethodDeclaration) {
|
|
|
|
auto Results = completions(R"objc(
|
|
|
|
@interface Foo
|
|
|
|
- (void)foo;
|
|
|
|
@end
|
|
|
|
@implementation Foo
|
|
|
|
fo^
|
|
|
|
@end
|
|
|
|
)objc",
|
|
|
|
/*IndexSymbols=*/{},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
|
|
|
|
|
|
|
auto C = Results.Completions;
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(C, ElementsAre(named("foo")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(kind(CompletionItemKind::Method)));
|
|
|
|
EXPECT_THAT(C, ElementsAre(qualifier("- (void)")));
|
2021-04-19 16:38:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, ObjectiveCMethodDeclaration) {
|
|
|
|
auto Results = completions(R"objc(
|
|
|
|
@interface Foo
|
|
|
|
- (int)valueForCharacter:(char)c secondArgument:(id)object;
|
|
|
|
@end
|
|
|
|
@implementation Foo
|
|
|
|
valueFor^
|
|
|
|
@end
|
|
|
|
)objc",
|
|
|
|
/*IndexSymbols=*/{},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
|
|
|
|
|
|
|
auto C = Results.Completions;
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(C, ElementsAre(named("valueForCharacter:")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(kind(CompletionItemKind::Method)));
|
|
|
|
EXPECT_THAT(C, ElementsAre(qualifier("- (int)")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(signature("(char)c secondArgument:(id)object")));
|
2021-04-19 16:38:37 -04:00
|
|
|
}
|
|
|
|
|
2022-06-29 10:04:21 -04:00
|
|
|
TEST(CompletionTest, ObjectiveCMethodDeclarationFilterOnEntireSelector) {
|
|
|
|
auto Results = completions(R"objc(
|
|
|
|
@interface Foo
|
|
|
|
- (int)valueForCharacter:(char)c secondArgument:(id)object;
|
|
|
|
@end
|
|
|
|
@implementation Foo
|
|
|
|
secondArg^
|
|
|
|
@end
|
|
|
|
)objc",
|
|
|
|
/*IndexSymbols=*/{},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
|
|
|
|
|
|
|
auto C = Results.Completions;
|
|
|
|
EXPECT_THAT(C, ElementsAre(named("valueForCharacter:")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(filterText("valueForCharacter:secondArgument:")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(kind(CompletionItemKind::Method)));
|
|
|
|
EXPECT_THAT(C, ElementsAre(qualifier("- (int)")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(signature("(char)c secondArgument:(id)object")));
|
|
|
|
}
|
|
|
|
|
2021-04-19 16:38:37 -04:00
|
|
|
TEST(CompletionTest, ObjectiveCMethodDeclarationPrefixTyped) {
|
|
|
|
auto Results = completions(R"objc(
|
|
|
|
@interface Foo
|
|
|
|
- (int)valueForCharacter:(char)c;
|
|
|
|
@end
|
|
|
|
@implementation Foo
|
|
|
|
- (int)valueFor^
|
|
|
|
@end
|
|
|
|
)objc",
|
|
|
|
/*IndexSymbols=*/{},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
|
|
|
|
|
|
|
auto C = Results.Completions;
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(C, ElementsAre(named("valueForCharacter:")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(kind(CompletionItemKind::Method)));
|
|
|
|
EXPECT_THAT(C, ElementsAre(signature("(char)c")));
|
2021-04-19 16:38:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, ObjectiveCMethodDeclarationFromMiddle) {
|
|
|
|
auto Results = completions(R"objc(
|
|
|
|
@interface Foo
|
|
|
|
- (int)valueForCharacter:(char)c secondArgument:(id)object;
|
|
|
|
@end
|
|
|
|
@implementation Foo
|
|
|
|
- (int)valueForCharacter:(char)c second^
|
|
|
|
@end
|
|
|
|
)objc",
|
|
|
|
/*IndexSymbols=*/{},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
|
|
|
|
|
|
|
auto C = Results.Completions;
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(C, ElementsAre(named("secondArgument:")));
|
|
|
|
EXPECT_THAT(C, ElementsAre(kind(CompletionItemKind::Method)));
|
|
|
|
EXPECT_THAT(C, ElementsAre(signature("(id)object")));
|
2021-04-19 16:38:37 -04:00
|
|
|
}
|
|
|
|
|
2022-06-06 12:19:32 -04:00
|
|
|
TEST(CompletionTest, ObjectiveCProtocolFromIndex) {
|
|
|
|
Symbol FoodClass = objcClass("FoodClass");
|
|
|
|
Symbol SymFood = objcProtocol("Food");
|
|
|
|
Symbol SymFooey = objcProtocol("Fooey");
|
2022-08-31 13:33:09 -04:00
|
|
|
auto Results = completions("id<Foo^>", {SymFood, FoodClass, SymFooey},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
|
|
|
|
|
|
|
// Should only give protocols for ObjC protocol completions.
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(
|
|
|
|
AllOf(named("Food"), kind(CompletionItemKind::Interface)),
|
|
|
|
AllOf(named("Fooey"), kind(CompletionItemKind::Interface))));
|
|
|
|
|
|
|
|
Results = completions("Fo^", {SymFood, FoodClass, SymFooey},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
|
|
|
// Shouldn't give protocols for non protocol completions.
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
ElementsAre(AllOf(named("FoodClass"), kind(CompletionItemKind::Class))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, ObjectiveCProtocolFromIndexSpeculation) {
|
|
|
|
MockFS FS;
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
|
|
|
|
|
|
|
|
auto File = testPath("Foo.m");
|
|
|
|
Annotations Test(R"cpp(
|
|
|
|
@protocol Food
|
|
|
|
@end
|
|
|
|
id<Foo$1^> foo;
|
|
|
|
Foo$2^ bar;
|
|
|
|
)cpp");
|
|
|
|
runAddDocument(Server, File, Test.code());
|
|
|
|
clangd::CodeCompleteOptions Opts = {};
|
|
|
|
|
|
|
|
Symbol FoodClass = objcClass("FoodClass");
|
|
|
|
IndexRequestCollector Requests({FoodClass});
|
|
|
|
Opts.Index = &Requests;
|
|
|
|
|
|
|
|
auto CompleteAtPoint = [&](StringRef P) {
|
|
|
|
return cantFail(runCodeComplete(Server, File, Test.point(P), Opts))
|
|
|
|
.Completions;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto C = CompleteAtPoint("1");
|
|
|
|
auto Reqs1 = Requests.consumeRequests(1);
|
|
|
|
ASSERT_EQ(Reqs1.size(), 1u);
|
|
|
|
EXPECT_THAT(C, ElementsAre(AllOf(named("Food"),
|
|
|
|
kind(CompletionItemKind::Interface))));
|
|
|
|
|
|
|
|
C = CompleteAtPoint("2");
|
|
|
|
auto Reqs2 = Requests.consumeRequests(1);
|
|
|
|
// Speculation succeeded. Used speculative index result, but filtering now to
|
|
|
|
// now include FoodClass.
|
|
|
|
ASSERT_EQ(Reqs2.size(), 1u);
|
|
|
|
EXPECT_EQ(Reqs2[0], Reqs1[0]);
|
|
|
|
EXPECT_THAT(C, ElementsAre(AllOf(named("FoodClass"),
|
|
|
|
kind(CompletionItemKind::Class))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CompletionTest, ObjectiveCCategoryFromIndexIgnored) {
|
|
|
|
Symbol FoodCategory = objcCategory("FoodClass", "Extension");
|
2022-06-06 12:19:32 -04:00
|
|
|
auto Results = completions(R"objc(
|
2022-08-31 13:33:09 -04:00
|
|
|
@interface Foo
|
|
|
|
@end
|
|
|
|
@interface Foo (^)
|
|
|
|
@end
|
2022-06-06 12:19:32 -04:00
|
|
|
)objc",
|
2022-08-31 13:33:09 -04:00
|
|
|
{FoodCategory},
|
2022-06-06 12:19:32 -04:00
|
|
|
/*Opts=*/{}, "Foo.m");
|
2022-08-31 13:33:09 -04:00
|
|
|
EXPECT_THAT(Results.Completions, IsEmpty());
|
2022-06-06 12:19:32 -04:00
|
|
|
}
|
|
|
|
|
2023-06-27 14:16:13 -04:00
|
|
|
TEST(CompletionTest, ObjectiveCForwardDeclFromIndex) {
|
|
|
|
Symbol FoodClass = objcClass("FoodClass");
|
|
|
|
FoodClass.IncludeHeaders.emplace_back("\"Foo.h\"", 2, Symbol::Import);
|
|
|
|
Symbol SymFood = objcProtocol("Food");
|
|
|
|
auto Results = completions("@class Foo^", {SymFood, FoodClass},
|
|
|
|
/*Opts=*/{}, "Foo.m");
|
|
|
|
|
|
|
|
// Should only give class names without any include insertion.
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
UnorderedElementsAre(AllOf(named("FoodClass"),
|
|
|
|
kind(CompletionItemKind::Class),
|
|
|
|
Not(insertInclude()))));
|
|
|
|
}
|
|
|
|
|
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.
|
2019-06-15 02:26:47 +00:00
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-07-18 03:48:04 -04:00
|
|
|
Contains(AllOf(named("while"),
|
|
|
|
snippetSuffix(" (${1:condition}) {\n$0\n}"))));
|
2019-05-28 15:33:37 +00:00
|
|
|
// However, snippets for functions must *not* end with $0.
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(named("while_foo"),
|
|
|
|
snippetSuffix("(${1:int a}, ${2:int b})"))));
|
2023-03-14 13:15:34 +08:00
|
|
|
|
|
|
|
Results = completions(R"cpp(
|
|
|
|
struct Base {
|
|
|
|
Base(int a, int b) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Derived : Base {
|
|
|
|
Derived() : Base^
|
|
|
|
};
|
|
|
|
)cpp",
|
|
|
|
/*IndexSymbols=*/{}, Options);
|
|
|
|
// Constructors from base classes are a kind of pattern that shouldn't end
|
|
|
|
// with $0.
|
|
|
|
EXPECT_THAT(Results.Completions,
|
|
|
|
Contains(AllOf(named("Base"),
|
|
|
|
snippetSuffix("(${1:int a}, ${2:int b})"))));
|
2019-05-28 15:33:37 +00:00
|
|
|
}
|
|
|
|
|
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");
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(R.Completions, ElementsAre(named("loopVar")));
|
2019-01-24 10:41:43 +00:00
|
|
|
}
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(AllOf(scope("std::"), named("foo"),
|
|
|
|
kind(CompletionItemKind::Reference))));
|
2019-02-26 14:23:47 +00:00
|
|
|
}
|
|
|
|
|
2022-11-02 12:50:50 +01:00
|
|
|
TEST(CompletionTest, Enums) {
|
|
|
|
const char *Header(R"cpp(
|
|
|
|
namespace ns {
|
|
|
|
enum Unscoped { Clangd1 };
|
|
|
|
class C {
|
|
|
|
enum Unscoped { Clangd2 };
|
|
|
|
};
|
|
|
|
enum class Scoped { Clangd3 };
|
|
|
|
})cpp");
|
|
|
|
const char *Source(R"cpp(
|
|
|
|
void bar() {
|
|
|
|
Clangd^
|
|
|
|
})cpp");
|
|
|
|
auto Index = TestTU::withHeaderCode(Header).index();
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.Index = Index.get();
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
auto R = completions(Source, {}, Opts);
|
2022-10-31 21:36:18 +01:00
|
|
|
EXPECT_THAT(R.Completions, UnorderedElementsAre(
|
|
|
|
AllOf(scope("ns::"), named("Clangd1"),
|
|
|
|
kind(CompletionItemKind::EnumMember)),
|
|
|
|
AllOf(scope("ns::C::"), named("Clangd2"),
|
|
|
|
kind(CompletionItemKind::EnumMember)),
|
|
|
|
AllOf(scope("ns::Scoped::"), named("Clangd3"),
|
|
|
|
kind(CompletionItemKind::EnumMember))));
|
2022-11-02 12:50:50 +01:00
|
|
|
}
|
|
|
|
|
[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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(qualifier(""), named("XYZ"))));
|
[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, 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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(qualifier(""), named("XYZ"))));
|
[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
|
|
|
}
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(qualifier(""), named("ABCDE"))));
|
2019-04-10 11:50:40 +00:00
|
|
|
}
|
|
|
|
|
2019-10-04 08:10:27 +00:00
|
|
|
TEST(CompletionTest, DerivedMethodsAreAlwaysVisible) {
|
|
|
|
// Despite the fact that base method matches the ref-qualifier better,
|
|
|
|
// completion results should only include the derived method.
|
|
|
|
auto Completions = completions(R"cpp(
|
|
|
|
struct deque_base {
|
|
|
|
float size();
|
|
|
|
double size() const;
|
|
|
|
};
|
|
|
|
struct deque : deque_base {
|
|
|
|
int size() const;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto x = deque().^
|
|
|
|
)cpp")
|
|
|
|
.Completions;
|
|
|
|
EXPECT_THAT(Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(AllOf(returnType("int"), named("size"))));
|
2019-10-04 08:10:27 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 14:42:06 +01:00
|
|
|
TEST(CompletionTest, NoCrashWithIncompleteLambda) {
|
|
|
|
auto Completions = completions("auto&& x = []{^").Completions;
|
|
|
|
// The completion of x itself can cause a problem: in the code completion
|
|
|
|
// callback, its type is not known, which affects the linkage calculation.
|
|
|
|
// A bad linkage value gets cached, and subsequently updated.
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Completions, Contains(named("x")));
|
2020-02-04 14:42:06 +01:00
|
|
|
|
|
|
|
auto Signatures = signatures("auto x() { x(^").signatures;
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Signatures, Contains(sig("x() -> auto")));
|
2020-02-04 14:42:06 +01:00
|
|
|
}
|
|
|
|
|
2020-03-31 23:33:56 +02:00
|
|
|
TEST(CompletionTest, DelayedTemplateParsing) {
|
|
|
|
Annotations Test(R"cpp(
|
|
|
|
int xxx;
|
|
|
|
template <typename T> int foo() { return xx^; }
|
|
|
|
)cpp");
|
|
|
|
auto TU = TestTU::withCode(Test.code());
|
|
|
|
// Even though delayed-template-parsing is on, we will disable it to provide
|
|
|
|
// completion in templates.
|
|
|
|
TU.ExtraArgs.push_back("-fdelayed-template-parsing");
|
|
|
|
|
|
|
|
EXPECT_THAT(completions(TU, Test.point()).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(named("xxx")));
|
2020-03-31 23:33:56 +02:00
|
|
|
}
|
|
|
|
|
2020-02-10 11:52:42 +01:00
|
|
|
TEST(CompletionTest, CompletionRange) {
|
|
|
|
const char *WithRange = "auto x = [[abc]]^";
|
|
|
|
auto Completions = completions(WithRange);
|
|
|
|
EXPECT_EQ(Completions.CompletionRange, Annotations(WithRange).range());
|
|
|
|
Completions = completionsNoCompile(WithRange);
|
|
|
|
EXPECT_EQ(Completions.CompletionRange, Annotations(WithRange).range());
|
|
|
|
|
|
|
|
const char *EmptyRange = "auto x = [[]]^";
|
|
|
|
Completions = completions(EmptyRange);
|
|
|
|
EXPECT_EQ(Completions.CompletionRange, Annotations(EmptyRange).range());
|
|
|
|
Completions = completionsNoCompile(EmptyRange);
|
|
|
|
EXPECT_EQ(Completions.CompletionRange, Annotations(EmptyRange).range());
|
|
|
|
|
|
|
|
// Sema doesn't trigger at all here, while the no-sema completion runs
|
|
|
|
// heuristics as normal and reports a range. It'd be nice to be consistent.
|
2021-09-30 15:11:29 +02:00
|
|
|
const char *NoCompletion = "/* foo [[]]^ */";
|
2020-02-10 11:52:42 +01:00
|
|
|
Completions = completions(NoCompletion);
|
2022-12-05 21:49:31 -08:00
|
|
|
EXPECT_EQ(Completions.CompletionRange, std::nullopt);
|
2020-02-10 11:52:42 +01:00
|
|
|
Completions = completionsNoCompile(NoCompletion);
|
|
|
|
EXPECT_EQ(Completions.CompletionRange, Annotations(NoCompletion).range());
|
|
|
|
}
|
|
|
|
|
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,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(named("void"), named("func"), named("int"),
|
|
|
|
named("xyz"), named("abc")));
|
2019-04-11 09:36:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(NoCompileCompletionTest, WithFilter) {
|
|
|
|
auto Results = completionsNoCompile(R"cpp(
|
|
|
|
void func() {
|
|
|
|
int sym1;
|
|
|
|
int sym2;
|
|
|
|
int xyz1;
|
|
|
|
int xyz2;
|
|
|
|
sy^
|
|
|
|
}
|
|
|
|
)cpp");
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(named("sym1"), named("sym2")));
|
2019-04-11 09:36:36 +00:00
|
|
|
}
|
|
|
|
|
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^
|
|
|
|
}
|
2019-08-01 11:05:06 +00:00
|
|
|
}
|
2019-04-26 07:45:49 +00:00
|
|
|
)cpp",
|
|
|
|
Syms);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(qualifier(""), scope("")),
|
|
|
|
AllOf(qualifier(""), scope("a::")),
|
|
|
|
AllOf(qualifier(""), scope("ns::b::"))));
|
2019-04-26 07:45:49 +00:00
|
|
|
CodeCompleteOptions Opts;
|
|
|
|
Opts.AllScopes = true;
|
|
|
|
Results = completionsNoCompile(
|
|
|
|
R"cpp(
|
|
|
|
// All-scopes unqualified completion.
|
|
|
|
using namespace a;
|
|
|
|
namespace ns {
|
|
|
|
using namespace b;
|
|
|
|
void foo() {
|
|
|
|
xx^
|
|
|
|
}
|
2019-08-01 11:05:06 +00:00
|
|
|
}
|
2019-04-26 07:45:49 +00:00
|
|
|
)cpp",
|
|
|
|
Syms, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(qualifier(""), scope("")),
|
|
|
|
AllOf(qualifier(""), scope("a::")),
|
|
|
|
AllOf(qualifier(""), scope("ns::b::")),
|
|
|
|
AllOf(qualifier("c::"), scope("c::")),
|
|
|
|
AllOf(qualifier("d::"), scope("ns::d::"))));
|
2019-04-26 07:45:49 +00:00
|
|
|
Results = completionsNoCompile(
|
|
|
|
R"cpp(
|
|
|
|
// Qualified completion.
|
|
|
|
using namespace a;
|
|
|
|
namespace ns {
|
|
|
|
using namespace b;
|
|
|
|
void foo() {
|
|
|
|
b::xx^
|
|
|
|
}
|
2019-08-01 11:05:06 +00:00
|
|
|
}
|
2019-04-26 07:45:49 +00:00
|
|
|
)cpp",
|
|
|
|
Syms, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(AllOf(qualifier(""), scope("ns::b::"))));
|
2019-04-26 07:45:49 +00:00
|
|
|
Results = completionsNoCompile(
|
|
|
|
R"cpp(
|
|
|
|
// Absolutely qualified completion.
|
|
|
|
using namespace a;
|
|
|
|
namespace ns {
|
|
|
|
using namespace b;
|
|
|
|
void foo() {
|
|
|
|
::a::xx^
|
|
|
|
}
|
2019-08-01 11:05:06 +00:00
|
|
|
}
|
2019-04-26 07:45:49 +00:00
|
|
|
)cpp",
|
|
|
|
Syms, Opts);
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(AllOf(qualifier(""), scope("a::"))));
|
2019-04-26 07:45:49 +00:00
|
|
|
}
|
|
|
|
|
2020-05-06 01:39:59 +02:00
|
|
|
TEST(AllowImplicitCompletion, All) {
|
|
|
|
const char *Yes[] = {
|
|
|
|
"foo.^bar",
|
|
|
|
"foo->^bar",
|
|
|
|
"foo::^bar",
|
|
|
|
" # include <^foo.h>",
|
|
|
|
"#import <foo/^bar.h>",
|
|
|
|
"#include_next \"^",
|
|
|
|
};
|
|
|
|
const char *No[] = {
|
|
|
|
"foo>^bar",
|
|
|
|
"foo:^bar",
|
|
|
|
"foo\n^bar",
|
|
|
|
"#include <foo.h> //^",
|
|
|
|
"#include \"foo.h\"^",
|
|
|
|
"#error <^",
|
|
|
|
"#<^",
|
|
|
|
};
|
|
|
|
for (const char *Test : Yes) {
|
|
|
|
llvm::Annotations A(Test);
|
|
|
|
EXPECT_TRUE(allowImplicitCompletion(A.code(), A.point())) << Test;
|
|
|
|
}
|
|
|
|
for (const char *Test : No) {
|
|
|
|
llvm::Annotations A(Test);
|
|
|
|
EXPECT_FALSE(allowImplicitCompletion(A.code(), A.point())) << Test;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 13:58:46 +02:00
|
|
|
TEST(CompletionTest, FunctionArgsExist) {
|
|
|
|
clangd::CodeCompleteOptions Opts;
|
|
|
|
Opts.EnableSnippets = true;
|
|
|
|
std::string Context = R"cpp(
|
2021-11-12 15:03:23 +01:00
|
|
|
#define MACRO(x)
|
2020-06-09 13:58:46 +02:00
|
|
|
int foo(int A);
|
|
|
|
int bar();
|
|
|
|
struct Object {
|
|
|
|
Object(int B) {}
|
|
|
|
};
|
|
|
|
template <typename T>
|
|
|
|
struct Container {
|
|
|
|
Container(int Size) {}
|
|
|
|
};
|
|
|
|
)cpp";
|
|
|
|
EXPECT_THAT(completions(Context + "int y = fo^", {}, Opts).Completions,
|
|
|
|
UnorderedElementsAre(
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(labeled("foo(int A)"), snippetSuffix("(${1:int A})"))));
|
2020-06-09 13:58:46 +02:00
|
|
|
EXPECT_THAT(
|
|
|
|
completions(Context + "int y = fo^(42)", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(labeled("foo(int A)"), snippetSuffix(""))));
|
2020-06-09 13:58:46 +02:00
|
|
|
// FIXME(kirillbobyrev): No snippet should be produced here.
|
|
|
|
EXPECT_THAT(completions(Context + "int y = fo^o(42)", {}, Opts).Completions,
|
|
|
|
UnorderedElementsAre(
|
2022-02-01 10:14:07 +00:00
|
|
|
AllOf(labeled("foo(int A)"), snippetSuffix("(${1:int A})"))));
|
2020-06-09 13:58:46 +02:00
|
|
|
EXPECT_THAT(
|
|
|
|
completions(Context + "int y = ba^", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(labeled("bar()"), snippetSuffix("()"))));
|
2020-06-09 13:58:46 +02:00
|
|
|
EXPECT_THAT(completions(Context + "int y = ba^()", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(AllOf(labeled("bar()"), snippetSuffix(""))));
|
2020-06-09 13:58:46 +02:00
|
|
|
EXPECT_THAT(
|
|
|
|
completions(Context + "Object o = Obj^", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(labeled("Object(int B)"), snippetSuffix("(${1:int B})"),
|
|
|
|
kind(CompletionItemKind::Constructor))));
|
2020-06-09 13:58:46 +02:00
|
|
|
EXPECT_THAT(completions(Context + "Object o = Obj^()", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(labeled("Object(int B)"), snippetSuffix(""),
|
|
|
|
kind(CompletionItemKind::Constructor))));
|
2020-06-09 13:58:46 +02:00
|
|
|
EXPECT_THAT(
|
|
|
|
completions(Context + "Container c = Cont^", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(labeled("Container<typename T>(int Size)"),
|
|
|
|
snippetSuffix("<${1:typename T}>(${2:int Size})"),
|
|
|
|
kind(CompletionItemKind::Constructor))));
|
2020-06-09 13:58:46 +02:00
|
|
|
EXPECT_THAT(
|
|
|
|
completions(Context + "Container c = Cont^()", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(labeled("Container<typename T>(int Size)"),
|
|
|
|
snippetSuffix("<${1:typename T}>"),
|
|
|
|
kind(CompletionItemKind::Constructor))));
|
2021-02-18 13:06:01 +01:00
|
|
|
EXPECT_THAT(
|
|
|
|
completions(Context + "Container c = Cont^<int>()", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(labeled("Container<typename T>(int Size)"),
|
|
|
|
snippetSuffix(""),
|
|
|
|
kind(CompletionItemKind::Constructor))));
|
2021-11-12 15:03:23 +01:00
|
|
|
EXPECT_THAT(completions(Context + "MAC^(2)", {}, Opts).Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
Contains(AllOf(labeled("MACRO(x)"), snippetSuffix(""),
|
2023-02-24 01:17:44 -05:00
|
|
|
kind(CompletionItemKind::Function))));
|
2020-06-09 13:58:46 +02:00
|
|
|
}
|
|
|
|
|
[clangd] Check if macro is already in the IdentifierTable before loading it
Having nested macros in the C code could cause clangd to fail an assert in clang::Preprocessor::setLoadedMacroDirective() and crash.
#1 0x00000000007ace30 PrintStackTraceSignalHandler(void*) /qdelacru/llvm-project/llvm/lib/Support/Unix/Signals.inc:632:1
#2 0x00000000007aaded llvm::sys::RunSignalHandlers() /qdelacru/llvm-project/llvm/lib/Support/Signals.cpp:76:20
#3 0x00000000007ac7c1 SignalHandler(int) /qdelacru/llvm-project/llvm/lib/Support/Unix/Signals.inc:407:1
#4 0x00007f096604db20 __restore_rt (/lib64/libpthread.so.0+0x12b20)
#5 0x00007f0964b307ff raise (/lib64/libc.so.6+0x377ff)
#6 0x00007f0964b1ac35 abort (/lib64/libc.so.6+0x21c35)
#7 0x00007f0964b1ab09 _nl_load_domain.cold.0 (/lib64/libc.so.6+0x21b09)
#8 0x00007f0964b28de6 (/lib64/libc.so.6+0x2fde6)
#9 0x0000000001004d1a clang::Preprocessor::setLoadedMacroDirective(clang::IdentifierInfo*, clang::MacroDirective*, clang::MacroDirective*) /qdelacru/llvm-project/clang/lib/Lex/PPMacroExpansion.cpp:116:5
An example of the code that causes the assert failure:
```
...
```
During code completion in clangd, the macros will be loaded in loadMainFilePreambleMacros() by iterating over the macro names and calling PreambleIdentifiers->get(). Since these macro names are store in a StringSet (has StringMap underlying container), the order of the iterator is not guaranteed to be same as the order seen in the source code.
When clangd is trying to resolve nested macros it sometimes attempts to load them out of order which causes a macro to be stored twice. In the example above, ECHO2 macro gets resolved first, but since it uses another macro that has not been resolved it will try to resolve/store that as well. Now there are two MacroDirectives stored in the Preprocessor, ECHO and ECHO2. When clangd tries to load the next macro, ECHO, the preprocessor fails an assert in clang::Preprocessor::setLoadedMacroDirective() because there is already a MacroDirective stored for that macro name.
In this diff, I check if the macro is already inside the IdentifierTable and if it is skip it so that it is not resolved twice.
Reviewed By: kadircet
Differential Revision: https://reviews.llvm.org/D101870
2021-05-06 08:22:32 +02:00
|
|
|
TEST(CompletionTest, NoCrashDueToMacroOrdering) {
|
|
|
|
EXPECT_THAT(completions(R"cpp(
|
|
|
|
#define ECHO(X) X
|
|
|
|
#define ECHO2(X) ECHO(X)
|
|
|
|
int finish_preamble = EC^HO(2);)cpp")
|
|
|
|
.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(labeled("ECHO(X)"), labeled("ECHO2(X)")));
|
[clangd] Check if macro is already in the IdentifierTable before loading it
Having nested macros in the C code could cause clangd to fail an assert in clang::Preprocessor::setLoadedMacroDirective() and crash.
#1 0x00000000007ace30 PrintStackTraceSignalHandler(void*) /qdelacru/llvm-project/llvm/lib/Support/Unix/Signals.inc:632:1
#2 0x00000000007aaded llvm::sys::RunSignalHandlers() /qdelacru/llvm-project/llvm/lib/Support/Signals.cpp:76:20
#3 0x00000000007ac7c1 SignalHandler(int) /qdelacru/llvm-project/llvm/lib/Support/Unix/Signals.inc:407:1
#4 0x00007f096604db20 __restore_rt (/lib64/libpthread.so.0+0x12b20)
#5 0x00007f0964b307ff raise (/lib64/libc.so.6+0x377ff)
#6 0x00007f0964b1ac35 abort (/lib64/libc.so.6+0x21c35)
#7 0x00007f0964b1ab09 _nl_load_domain.cold.0 (/lib64/libc.so.6+0x21b09)
#8 0x00007f0964b28de6 (/lib64/libc.so.6+0x2fde6)
#9 0x0000000001004d1a clang::Preprocessor::setLoadedMacroDirective(clang::IdentifierInfo*, clang::MacroDirective*, clang::MacroDirective*) /qdelacru/llvm-project/clang/lib/Lex/PPMacroExpansion.cpp:116:5
An example of the code that causes the assert failure:
```
...
```
During code completion in clangd, the macros will be loaded in loadMainFilePreambleMacros() by iterating over the macro names and calling PreambleIdentifiers->get(). Since these macro names are store in a StringSet (has StringMap underlying container), the order of the iterator is not guaranteed to be same as the order seen in the source code.
When clangd is trying to resolve nested macros it sometimes attempts to load them out of order which causes a macro to be stored twice. In the example above, ECHO2 macro gets resolved first, but since it uses another macro that has not been resolved it will try to resolve/store that as well. Now there are two MacroDirectives stored in the Preprocessor, ECHO and ECHO2. When clangd tries to load the next macro, ECHO, the preprocessor fails an assert in clang::Preprocessor::setLoadedMacroDirective() because there is already a MacroDirective stored for that macro name.
In this diff, I check if the macro is already inside the IdentifierTable and if it is skip it so that it is not resolved twice.
Reviewed By: kadircet
Differential Revision: https://reviews.llvm.org/D101870
2021-05-06 08:22:32 +02:00
|
|
|
}
|
|
|
|
|
2021-06-18 17:25:00 +02:00
|
|
|
TEST(CompletionTest, ObjCCategoryDecls) {
|
|
|
|
TestTU TU;
|
|
|
|
TU.ExtraArgs.push_back("-xobjective-c");
|
|
|
|
TU.HeaderCode = R"objc(
|
|
|
|
@interface Foo
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface Foo (FooExt1)
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface Foo (FooExt2)
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface Bar
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface Bar (BarExt)
|
|
|
|
@end)objc";
|
|
|
|
|
|
|
|
{
|
|
|
|
Annotations Test(R"objc(
|
|
|
|
@implementation Foo (^)
|
|
|
|
@end
|
|
|
|
)objc");
|
|
|
|
TU.Code = Test.code().str();
|
|
|
|
auto Results = completions(TU, Test.point());
|
|
|
|
EXPECT_THAT(Results.Completions,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(labeled("FooExt1"), labeled("FooExt2")));
|
2021-06-18 17:25:00 +02:00
|
|
|
}
|
|
|
|
{
|
|
|
|
Annotations Test(R"objc(
|
|
|
|
@interface Foo (^)
|
|
|
|
@end
|
|
|
|
)objc");
|
|
|
|
TU.Code = Test.code().str();
|
|
|
|
auto Results = completions(TU, Test.point());
|
2022-02-01 10:14:07 +00:00
|
|
|
EXPECT_THAT(Results.Completions, UnorderedElementsAre(labeled("BarExt")));
|
2021-06-18 17:25:00 +02:00
|
|
|
}
|
|
|
|
}
|
2021-09-16 10:17:37 +02:00
|
|
|
|
|
|
|
TEST(CompletionTest, PreambleCodeComplete) {
|
|
|
|
llvm::StringLiteral Baseline = "\n#define MACRO 12\nint num = MACRO;";
|
|
|
|
llvm::StringLiteral ModifiedCC =
|
|
|
|
"#include \"header.h\"\n#define MACRO 12\nint num = MACRO; int num2 = M^";
|
|
|
|
|
|
|
|
Annotations Test(ModifiedCC);
|
|
|
|
auto BaselineTU = TestTU::withCode(Baseline);
|
|
|
|
auto ModifiedTU = TestTU::withCode(Test.code());
|
|
|
|
|
|
|
|
MockFS FS;
|
|
|
|
auto Inputs = ModifiedTU.inputs(FS);
|
|
|
|
auto Result = codeComplete(testPath(ModifiedTU.Filename), Test.point(),
|
|
|
|
BaselineTU.preamble().get(), Inputs, {});
|
|
|
|
EXPECT_THAT(Result.Completions, Not(testing::IsEmpty()));
|
|
|
|
}
|
|
|
|
|
2021-09-30 15:11:29 +02:00
|
|
|
TEST(CompletionTest, CommentParamName) {
|
|
|
|
const std::string Code = R"cpp(
|
|
|
|
void fun(int foo, int bar);
|
|
|
|
void overloaded(int param_int);
|
|
|
|
void overloaded(int param_int, int param_other);
|
|
|
|
void overloaded(char param_char);
|
|
|
|
int main() {
|
|
|
|
)cpp";
|
|
|
|
|
2022-10-28 16:30:22 +02:00
|
|
|
EXPECT_THAT(completions(Code + "fun(/*^").Completions,
|
|
|
|
UnorderedElementsAre(labeled("foo=*/")));
|
|
|
|
EXPECT_THAT(completions(Code + "fun(1, /*^").Completions,
|
|
|
|
UnorderedElementsAre(labeled("bar=*/")));
|
|
|
|
EXPECT_THAT(completions(Code + "/*^").Completions, IsEmpty());
|
2021-09-30 15:11:29 +02:00
|
|
|
// Test de-duplication.
|
|
|
|
EXPECT_THAT(
|
2022-10-28 16:30:22 +02:00
|
|
|
completions(Code + "overloaded(/*^").Completions,
|
|
|
|
UnorderedElementsAre(labeled("param_int=*/"), labeled("param_char=*/")));
|
2021-09-30 15:11:29 +02:00
|
|
|
// Comment already has some text in it.
|
2022-10-28 16:30:22 +02:00
|
|
|
EXPECT_THAT(completions(Code + "fun(/* ^").Completions,
|
|
|
|
UnorderedElementsAre(labeled("foo=*/")));
|
|
|
|
EXPECT_THAT(completions(Code + "fun(/* f^").Completions,
|
|
|
|
UnorderedElementsAre(labeled("foo=*/")));
|
|
|
|
EXPECT_THAT(completions(Code + "fun(/* x^").Completions, IsEmpty());
|
|
|
|
EXPECT_THAT(completions(Code + "fun(/* f ^").Completions, IsEmpty());
|
|
|
|
|
|
|
|
// Test ranges
|
|
|
|
{
|
|
|
|
std::string CompletionRangeTest(Code + "fun(/*[[^]]");
|
|
|
|
auto Results = completions(CompletionRangeTest);
|
|
|
|
EXPECT_THAT(Results.CompletionRange,
|
|
|
|
llvm::ValueIs(Annotations(CompletionRangeTest).range()));
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
testing::Each(
|
|
|
|
AllOf(replacesRange(Annotations(CompletionRangeTest).range()),
|
|
|
|
origin(SymbolOrigin::AST), kind(CompletionItemKind::Text))));
|
|
|
|
}
|
|
|
|
{
|
|
|
|
std::string CompletionRangeTest(Code + "fun(/*[[fo^]]");
|
|
|
|
auto Results = completions(CompletionRangeTest);
|
|
|
|
EXPECT_THAT(Results.CompletionRange,
|
|
|
|
llvm::ValueIs(Annotations(CompletionRangeTest).range()));
|
|
|
|
EXPECT_THAT(
|
|
|
|
Results.Completions,
|
|
|
|
testing::Each(
|
|
|
|
AllOf(replacesRange(Annotations(CompletionRangeTest).range()),
|
|
|
|
origin(SymbolOrigin::AST), kind(CompletionItemKind::Text))));
|
|
|
|
}
|
2021-09-30 15:11:29 +02:00
|
|
|
}
|
|
|
|
|
2022-04-26 13:38:28 +00:00
|
|
|
TEST(CompletionTest, Concepts) {
|
|
|
|
Annotations Code(R"cpp(
|
|
|
|
template<class T>
|
|
|
|
concept A = sizeof(T) <= 8;
|
|
|
|
|
|
|
|
template<$tparam^A U>
|
|
|
|
int foo();
|
|
|
|
|
2023-07-04 16:31:49 +02:00
|
|
|
template<typename T>
|
|
|
|
int bar(T t) requires $expr^A<int>;
|
|
|
|
|
2022-04-26 13:38:28 +00:00
|
|
|
template<class T>
|
2023-07-04 16:31:49 +02:00
|
|
|
concept b = $expr^A && $expr^sizeof(T) % 2 == 0 || $expr^A && sizeof(T) == 1;
|
|
|
|
|
|
|
|
$toplevel^A auto i = 19;
|
|
|
|
|
|
|
|
template<$toplevel^A auto i> void constrainedNTTP();
|
2022-04-26 13:38:28 +00:00
|
|
|
|
2023-07-04 16:31:49 +02:00
|
|
|
// FIXME: The first parameter should be dropped in this case.
|
|
|
|
void abbreviated($expr^A auto x) {}
|
2022-04-26 13:38:28 +00:00
|
|
|
)cpp");
|
|
|
|
TestTU TU;
|
|
|
|
TU.Code = Code.code().str();
|
|
|
|
TU.ExtraArgs = {"-std=c++20"};
|
|
|
|
|
2023-07-04 16:31:49 +02:00
|
|
|
auto Sym = conceptSym("same_as");
|
|
|
|
Sym.Signature = "<typename Tp, typename Up>";
|
|
|
|
Sym.CompletionSnippetSuffix = "<${1:typename Tp}, ${2:typename Up}>";
|
|
|
|
std::vector<Symbol> Syms = {Sym};
|
2022-04-26 13:38:28 +00:00
|
|
|
for (auto P : Code.points("tparam")) {
|
2023-07-04 16:31:49 +02:00
|
|
|
ASSERT_THAT(
|
|
|
|
completions(TU, P, Syms).Completions,
|
|
|
|
AllOf(Contains(AllOf(named("A"), signature(""), snippetSuffix(""))),
|
|
|
|
Contains(AllOf(named("same_as"), signature("<typename Up>"),
|
|
|
|
snippetSuffix("<${2:typename Up}>"))),
|
|
|
|
Contains(named("class")), Contains(named("typename"))))
|
2022-04-26 13:38:28 +00:00
|
|
|
<< "Completing template parameter at position " << P;
|
|
|
|
}
|
|
|
|
|
2023-07-04 16:31:49 +02:00
|
|
|
for (auto P : Code.points("toplevel")) {
|
|
|
|
EXPECT_THAT(
|
|
|
|
completions(TU, P, Syms).Completions,
|
|
|
|
AllOf(Contains(AllOf(named("A"), signature(""), snippetSuffix(""))),
|
|
|
|
Contains(AllOf(named("same_as"), signature("<typename Up>"),
|
|
|
|
snippetSuffix("<${2:typename Up}>")))))
|
|
|
|
<< "Completing 'requires' expression at position " << P;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto P : Code.points("expr")) {
|
|
|
|
EXPECT_THAT(
|
|
|
|
completions(TU, P, Syms).Completions,
|
|
|
|
AllOf(Contains(AllOf(named("A"), signature("<class T>"),
|
|
|
|
snippetSuffix("<${1:class T}>"))),
|
|
|
|
Contains(AllOf(
|
|
|
|
named("same_as"), signature("<typename Tp, typename Up>"),
|
|
|
|
snippetSuffix("<${1:typename Tp}, ${2:typename Up}>")))))
|
2022-04-26 13:38:28 +00:00
|
|
|
<< "Completing 'requires' expression at position " << P;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-09 09:36:04 +01:00
|
|
|
TEST(SignatureHelp, DocFormat) {
|
|
|
|
Annotations Code(R"cpp(
|
|
|
|
// Comment `with` markup.
|
|
|
|
void foo(int);
|
|
|
|
void bar() { foo(^); }
|
|
|
|
)cpp");
|
|
|
|
for (auto DocumentationFormat :
|
|
|
|
{MarkupKind::PlainText, MarkupKind::Markdown}) {
|
|
|
|
auto Sigs = signatures(Code.code(), Code.point(), /*IndexSymbols=*/{},
|
|
|
|
DocumentationFormat);
|
|
|
|
ASSERT_EQ(Sigs.signatures.size(), 1U);
|
|
|
|
EXPECT_EQ(Sigs.signatures[0].documentation.kind, DocumentationFormat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-29 04:16:47 +01:00
|
|
|
TEST(SignatureHelp, TemplateArguments) {
|
|
|
|
std::string Top = R"cpp(
|
|
|
|
template <typename T, int> bool foo(char);
|
|
|
|
template <int I, int> bool foo(float);
|
|
|
|
)cpp";
|
|
|
|
|
|
|
|
auto First = signatures(Top + "bool x = foo<^");
|
|
|
|
EXPECT_THAT(
|
|
|
|
First.signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
UnorderedElementsAre(sig("foo<[[typename T]], [[int]]>() -> bool"),
|
|
|
|
sig("foo<[[int I]], [[int]]>() -> bool")));
|
2021-12-29 04:16:47 +01:00
|
|
|
EXPECT_EQ(First.activeParameter, 0);
|
|
|
|
|
|
|
|
auto Second = signatures(Top + "bool x = foo<1, ^");
|
|
|
|
EXPECT_THAT(Second.signatures,
|
2022-02-01 10:14:07 +00:00
|
|
|
ElementsAre(sig("foo<[[int I]], [[int]]>() -> bool")));
|
2021-12-29 04:16:47 +01:00
|
|
|
EXPECT_EQ(Second.activeParameter, 1);
|
|
|
|
}
|
|
|
|
|
2023-05-03 10:50:46 +02:00
|
|
|
TEST(CompletionTest, DoNotCrash) {
|
|
|
|
llvm::StringLiteral Cases[] = {
|
|
|
|
R"cpp(
|
|
|
|
template <typename = int> struct Foo {};
|
|
|
|
auto a = [x(3)](Foo<^>){};
|
|
|
|
)cpp",
|
|
|
|
};
|
|
|
|
for (auto Case : Cases) {
|
|
|
|
SCOPED_TRACE(Case);
|
|
|
|
auto Completions = completions(Case);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-05 07:20:26 +00:00
|
|
|
} // namespace
|
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|