Skip to content

Commit 4de5ed0

Browse files
[clangd] Autocomplete fixes for methods
1 parent 093c738 commit 4de5ed0

File tree

7 files changed

+132
-36
lines changed

7 files changed

+132
-36
lines changed

clang-tools-extra/clangd/CodeCompletionStrings.cpp

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,29 @@ void appendEscapeSnippet(const llvm::StringRef Text, std::string *Out) {
3939
}
4040
}
4141

42-
void appendOptionalChunk(const CodeCompletionString &CCS, std::string *Out) {
42+
/// Removes the value for defaults arguments.
43+
static void addWithoutValue(std::string *Out, const std::string &ToAdd) {
44+
size_t Val = ToAdd.find('=');
45+
if (Val != ToAdd.npos)
46+
*Out += ToAdd.substr(0, Val - 1); // removing value in definition
47+
else
48+
*Out += ToAdd;
49+
}
50+
51+
void appendOptionalChunk(const CodeCompletionString &CCS, std::string *Out,
52+
bool RemoveValues = false) {
4353
for (const CodeCompletionString::Chunk &C : CCS) {
4454
switch (C.Kind) {
4555
case CodeCompletionString::CK_Optional:
4656
assert(C.Optional &&
4757
"Expected the optional code completion string to be non-null.");
48-
appendOptionalChunk(*C.Optional, Out);
58+
appendOptionalChunk(*C.Optional, Out, RemoveValues);
4959
break;
5060
default:
51-
*Out += C.Text;
61+
if (RemoveValues)
62+
addWithoutValue(Out, C.Text);
63+
else
64+
*Out += C.Text;
5265
break;
5366
}
5467
}
@@ -184,6 +197,7 @@ void getSignature(const CodeCompletionString &CCS, std::string *Signature,
184197
unsigned SnippetArg = 0;
185198
bool HadObjCArguments = false;
186199
bool HadInformativeChunks = false;
200+
int IsTemplateArgument = 0;
187201

188202
std::optional<unsigned> TruncateSnippetAt;
189203
for (const auto &Chunk : CCS) {
@@ -252,33 +266,50 @@ void getSignature(const CodeCompletionString &CCS, std::string *Signature,
252266
}
253267
}
254268
break;
255-
case CodeCompletionString::CK_Text:
269+
case CodeCompletionString::CK_FunctionQualifier:
270+
if (!IncludeFunctionArguments) // if not a call
271+
*Snippet += Chunk.Text;
256272
*Signature += Chunk.Text;
273+
break;
274+
case CodeCompletionString::CK_Text:
257275
*Snippet += Chunk.Text;
276+
*Signature += Chunk.Text;
258277
break;
259278
case CodeCompletionString::CK_Optional:
260279
assert(Chunk.Optional);
261280
// No need to create placeholders for default arguments in Snippet.
262281
appendOptionalChunk(*Chunk.Optional, Signature);
282+
if (!IncludeFunctionArguments) { // complete args with default value in
283+
// definition
284+
appendOptionalChunk(*Chunk.Optional, Snippet, /*RemoveValues=*/true);
285+
}
263286
break;
264287
case CodeCompletionString::CK_Placeholder:
265288
*Signature += Chunk.Text;
266-
++SnippetArg;
267-
if (SnippetArg == CursorSnippetArg) {
268-
// We'd like to make $0 a placeholder too, but vscode does not support
269-
// this (https://github.com/microsoft/vscode/issues/152837).
270-
*Snippet += "$0";
289+
if (IncludeFunctionArguments || IsTemplateArgument) {
290+
++SnippetArg;
291+
if (SnippetArg == CursorSnippetArg) {
292+
// We'd like to make $0 a placeholder too, but vscode does not support
293+
// this (https://github.com/microsoft/vscode/issues/152837).
294+
*Snippet += "$0";
295+
} else {
296+
*Snippet += "${" + std::to_string(SnippetArg) + ':';
297+
appendEscapeSnippet(Chunk.Text, Snippet);
298+
*Snippet += '}';
299+
}
271300
} else {
272-
*Snippet += "${" + std::to_string(SnippetArg) + ':';
273-
appendEscapeSnippet(Chunk.Text, Snippet);
274-
*Snippet += '}';
301+
*Snippet += Chunk.Text;
275302
}
276303
break;
277304
case CodeCompletionString::CK_Informative:
278305
HadInformativeChunks = true;
279306
// For example, the word "const" for a const method, or the name of
280307
// the base class for methods that are part of the base class.
281308
*Signature += Chunk.Text;
309+
if (strcmp(Chunk.Text, " const") == 0 ||
310+
strcmp(Chunk.Text, " volatile") == 0 ||
311+
strcmp(Chunk.Text, " restrict") == 0)
312+
*Snippet += Chunk.Text;
282313
// Don't put the informative chunks in the snippet.
283314
break;
284315
case CodeCompletionString::CK_ResultType:
@@ -290,21 +321,22 @@ void getSignature(const CodeCompletionString &CCS, std::string *Signature,
290321
llvm_unreachable("Unexpected CK_CurrentParameter while collecting "
291322
"CompletionItems");
292323
break;
324+
case CodeCompletionString::CK_LeftAngle:
325+
IsTemplateArgument++;
326+
*Signature += Chunk.Text;
327+
*Snippet += Chunk.Text;
328+
break;
329+
case CodeCompletionString::CK_RightAngle:
330+
IsTemplateArgument--;
331+
*Signature += Chunk.Text;
332+
*Snippet += Chunk.Text;
333+
break;
293334
case CodeCompletionString::CK_LeftParen:
294-
// We're assuming that a LeftParen in a declaration starts a function
295-
// call, and arguments following the parenthesis could be discarded if
296-
// IncludeFunctionArguments is false.
297-
if (!IncludeFunctionArguments &&
298-
ResultKind == CodeCompletionResult::RK_Declaration)
299-
TruncateSnippetAt.emplace(Snippet->size());
300-
[[fallthrough]];
301335
case CodeCompletionString::CK_RightParen:
302336
case CodeCompletionString::CK_LeftBracket:
303337
case CodeCompletionString::CK_RightBracket:
304338
case CodeCompletionString::CK_LeftBrace:
305339
case CodeCompletionString::CK_RightBrace:
306-
case CodeCompletionString::CK_LeftAngle:
307-
case CodeCompletionString::CK_RightAngle:
308340
case CodeCompletionString::CK_Comma:
309341
case CodeCompletionString::CK_Colon:
310342
case CodeCompletionString::CK_SemiColon:
@@ -319,8 +351,6 @@ void getSignature(const CodeCompletionString &CCS, std::string *Signature,
319351
break;
320352
}
321353
}
322-
if (TruncateSnippetAt)
323-
*Snippet = Snippet->substr(0, *TruncateSnippetAt);
324354
}
325355

326356
std::string formatDocumentation(const CodeCompletionString &CCS,

clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -586,14 +586,14 @@ TEST(CompletionTest, HeuristicsForMemberFunctionCompletion) {
586586
auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts);
587587
EXPECT_THAT(Results.Completions,
588588
Contains(AllOf(named("method"), signature("(int) const"),
589-
snippetSuffix(""))));
589+
snippetSuffix("(int) const"))));
590590
// We don't have any arguments to deduce against if this isn't a call.
591591
// Thus, we should emit these deducible template arguments explicitly.
592592
EXPECT_THAT(
593593
Results.Completions,
594-
Contains(AllOf(named("generic"),
595-
signature("<typename T, typename U>(U, V)"),
596-
snippetSuffix("<${1:typename T}, ${2:typename U}>"))));
594+
Contains(
595+
AllOf(named("generic"), signature("<typename T, typename U>(U, V)"),
596+
snippetSuffix("<${1:typename T}, ${2:typename U}>(U, V)"))));
597597
}
598598

599599
for (const auto &P : Code.points("canBeCall")) {
@@ -620,6 +620,27 @@ TEST(CompletionTest, HeuristicsForMemberFunctionCompletion) {
620620
}
621621
}
622622

623+
TEST(CompletionTest, DefaultArgsWithValues) {
624+
clangd::CodeCompleteOptions Opts;
625+
Opts.EnableSnippets = true;
626+
auto Results = completions(
627+
R"cpp(
628+
struct Arg {
629+
Arg(int a, int b);
630+
};
631+
struct Foo {
632+
void foo(int x = 42, int y = 0, Arg arg = Arg(42, 0));
633+
};
634+
void Foo::foo^
635+
)cpp",
636+
/*IndexSymbols=*/{}, Opts);
637+
EXPECT_THAT(Results.Completions,
638+
Contains(AllOf(
639+
named("foo"),
640+
signature("(int x = 42, int y = 0, Arg arg = Arg(42, 0))"),
641+
snippetSuffix("(int x, int y, Arg arg)"))));
642+
}
643+
623644
TEST(CompletionTest, NoSnippetsInUsings) {
624645
clangd::CodeCompleteOptions Opts;
625646
Opts.EnableSnippets = true;
@@ -4490,14 +4511,14 @@ TEST(CompletionTest, SkipExplicitObjectParameter) {
44904511
EXPECT_THAT(
44914512
Result.Completions,
44924513
ElementsAre(AllOf(named("foo"), signature("<class self:auto>(int arg)"),
4493-
snippetSuffix("<${1:class self:auto}>"))));
4514+
snippetSuffix("<${1:class self:auto}>(int arg)"))));
44944515
}
44954516
{
44964517
auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"),
44974518
Preamble.get(), Inputs, Opts);
44984519
EXPECT_THAT(Result.Completions,
44994520
ElementsAre(AllOf(named("bar"), signature("(int arg)"),
4500-
snippetSuffix(""))));
4521+
snippetSuffix("(int arg)"))));
45014522
}
45024523
}
45034524

clang-tools-extra/clangd/unittests/CodeCompletionStringsTests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ TEST_F(CompletionStringTest, DropFunctionArguments) {
177177
/*IncludeFunctionArguments=*/false);
178178
// Arguments dropped from snippet, kept in signature.
179179
EXPECT_EQ(Signature, "<typename T, int U>(arg1, arg2)");
180-
EXPECT_EQ(Snippet, "<${1:typename T}, ${2:int U}>");
180+
EXPECT_EQ(Snippet, "<${1:typename T}, ${2:int U}>(arg1, arg2)");
181181
}
182182

183183
TEST_F(CompletionStringTest, IgnoreInformativeQualifier) {

clang/include/clang/Sema/CodeCompleteConsumer.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,12 @@ class CodeCompletionString {
473473
/// A piece of text that describes something about the result but
474474
/// should not be inserted into the buffer.
475475
CK_Informative,
476+
477+
/// A piece of text that holds function qualifiers. Should be inserted
478+
/// into the buffer only in definition, not on call.
479+
/// e.g. const for a function or a method.
480+
CK_FunctionQualifier,
481+
476482
/// A piece of text that describes the type of an entity or, for
477483
/// functions and methods, the return type.
478484
CK_ResultType,
@@ -534,7 +540,7 @@ class CodeCompletionString {
534540

535541
union {
536542
/// The text string associated with a CK_Text, CK_Placeholder,
537-
/// CK_Informative, or CK_Comma chunk.
543+
/// CK_Informative, CK_FunctionQualifier, or CK_Comma chunk.
538544
/// The string is owned by the chunk and will be deallocated
539545
/// (with delete[]) when the chunk is destroyed.
540546
const char *Text;
@@ -561,6 +567,9 @@ class CodeCompletionString {
561567
/// Create a new informative chunk.
562568
static Chunk CreateInformative(const char *Informative);
563569

570+
/// Create a new declaration informative chunk.
571+
static Chunk CreateFunctionQualifier(const char *FunctionQualifier);
572+
564573
/// Create a new result type chunk.
565574
static Chunk CreateResultType(const char *ResultType);
566575

@@ -737,6 +746,9 @@ class CodeCompletionBuilder {
737746
/// Add a new informative chunk.
738747
void AddInformativeChunk(const char *Text);
739748

749+
/// Add a new function qualifier chunk.
750+
void AddFunctionQualifierChunk(const char *Text);
751+
740752
/// Add a new result-type chunk.
741753
void AddResultTypeChunk(const char *ResultType);
742754

clang/lib/Sema/CodeCompleteConsumer.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ CodeCompletionString::Chunk::Chunk(ChunkKind Kind, const char *Text)
183183
case CK_Text:
184184
case CK_Placeholder:
185185
case CK_Informative:
186+
case CK_FunctionQualifier:
186187
case CK_ResultType:
187188
case CK_CurrentParameter:
188189
this->Text = Text;
@@ -272,6 +273,12 @@ CodeCompletionString::Chunk::CreateInformative(const char *Informative) {
272273
return Chunk(CK_Informative, Informative);
273274
}
274275

276+
CodeCompletionString::Chunk
277+
CodeCompletionString::Chunk::CreateFunctionQualifier(
278+
const char *FunctionQualifier) {
279+
return Chunk(CK_FunctionQualifier, FunctionQualifier);
280+
}
281+
275282
CodeCompletionString::Chunk
276283
CodeCompletionString::Chunk::CreateResultType(const char *ResultType) {
277284
return Chunk(CK_ResultType, ResultType);
@@ -326,6 +333,7 @@ std::string CodeCompletionString::getAsString() const {
326333
OS << "<#" << C.Text << "#>";
327334
break;
328335
case CK_Informative:
336+
case CK_FunctionQualifier:
329337
case CK_ResultType:
330338
OS << "[#" << C.Text << "#]";
331339
break;
@@ -461,6 +469,10 @@ void CodeCompletionBuilder::AddInformativeChunk(const char *Text) {
461469
Chunks.push_back(Chunk::CreateInformative(Text));
462470
}
463471

472+
void CodeCompletionBuilder::AddFunctionQualifierChunk(const char *Text) {
473+
Chunks.push_back(Chunk::CreateFunctionQualifier(Text));
474+
}
475+
464476
void CodeCompletionBuilder::AddResultTypeChunk(const char *ResultType) {
465477
Chunks.push_back(Chunk::CreateResultType(ResultType));
466478
}
@@ -728,6 +740,7 @@ static std::string getOverloadAsString(const CodeCompletionString &CCS) {
728740
for (auto &C : CCS) {
729741
switch (C.Kind) {
730742
case CodeCompletionString::CK_Informative:
743+
case CodeCompletionString::CK_FunctionQualifier:
731744
case CodeCompletionString::CK_ResultType:
732745
OS << "[#" << C.Text << "#]";
733746
break;

clang/lib/Sema/SemaCodeComplete.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3440,17 +3440,17 @@ static void AddFunctionTypeQuals(CodeCompletionBuilder &Result,
34403440

34413441
// Handle single qualifiers without copying
34423442
if (Quals.hasOnlyConst()) {
3443-
Result.AddInformativeChunk(" const");
3443+
Result.AddFunctionQualifierChunk(" const");
34443444
return;
34453445
}
34463446

34473447
if (Quals.hasOnlyVolatile()) {
3448-
Result.AddInformativeChunk(" volatile");
3448+
Result.AddFunctionQualifierChunk(" volatile");
34493449
return;
34503450
}
34513451

34523452
if (Quals.hasOnlyRestrict()) {
3453-
Result.AddInformativeChunk(" restrict");
3453+
Result.AddFunctionQualifierChunk(" restrict");
34543454
return;
34553455
}
34563456

@@ -3462,7 +3462,7 @@ static void AddFunctionTypeQuals(CodeCompletionBuilder &Result,
34623462
QualsStr += " volatile";
34633463
if (Quals.hasRestrict())
34643464
QualsStr += " restrict";
3465-
Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
3465+
Result.AddFunctionQualifierChunk(Result.getAllocator().CopyString(QualsStr));
34663466
}
34673467

34683468
static void
@@ -6859,12 +6859,26 @@ void SemaCodeCompletion::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS,
68596859
// resolves to a dependent record.
68606860
DeclContext *Ctx = SemaRef.computeDeclContext(SS, /*EnteringContext=*/true);
68616861

6862+
// This is used to change context to access private methods for completion
6863+
DeclContext *SavedContext = nullptr;
6864+
if (!SemaRef.CurContext->isFunctionOrMethod() &&
6865+
!SemaRef.CurContext->isRecord()) {
6866+
// We are in global scope or namespace
6867+
// -> likely a method definition
6868+
SavedContext = SemaRef.CurContext;
6869+
SemaRef.CurContext = Ctx; // Simulate that we are in class scope
6870+
// to access private methods
6871+
}
6872+
68626873
// Try to instantiate any non-dependent declaration contexts before
68636874
// we look in them. Bail out if we fail.
68646875
NestedNameSpecifier NNS = SS.getScopeRep();
68656876
if (NNS && !NNS.isDependent()) {
6866-
if (Ctx == nullptr || SemaRef.RequireCompleteDeclContext(SS, Ctx))
6877+
if (Ctx == nullptr || SemaRef.RequireCompleteDeclContext(SS, Ctx)) {
6878+
if (SavedContext)
6879+
SemaRef.CurContext = SavedContext;
68676880
return;
6881+
}
68686882
}
68696883

68706884
ResultBuilder Results(SemaRef, CodeCompleter->getAllocator(),
@@ -6910,6 +6924,8 @@ void SemaCodeCompletion::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS,
69106924
/*IncludeDependentBases=*/true,
69116925
CodeCompleter->loadExternal());
69126926
}
6927+
if (SavedContext)
6928+
SemaRef.CurContext = SavedContext;
69136929

69146930
HandleCodeCompleteResults(&SemaRef, CodeCompleter,
69156931
Results.getCompletionContext(), Results.data(),

clang/tools/libclang/CIndexCodeCompletion.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ clang_getCompletionChunkKind(CXCompletionString completion_string,
7070
case CodeCompletionString::CK_Placeholder:
7171
return CXCompletionChunk_Placeholder;
7272
case CodeCompletionString::CK_Informative:
73+
case CodeCompletionString::CK_FunctionQualifier:
74+
// as FunctionQualifier are informative, except for completion.
7375
return CXCompletionChunk_Informative;
7476
case CodeCompletionString::CK_ResultType:
7577
return CXCompletionChunk_ResultType;
@@ -120,6 +122,7 @@ CXString clang_getCompletionChunkText(CXCompletionString completion_string,
120122
case CodeCompletionString::CK_Placeholder:
121123
case CodeCompletionString::CK_CurrentParameter:
122124
case CodeCompletionString::CK_Informative:
125+
case CodeCompletionString::CK_FunctionQualifier:
123126
case CodeCompletionString::CK_LeftParen:
124127
case CodeCompletionString::CK_RightParen:
125128
case CodeCompletionString::CK_LeftBracket:
@@ -159,6 +162,7 @@ clang_getCompletionChunkCompletionString(CXCompletionString completion_string,
159162
case CodeCompletionString::CK_Placeholder:
160163
case CodeCompletionString::CK_CurrentParameter:
161164
case CodeCompletionString::CK_Informative:
165+
case CodeCompletionString::CK_FunctionQualifier:
162166
case CodeCompletionString::CK_LeftParen:
163167
case CodeCompletionString::CK_RightParen:
164168
case CodeCompletionString::CK_LeftBracket:

0 commit comments

Comments
 (0)