forked from OSchip/llvm-project
[clangd] Do not insert parentheses when completing a using declaration
Summary: Would be nice to also fix this in clang, but that looks like more work if we want to preserve signatures in informative chunks. Fixes https://github.com/clangd/clangd/issues/118 Reviewers: kadircet Reviewed By: kadircet Subscribers: merge_guards_bot, MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D69382
This commit is contained in:
parent
7f19dd1ebf
commit
d9971d0b2e
|
@ -253,9 +253,10 @@ struct CodeCompletionBuilder {
|
|||
const IncludeInserter &Includes,
|
||||
llvm::StringRef FileName,
|
||||
CodeCompletionContext::Kind ContextKind,
|
||||
const CodeCompleteOptions &Opts)
|
||||
const CodeCompleteOptions &Opts, bool GenerateSnippets)
|
||||
: ASTCtx(ASTCtx), ExtractDocumentation(Opts.IncludeComments),
|
||||
EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets) {
|
||||
EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets),
|
||||
GenerateSnippets(GenerateSnippets) {
|
||||
add(C, SemaCCS);
|
||||
if (C.SemaResult) {
|
||||
assert(ASTCtx);
|
||||
|
@ -419,6 +420,8 @@ private:
|
|||
}
|
||||
|
||||
std::string summarizeSnippet() const {
|
||||
if (!GenerateSnippets)
|
||||
return "";
|
||||
auto *Snippet = onlyValue<&BundledEntry::SnippetSuffix>();
|
||||
if (!Snippet)
|
||||
// All bundles are function calls.
|
||||
|
@ -476,6 +479,8 @@ private:
|
|||
llvm::SmallVector<BundledEntry, 1> Bundled;
|
||||
bool ExtractDocumentation;
|
||||
bool EnableFunctionArgSnippets;
|
||||
/// When false, no snippets are generated argument lists.
|
||||
bool GenerateSnippets;
|
||||
};
|
||||
|
||||
// Determine the symbol ID for a Sema code completion result, if possible.
|
||||
|
@ -1204,6 +1209,7 @@ class CodeCompleteFlow {
|
|||
// Sema takes ownership of Recorder. Recorder is valid until Sema cleanup.
|
||||
CompletionRecorder *Recorder = nullptr;
|
||||
CodeCompletionContext::Kind CCContextKind = CodeCompletionContext::CCC_Other;
|
||||
bool IsUsingDeclaration = false;
|
||||
// Counters for logging.
|
||||
int NSema = 0, NIndex = 0, NSemaAndIndex = 0, NIdent = 0;
|
||||
bool Incomplete = false; // Would more be available with a higher limit?
|
||||
|
@ -1254,6 +1260,7 @@ public:
|
|||
auto RecorderOwner = std::make_unique<CompletionRecorder>(Opts, [&]() {
|
||||
assert(Recorder && "Recorder is not set");
|
||||
CCContextKind = Recorder->CCContext.getKind();
|
||||
IsUsingDeclaration = Recorder->CCContext.isUsingDeclaration();
|
||||
auto Style = getFormatStyleForFile(
|
||||
SemaCCInput.FileName, SemaCCInput.Contents, SemaCCInput.VFS.get());
|
||||
// If preprocessor was run, inclusions from preprocessor callback should
|
||||
|
@ -1289,11 +1296,12 @@ public:
|
|||
SPAN_ATTACH(Tracer, "sema_completion_kind",
|
||||
getCompletionKindString(CCContextKind));
|
||||
log("Code complete: sema context {0}, query scopes [{1}] (AnyScope={2}), "
|
||||
"expected type {3}",
|
||||
"expected type {3}{4}",
|
||||
getCompletionKindString(CCContextKind),
|
||||
llvm::join(QueryScopes.begin(), QueryScopes.end(), ","), AllScopes,
|
||||
PreferredType ? Recorder->CCContext.getPreferredType().getAsString()
|
||||
: "<none>");
|
||||
: "<none>",
|
||||
IsUsingDeclaration ? ", inside using declaration" : "");
|
||||
});
|
||||
|
||||
Recorder = RecorderOwner.get();
|
||||
|
@ -1328,6 +1336,7 @@ public:
|
|||
HeuristicPrefix = guessCompletionPrefix(Content, Offset);
|
||||
populateContextWords(Content);
|
||||
CCContextKind = CodeCompletionContext::CCC_Recovery;
|
||||
IsUsingDeclaration = false;
|
||||
Filter = FuzzyMatcher(HeuristicPrefix.Name);
|
||||
auto Pos = offsetToPosition(Content, Offset);
|
||||
ReplacedRange.start = ReplacedRange.end = Pos;
|
||||
|
@ -1665,7 +1674,8 @@ private:
|
|||
if (!Builder)
|
||||
Builder.emplace(Recorder ? &Recorder->CCSema->getASTContext() : nullptr,
|
||||
Item, SemaCCS, QueryScopes, *Inserter, FileName,
|
||||
CCContextKind, Opts);
|
||||
CCContextKind, Opts,
|
||||
/*GenerateSnippets=*/!IsUsingDeclaration);
|
||||
else
|
||||
Builder->add(Item, SemaCCS);
|
||||
}
|
||||
|
|
|
@ -428,6 +428,48 @@ TEST(CompletionTest, Snippets) {
|
|||
SnippetSuffix("(${1:int i}, ${2:const float f})")));
|
||||
}
|
||||
|
||||
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,
|
||||
ElementsAre(AllOf(Named("func"), Labeled("func(int a, int b)"),
|
||||
SnippetSuffix(""))));
|
||||
|
||||
// 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,
|
||||
ElementsAre(AllOf(Named("func"), Labeled("func(int a, int b)"),
|
||||
SnippetSuffix(""))));
|
||||
|
||||
// Check all-scopes completions too.
|
||||
Opts.AllScopes = true;
|
||||
Results = completions(R"cpp(
|
||||
using ^;
|
||||
)cpp",
|
||||
/*IndexSymbols=*/{Func}, Opts);
|
||||
EXPECT_THAT(Results.Completions,
|
||||
Contains(AllOf(Named("func"), Labeled("ns::func(int a, int b)"),
|
||||
SnippetSuffix(""))));
|
||||
}
|
||||
|
||||
TEST(CompletionTest, Kinds) {
|
||||
auto Results = completions(
|
||||
R"cpp(
|
||||
|
|
|
@ -1758,13 +1758,13 @@ private:
|
|||
bool EnteringContext, IdentifierInfo &II,
|
||||
CXXScopeSpec &SS);
|
||||
|
||||
bool ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
|
||||
ParsedType ObjectType,
|
||||
bool ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, ParsedType ObjectType,
|
||||
bool EnteringContext,
|
||||
bool *MayBePseudoDestructor = nullptr,
|
||||
bool IsTypename = false,
|
||||
IdentifierInfo **LastII = nullptr,
|
||||
bool OnlyNamespace = false);
|
||||
bool OnlyNamespace = false,
|
||||
bool InUsingDeclaration = false);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// C++11 5.1.2: Lambda expressions
|
||||
|
|
|
@ -339,6 +339,11 @@ public:
|
|||
private:
|
||||
Kind CCKind;
|
||||
|
||||
/// Indicates whether we are completing a name of a using declaration, e.g.
|
||||
/// using ^;
|
||||
/// using a::^;
|
||||
bool IsUsingDeclaration;
|
||||
|
||||
/// The type that would prefer to see at this point (e.g., the type
|
||||
/// of an initializer or function parameter).
|
||||
QualType PreferredType;
|
||||
|
@ -359,12 +364,13 @@ private:
|
|||
|
||||
public:
|
||||
/// Construct a new code-completion context of the given kind.
|
||||
CodeCompletionContext(Kind CCKind) : CCKind(CCKind), SelIdents(None) {}
|
||||
CodeCompletionContext(Kind CCKind)
|
||||
: CCKind(CCKind), IsUsingDeclaration(false), SelIdents(None) {}
|
||||
|
||||
/// Construct a new code-completion context of the given kind.
|
||||
CodeCompletionContext(Kind CCKind, QualType T,
|
||||
ArrayRef<IdentifierInfo *> SelIdents = None)
|
||||
: CCKind(CCKind), SelIdents(SelIdents) {
|
||||
: CCKind(CCKind), IsUsingDeclaration(false), SelIdents(SelIdents) {
|
||||
if (CCKind == CCC_DotMemberAccess || CCKind == CCC_ArrowMemberAccess ||
|
||||
CCKind == CCC_ObjCPropertyAccess || CCKind == CCC_ObjCClassMessage ||
|
||||
CCKind == CCC_ObjCInstanceMessage)
|
||||
|
@ -373,6 +379,9 @@ public:
|
|||
PreferredType = T;
|
||||
}
|
||||
|
||||
bool isUsingDeclaration() const { return IsUsingDeclaration; }
|
||||
void setIsUsingDeclaration(bool V) { IsUsingDeclaration = V; }
|
||||
|
||||
/// Retrieve the kind of code-completion context.
|
||||
Kind getKind() const { return CCKind; }
|
||||
|
||||
|
|
|
@ -11216,7 +11216,8 @@ public:
|
|||
void CodeCompleteAfterIf(Scope *S);
|
||||
|
||||
void CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS, bool EnteringContext,
|
||||
QualType BaseType, QualType PreferredType);
|
||||
bool IsUsingDeclaration, QualType BaseType,
|
||||
QualType PreferredType);
|
||||
void CodeCompleteUsing(Scope *S);
|
||||
void CodeCompleteUsingDirective(Scope *S);
|
||||
void CodeCompleteNamespaceDecl(Scope *S);
|
||||
|
|
|
@ -600,7 +600,10 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
|
|||
if (ParseOptionalCXXScopeSpecifier(D.SS, nullptr, /*EnteringContext=*/false,
|
||||
/*MayBePseudoDtor=*/nullptr,
|
||||
/*IsTypename=*/false,
|
||||
/*LastII=*/&LastII))
|
||||
/*LastII=*/&LastII,
|
||||
/*OnlyNamespace=*/false,
|
||||
/*InUsingDeclaration=*/true))
|
||||
|
||||
return true;
|
||||
if (D.SS.isInvalid())
|
||||
return true;
|
||||
|
|
|
@ -143,13 +143,10 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
|
|||
/// \param OnlyNamespace If true, only considers namespaces in lookup.
|
||||
///
|
||||
/// \returns true if there was an error parsing a scope specifier
|
||||
bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
|
||||
ParsedType ObjectType,
|
||||
bool EnteringContext,
|
||||
bool *MayBePseudoDestructor,
|
||||
bool IsTypename,
|
||||
IdentifierInfo **LastII,
|
||||
bool OnlyNamespace) {
|
||||
bool Parser::ParseOptionalCXXScopeSpecifier(
|
||||
CXXScopeSpec &SS, ParsedType ObjectType, bool EnteringContext,
|
||||
bool *MayBePseudoDestructor, bool IsTypename, IdentifierInfo **LastII,
|
||||
bool OnlyNamespace, bool InUsingDeclaration) {
|
||||
assert(getLangOpts().CPlusPlus &&
|
||||
"Call sites of this function should be guarded by checking for C++");
|
||||
|
||||
|
@ -240,7 +237,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
|
|||
// Code completion for a nested-name-specifier, where the code
|
||||
// completion token follows the '::'.
|
||||
Actions.CodeCompleteQualifiedId(getCurScope(), SS, EnteringContext,
|
||||
ObjectType.get(),
|
||||
InUsingDeclaration, ObjectType.get(),
|
||||
SavedType.get(SS.getBeginLoc()));
|
||||
// Include code completion token into the range of the scope otherwise
|
||||
// when we try to annotate the scope tokens the dangling code completion
|
||||
|
|
|
@ -5330,18 +5330,21 @@ void Sema::CodeCompleteAfterIf(Scope *S) {
|
|||
}
|
||||
|
||||
void Sema::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS,
|
||||
bool EnteringContext, QualType BaseType,
|
||||
bool EnteringContext,
|
||||
bool IsUsingDeclaration, QualType BaseType,
|
||||
QualType PreferredType) {
|
||||
if (SS.isEmpty() || !CodeCompleter)
|
||||
return;
|
||||
|
||||
CodeCompletionContext CC(CodeCompletionContext::CCC_Symbol, PreferredType);
|
||||
CC.setIsUsingDeclaration(IsUsingDeclaration);
|
||||
CC.setCXXScopeSpecifier(SS);
|
||||
|
||||
// We want to keep the scope specifier even if it's invalid (e.g. the scope
|
||||
// "a::b::" is not corresponding to any context/namespace in the AST), since
|
||||
// it can be useful for global code completion which have information about
|
||||
// contexts/symbols that are not in the AST.
|
||||
if (SS.isInvalid()) {
|
||||
CodeCompletionContext CC(CodeCompletionContext::CCC_Symbol, PreferredType);
|
||||
CC.setCXXScopeSpecifier(SS);
|
||||
// As SS is invalid, we try to collect accessible contexts from the current
|
||||
// scope with a dummy lookup so that the completion consumer can try to
|
||||
// guess what the specified scope is.
|
||||
|
@ -5371,10 +5374,8 @@ void Sema::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS,
|
|||
if (!isDependentScopeSpecifier(SS) && RequireCompleteDeclContext(SS, Ctx))
|
||||
return;
|
||||
|
||||
ResultBuilder Results(
|
||||
*this, CodeCompleter->getAllocator(),
|
||||
CodeCompleter->getCodeCompletionTUInfo(),
|
||||
CodeCompletionContext(CodeCompletionContext::CCC_Symbol, PreferredType));
|
||||
ResultBuilder Results(*this, CodeCompleter->getAllocator(),
|
||||
CodeCompleter->getCodeCompletionTUInfo(), CC);
|
||||
if (!PreferredType.isNull())
|
||||
Results.setPreferredType(PreferredType);
|
||||
Results.EnterNewScope();
|
||||
|
@ -5403,23 +5404,21 @@ void Sema::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS,
|
|||
CodeCompleter->loadExternal());
|
||||
}
|
||||
|
||||
auto CC = Results.getCompletionContext();
|
||||
CC.setCXXScopeSpecifier(SS);
|
||||
|
||||
HandleCodeCompleteResults(this, CodeCompleter, CC, Results.data(),
|
||||
Results.size());
|
||||
HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
|
||||
Results.data(), Results.size());
|
||||
}
|
||||
|
||||
void Sema::CodeCompleteUsing(Scope *S) {
|
||||
if (!CodeCompleter)
|
||||
return;
|
||||
|
||||
// This can be both a using alias or using declaration, in the former we
|
||||
// expect a new name and a symbol in the latter case.
|
||||
CodeCompletionContext Context(CodeCompletionContext::CCC_SymbolOrNewName);
|
||||
Context.setIsUsingDeclaration(true);
|
||||
|
||||
ResultBuilder Results(*this, CodeCompleter->getAllocator(),
|
||||
CodeCompleter->getCodeCompletionTUInfo(),
|
||||
// This can be both a using alias or using
|
||||
// declaration, in the former we expect a new name and a
|
||||
// symbol in the latter case.
|
||||
CodeCompletionContext::CCC_SymbolOrNewName,
|
||||
CodeCompleter->getCodeCompletionTUInfo(), Context,
|
||||
&ResultBuilder::IsNestedNameSpecifier);
|
||||
Results.EnterNewScope();
|
||||
|
||||
|
|
Loading…
Reference in New Issue