diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -438,7 +438,7 @@ if (C.SemaResult) { bool IsPattern = C.SemaResult->Kind == CodeCompletionResult::RK_Pattern; getSignature(*SemaCCS, &S.Signature, &S.SnippetSuffix, - &Completion.RequiredQualifier, IsPattern); + &Completion.RequiredQualifier, IsPattern, C.SemaResult); if (!C.SemaResult->FunctionCanBeCall) S.SnippetSuffix.clear(); S.ReturnType = getReturnType(*SemaCCS); diff --git a/clang-tools-extra/clangd/CodeCompletionStrings.h b/clang-tools-extra/clangd/CodeCompletionStrings.h --- a/clang-tools-extra/clangd/CodeCompletionStrings.h +++ b/clang-tools-extra/clangd/CodeCompletionStrings.h @@ -47,7 +47,8 @@ void getSignature(const CodeCompletionString &CCS, std::string *Signature, std::string *Snippet, std::string *RequiredQualifiers = nullptr, - bool CompletingPattern = false); + bool CompletingPattern = false, + const CodeCompletionResult *CCR = nullptr); /// Assembles formatted documentation for a completion result. This includes /// documentation comments and other relevant information like annotations. diff --git a/clang-tools-extra/clangd/CodeCompletionStrings.cpp b/clang-tools-extra/clangd/CodeCompletionStrings.cpp --- a/clang-tools-extra/clangd/CodeCompletionStrings.cpp +++ b/clang-tools-extra/clangd/CodeCompletionStrings.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "CodeCompletionStrings.h" +#include "clang-c/Index.h" #include "clang/AST/ASTContext.h" #include "clang/AST/RawCommentList.h" #include "clang/Basic/SourceManager.h" @@ -96,7 +97,7 @@ void getSignature(const CodeCompletionString &CCS, std::string *Signature, std::string *Snippet, std::string *RequiredQualifiers, - bool CompletingPattern) { + bool CompletingPattern, const CodeCompletionResult *CCR) { // Placeholder with this index will be ${0:…} to mark final cursor position. // Usually we do not add $0, so the cursor is placed at end of completed text. unsigned CursorSnippetArg = std::numeric_limits::max(); @@ -111,6 +112,23 @@ return C.Kind == CodeCompletionString::CK_Placeholder; }); } + // If the result kind of CCR is `RK_Pattern`, it doesn't always mean we're + // completing a chunk of statements. Constructors defined in base class, for + // example, are considered as a type of pattern, with the cursor type set to + // CXCursor_Constructor. + // We have to discriminate these cases manually in order to avoid providing + // incorrect placeholder `$0` which should have been a normal parameter. + bool ShouldPatchPlaceholder0 = CompletingPattern && [CCR] { + if (!CCR) + return true; + // The process of CCR construction employs `clang::getCursorKindForDecl` to + // obtain cursor kind for Decls, otherwise CursorKind would be set by + // constructor. Note that the default value is CXCursor_NotImplemented. + if (CCR->CursorKind == CXCursorKind::CXCursor_Constructor || + CCR->CursorKind == CXCursorKind::CXCursor_Destructor) + return false; + return true; + }(); unsigned SnippetArg = 0; bool HadObjCArguments = false; bool HadInformativeChunks = false; @@ -185,14 +203,14 @@ *Snippet += Chunk.Text; break; case CodeCompletionString::CK_Optional: - assert(Chunk.Optional); + assert(Chunk.Optional); // No need to create placeholders for default arguments in Snippet. appendOptionalChunk(*Chunk.Optional, Signature); break; case CodeCompletionString::CK_Placeholder: *Signature += Chunk.Text; ++SnippetArg; - if (SnippetArg == CursorSnippetArg) { + if (SnippetArg == CursorSnippetArg && ShouldPatchPlaceholder0) { // We'd like to make $0 a placeholder too, but vscode does not support // this (https://github.com/microsoft/vscode/issues/152837). *Snippet += "$0"; diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3450,6 +3450,22 @@ EXPECT_THAT(Results.Completions, Contains(AllOf(named("while_foo"), snippetSuffix("(${1:int a}, ${2:int b})")))); + + 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})")))); } TEST(CompletionTest, WorksWithNullType) {