Skip to content

Commit 128ce70

Browse files
committed
[CodeCompletion] Avoid spurious signature help for init-list args
Somewhat surprisingly, signature help is emitted as a side-effect of computing the expected type of a function argument. The reason is that both actions require enumerating the possible function signatures and running partial overload resolution, and doing this twice would be wasteful and complicated. Change #1: document this, it's subtle :-) However, sometimes we need to compute the expected type without having reached the code completion cursor yet - in particular to allow completion of designators. eb4ab33 did this but introduced a regression - it emits signature help in the wrong location as a side-effect. Change rust-lang#2: only emit signature help if the code completion cursor was reached. Currently there is PP.isCodeCompletionReached(), but we can't use it because it's set *after* running code completion. It'd be nice to set this implicitly when the completion token is lexed, but ConsumeCodeCompletionToken() makes this complicated. Change rust-lang#3: call cutOffParsing() *first* when seeing a completion token. After this, the fact that the Sema::Produce*SignatureHelp() functions are even more confusing, as they only sometimes do that. I don't want to rename them in this patch as it's another large mechanical change, but we should soon. Change rust-lang#4: prepare to rename ProduceSignatureHelp() to GuessArgumentType() etc. Differential Revision: https://reviews.llvm.org/D98488
1 parent 5ac3b37 commit 128ce70

File tree

15 files changed

+144
-84
lines changed

15 files changed

+144
-84
lines changed

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

+13
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,19 @@ TEST(SignatureHelpTest, Overloads) {
12531253
EXPECT_EQ(0, Results.activeParameter);
12541254
}
12551255

1256+
TEST(SignatureHelpTest, OverloadInitListRegression) {
1257+
auto Results = signatures(R"cpp(
1258+
struct A {int x;};
1259+
struct B {B(A);};
1260+
void f();
1261+
int main() {
1262+
B b({1});
1263+
f(^);
1264+
}
1265+
)cpp");
1266+
EXPECT_THAT(Results.signatures, UnorderedElementsAre(Sig("f() -> void")));
1267+
}
1268+
12561269
TEST(SignatureHelpTest, DefaultArgs) {
12571270
auto Results = signatures(R"cpp(
12581271
void bar(int x, int y = 0);

clang/include/clang/Sema/Sema.h

+17-2
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,9 @@ class PreferredTypeBuilder {
306306
/// Clients should be very careful when using this funciton, as it stores a
307307
/// function_ref, clients should make sure all calls to get() with the same
308308
/// location happen while function_ref is alive.
309+
///
310+
/// The callback should also emit signature help as a side-effect, but only
311+
/// if the completion point has been reached.
309312
void enterFunctionArgument(SourceLocation Tok,
310313
llvm::function_ref<QualType()> ComputeType);
311314

@@ -318,6 +321,12 @@ class PreferredTypeBuilder {
318321
/// Handles all type casts, including C-style cast, C++ casts, etc.
319322
void enterTypeCast(SourceLocation Tok, QualType CastType);
320323

324+
/// Get the expected type associated with this location, if any.
325+
///
326+
/// If the location is a function argument, determining the expected type
327+
/// involves considering all function overloads and the arguments so far.
328+
/// In this case, signature help for these function overloads will be reported
329+
/// as a side-effect (only if the completion point has been reached).
321330
QualType get(SourceLocation Tok) const {
322331
if (!Enabled || Tok != ExpectedLoc)
323332
return QualType();
@@ -12216,8 +12225,14 @@ class Sema final {
1221612225
const VirtSpecifiers *VS = nullptr);
1221712226
void CodeCompleteBracketDeclarator(Scope *S);
1221812227
void CodeCompleteCase(Scope *S);
12219-
/// Reports signatures for a call to CodeCompleteConsumer and returns the
12220-
/// preferred type for the current argument. Returned type can be null.
12228+
/// Determines the preferred type of the current function argument, by
12229+
/// examining the signatures of all possible overloads.
12230+
/// Returns null if unknown or ambiguous, or if code completion is off.
12231+
///
12232+
/// If the code completion point has been reached, also reports the function
12233+
/// signatures that were considered.
12234+
///
12235+
/// FIXME: rename to GuessCallArgumentType to reduce confusion.
1222112236
QualType ProduceCallSignatureHelp(Scope *S, Expr *Fn, ArrayRef<Expr *> Args,
1222212237
SourceLocation OpenParLoc);
1222312238
QualType ProduceConstructorSignatureHelp(Scope *S, QualType Type,

clang/lib/Lex/PPDirectives.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -441,9 +441,9 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
441441
CurLexer->Lex(Tok);
442442

443443
if (Tok.is(tok::code_completion)) {
444+
setCodeCompletionReached();
444445
if (CodeComplete)
445446
CodeComplete->CodeCompleteInConditionalExclusion();
446-
setCodeCompletionReached();
447447
continue;
448448
}
449449

@@ -966,10 +966,10 @@ void Preprocessor::HandleDirective(Token &Result) {
966966
case tok::eod:
967967
return; // null directive.
968968
case tok::code_completion:
969+
setCodeCompletionReached();
969970
if (CodeComplete)
970971
CodeComplete->CodeCompleteDirective(
971972
CurPPLexer->getConditionalStackDepth() > 0);
972-
setCodeCompletionReached();
973973
return;
974974
case tok::numeric_constant: // # 7 GNU line marker directive.
975975
if (getLangOpts().AsmPreprocessor)

clang/lib/Lex/Preprocessor.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -442,15 +442,15 @@ bool Preprocessor::SetCodeCompletionPoint(const FileEntry *File,
442442

443443
void Preprocessor::CodeCompleteIncludedFile(llvm::StringRef Dir,
444444
bool IsAngled) {
445+
setCodeCompletionReached();
445446
if (CodeComplete)
446447
CodeComplete->CodeCompleteIncludedFile(Dir, IsAngled);
447-
setCodeCompletionReached();
448448
}
449449

450450
void Preprocessor::CodeCompleteNaturalLanguage() {
451+
setCodeCompletionReached();
451452
if (CodeComplete)
452453
CodeComplete->CodeCompleteNaturalLanguage();
453-
setCodeCompletionReached();
454454
}
455455

456456
/// getSpelling - This method is used to get the spelling of a token into a

clang/lib/Parse/ParseDecl.cpp

+12-7
Original file line numberDiff line numberDiff line change
@@ -1970,8 +1970,8 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
19701970
// Check to see if we have a function *definition* which must have a body.
19711971
if (D.isFunctionDeclarator()) {
19721972
if (Tok.is(tok::equal) && NextToken().is(tok::code_completion)) {
1973-
Actions.CodeCompleteAfterFunctionEquals(D);
19741973
cutOffParsing();
1974+
Actions.CodeCompleteAfterFunctionEquals(D);
19751975
return nullptr;
19761976
}
19771977
// Look at the next token to make sure that this isn't a function
@@ -2310,9 +2310,9 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
23102310
InitializerScopeRAII InitScope(*this, D, ThisDecl);
23112311

23122312
if (Tok.is(tok::code_completion)) {
2313+
cutOffParsing();
23132314
Actions.CodeCompleteInitializer(getCurScope(), ThisDecl);
23142315
Actions.FinalizeDeclaration(ThisDecl);
2315-
cutOffParsing();
23162316
return nullptr;
23172317
}
23182318

@@ -3090,10 +3090,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
30903090
= DSContext == DeclSpecContext::DSC_top_level ||
30913091
(DSContext == DeclSpecContext::DSC_class && DS.isFriendSpecified());
30923092

3093+
cutOffParsing();
30933094
Actions.CodeCompleteDeclSpec(getCurScope(), DS,
30943095
AllowNonIdentifiers,
30953096
AllowNestedNameSpecifiers);
3096-
return cutOffParsing();
3097+
return;
30973098
}
30983099

30993100
if (getCurScope()->getFnParent() || getCurScope()->getBlockParent())
@@ -3106,8 +3107,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
31063107
else if (CurParsedObjCImpl)
31073108
CCC = Sema::PCC_ObjCImplementation;
31083109

3110+
cutOffParsing();
31093111
Actions.CodeCompleteOrdinaryName(getCurScope(), CCC);
3110-
return cutOffParsing();
3112+
return;
31113113
}
31123114

31133115
case tok::coloncolon: // ::foo::bar
@@ -4362,8 +4364,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
43624364
// Parse the tag portion of this.
43634365
if (Tok.is(tok::code_completion)) {
43644366
// Code completion for an enum name.
4367+
cutOffParsing();
43654368
Actions.CodeCompleteTag(getCurScope(), DeclSpec::TST_enum);
4366-
return cutOffParsing();
4369+
return;
43674370
}
43684371

43694372
// If attributes exist after tag, parse them.
@@ -5457,11 +5460,12 @@ void Parser::ParseTypeQualifierListOpt(
54575460

54585461
switch (Tok.getKind()) {
54595462
case tok::code_completion:
5463+
cutOffParsing();
54605464
if (CodeCompletionHandler)
54615465
(*CodeCompletionHandler)();
54625466
else
54635467
Actions.CodeCompleteTypeQualifiers(DS);
5464-
return cutOffParsing();
5468+
return;
54655469

54665470
case tok::kw_const:
54675471
isInvalid = DS.SetTypeQual(DeclSpec::TQ_const , Loc, PrevSpec, DiagID,
@@ -6998,8 +7002,9 @@ void Parser::ParseBracketDeclarator(Declarator &D) {
69987002
std::move(attrs), T.getCloseLocation());
69997003
return;
70007004
} else if (Tok.getKind() == tok::code_completion) {
7005+
cutOffParsing();
70017006
Actions.CodeCompleteBracketDeclarator(getCurScope());
7002-
return cutOffParsing();
7007+
return;
70037008
}
70047009

70057010
// If valid, this location is the position where we read the 'static' keyword.

clang/lib/Parse/ParseDeclCXX.cpp

+9-7
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
6363
ObjCDeclContextSwitch ObjCDC(*this);
6464

6565
if (Tok.is(tok::code_completion)) {
66-
Actions.CodeCompleteNamespaceDecl(getCurScope());
6766
cutOffParsing();
67+
Actions.CodeCompleteNamespaceDecl(getCurScope());
6868
return nullptr;
6969
}
7070

@@ -283,8 +283,8 @@ Decl *Parser::ParseNamespaceAlias(SourceLocation NamespaceLoc,
283283
ConsumeToken(); // eat the '='.
284284

285285
if (Tok.is(tok::code_completion)) {
286-
Actions.CodeCompleteNamespaceAliasDecl(getCurScope());
287286
cutOffParsing();
287+
Actions.CodeCompleteNamespaceAliasDecl(getCurScope());
288288
return nullptr;
289289
}
290290

@@ -471,8 +471,8 @@ Parser::ParseUsingDirectiveOrDeclaration(DeclaratorContext Context,
471471
SourceLocation UsingLoc = ConsumeToken();
472472

473473
if (Tok.is(tok::code_completion)) {
474-
Actions.CodeCompleteUsing(getCurScope());
475474
cutOffParsing();
475+
Actions.CodeCompleteUsing(getCurScope());
476476
return nullptr;
477477
}
478478

@@ -525,8 +525,8 @@ Decl *Parser::ParseUsingDirective(DeclaratorContext Context,
525525
SourceLocation NamespcLoc = ConsumeToken();
526526

527527
if (Tok.is(tok::code_completion)) {
528-
Actions.CodeCompleteUsingDirective(getCurScope());
529528
cutOffParsing();
529+
Actions.CodeCompleteUsingDirective(getCurScope());
530530
return nullptr;
531531
}
532532

@@ -1433,8 +1433,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
14331433

14341434
if (Tok.is(tok::code_completion)) {
14351435
// Code completion for a struct, class, or union name.
1436+
cutOffParsing();
14361437
Actions.CodeCompleteTag(getCurScope(), TagType);
1437-
return cutOffParsing();
1438+
return;
14381439
}
14391440

14401441
// C++03 [temp.explicit] 14.7.2/8:
@@ -2749,8 +2750,8 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
27492750
else if (KW.is(tok::kw_delete))
27502751
DefinitionKind = FunctionDefinitionKind::Deleted;
27512752
else if (KW.is(tok::code_completion)) {
2752-
Actions.CodeCompleteAfterFunctionEquals(DeclaratorInfo);
27532753
cutOffParsing();
2754+
Actions.CodeCompleteAfterFunctionEquals(DeclaratorInfo);
27542755
return nullptr;
27552756
}
27562757
}
@@ -3498,9 +3499,10 @@ void Parser::ParseConstructorInitializer(Decl *ConstructorDecl) {
34983499

34993500
do {
35003501
if (Tok.is(tok::code_completion)) {
3502+
cutOffParsing();
35013503
Actions.CodeCompleteConstructorInitializer(ConstructorDecl,
35023504
MemInitializers);
3503-
return cutOffParsing();
3505+
return;
35043506
}
35053507

35063508
MemInitResult MemInit = ParseMemInitializer(ConstructorDecl);

clang/lib/Parse/ParseExpr.cpp

+9-8
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,9 @@ Parser::ParseExpressionWithLeadingExtension(SourceLocation ExtLoc) {
159159
/// Parse an expr that doesn't include (top-level) commas.
160160
ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) {
161161
if (Tok.is(tok::code_completion)) {
162+
cutOffParsing();
162163
Actions.CodeCompleteExpression(getCurScope(),
163164
PreferredType.get(Tok.getLocation()));
164-
cutOffParsing();
165165
return ExprError();
166166
}
167167

@@ -1156,9 +1156,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
11561156
ConsumeToken();
11571157

11581158
if (Tok.is(tok::code_completion) && &II != Ident_super) {
1159+
cutOffParsing();
11591160
Actions.CodeCompleteObjCClassPropertyRefExpr(
11601161
getCurScope(), II, ILoc, ExprStatementTokLoc == ILoc);
1161-
cutOffParsing();
11621162
return ExprError();
11631163
}
11641164
// Allow either an identifier or the keyword 'class' (in C++).
@@ -1724,9 +1724,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
17241724
Res = ParseBlockLiteralExpression();
17251725
break;
17261726
case tok::code_completion: {
1727+
cutOffParsing();
17271728
Actions.CodeCompleteExpression(getCurScope(),
17281729
PreferredType.get(Tok.getLocation()));
1729-
cutOffParsing();
17301730
return ExprError();
17311731
}
17321732
case tok::l_square:
@@ -1856,9 +1856,9 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
18561856
if (InMessageExpression)
18571857
return LHS;
18581858

1859+
cutOffParsing();
18591860
Actions.CodeCompletePostfixExpression(
18601861
getCurScope(), LHS, PreferredType.get(Tok.getLocation()));
1861-
cutOffParsing();
18621862
return ExprError();
18631863

18641864
case tok::identifier:
@@ -2140,12 +2140,12 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
21402140
CorrectedBase = Base;
21412141

21422142
// Code completion for a member access expression.
2143+
cutOffParsing();
21432144
Actions.CodeCompleteMemberReferenceExpr(
21442145
getCurScope(), Base, CorrectedBase, OpLoc, OpKind == tok::arrow,
21452146
Base && ExprStatementTokLoc == Base->getBeginLoc(),
21462147
PreferredType.get(Tok.getLocation()));
21472148

2148-
cutOffParsing();
21492149
return ExprError();
21502150
}
21512151

@@ -2778,10 +2778,10 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
27782778
CastTy = nullptr;
27792779

27802780
if (Tok.is(tok::code_completion)) {
2781+
cutOffParsing();
27812782
Actions.CodeCompleteExpression(
27822783
getCurScope(), PreferredType.get(Tok.getLocation()),
27832784
/*IsParenthesized=*/ExprType >= CompoundLiteral);
2784-
cutOffParsing();
27852785
return ExprError();
27862786
}
27872787

@@ -3412,8 +3412,9 @@ Parser::ParseSimpleExpressionList(SmallVectorImpl<Expr*> &Exprs,
34123412
/// \endverbatim
34133413
void Parser::ParseBlockId(SourceLocation CaretLoc) {
34143414
if (Tok.is(tok::code_completion)) {
3415+
cutOffParsing();
34153416
Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Type);
3416-
return cutOffParsing();
3417+
return;
34173418
}
34183419

34193420
// Parse the specifier-qualifier-list piece.
@@ -3598,8 +3599,8 @@ Optional<AvailabilitySpec> Parser::ParseAvailabilitySpec() {
35983599
} else {
35993600
// Parse the platform name.
36003601
if (Tok.is(tok::code_completion)) {
3601-
Actions.CodeCompleteAvailabilityPlatformName();
36023602
cutOffParsing();
3603+
Actions.CodeCompleteAvailabilityPlatformName();
36033604
return None;
36043605
}
36053606
if (Tok.isNot(tok::identifier)) {

0 commit comments

Comments
 (0)