Skip to content

Commit eb4ab33

Browse files
committed
[CodeComplete] Guess type for designated initializers
This enables: - completion in { .x.^ } - completion in { .x = { .^ } } - type-based ranking of candidates for { .x = ^ } Differential Revision: https://reviews.llvm.org/D96058
1 parent fcb90cb commit eb4ab33

File tree

5 files changed

+87
-32
lines changed

5 files changed

+87
-32
lines changed

clang/include/clang/Parse/Parser.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -2018,8 +2018,11 @@ class Parser : public CodeCompletionHandler {
20182018
}
20192019
bool MayBeDesignationStart();
20202020
ExprResult ParseBraceInitializer();
2021-
ExprResult ParseInitializerWithPotentialDesignator(
2022-
llvm::function_ref<void(const Designation &)> CodeCompleteCB);
2021+
struct DesignatorCompletionInfo {
2022+
SmallVectorImpl<Expr *> &InitExprs;
2023+
QualType PreferredBaseType;
2024+
};
2025+
ExprResult ParseInitializerWithPotentialDesignator(DesignatorCompletionInfo);
20232026

20242027
//===--------------------------------------------------------------------===//
20252028
// clang Expressions

clang/include/clang/Sema/Sema.h

+3
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ class PreferredTypeBuilder {
298298
void enterCondition(Sema &S, SourceLocation Tok);
299299
void enterReturn(Sema &S, SourceLocation Tok);
300300
void enterVariableInit(SourceLocation Tok, Decl *D);
301+
/// Handles e.g. BaseType{ .D = Tok...
302+
void enterDesignatedInitializer(SourceLocation Tok, QualType BaseType,
303+
const Designation &D);
301304
/// Computing a type for the function argument may require running
302305
/// overloading, so we postpone its computation until it is actually needed.
303306
///

clang/lib/Parse/ParseInit.cpp

+19-10
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ static void CheckArrayDesignatorSyntax(Parser &P, SourceLocation Loc,
159159
///
160160
/// \p CodeCompleteCB is called with Designation parsed so far.
161161
ExprResult Parser::ParseInitializerWithPotentialDesignator(
162-
llvm::function_ref<void(const Designation &)> CodeCompleteCB) {
162+
DesignatorCompletionInfo DesignatorCompletion) {
163+
if (!getPreprocessor().isCodeCompletionEnabled())
164+
DesignatorCompletion.PreferredBaseType = QualType(); // skip field lookup
163165

164166
// If this is the old-style GNU extension:
165167
// designation ::= identifier ':'
@@ -183,6 +185,8 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator(
183185

184186
Designation D;
185187
D.AddDesignator(Designator::getField(FieldName, SourceLocation(), NameLoc));
188+
PreferredType.enterDesignatedInitializer(
189+
Tok.getLocation(), DesignatorCompletion.PreferredBaseType, D);
186190
return Actions.ActOnDesignatedInitializer(D, ColonLoc, true,
187191
ParseInitializer());
188192
}
@@ -199,7 +203,8 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator(
199203
SourceLocation DotLoc = ConsumeToken();
200204

201205
if (Tok.is(tok::code_completion)) {
202-
CodeCompleteCB(Desig);
206+
Actions.CodeCompleteDesignator(DesignatorCompletion.PreferredBaseType,
207+
DesignatorCompletion.InitExprs, Desig);
203208
cutOffParsing();
204209
return ExprError();
205210
}
@@ -388,6 +393,8 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator(
388393
// Handle a normal designator sequence end, which is an equal.
389394
if (Tok.is(tok::equal)) {
390395
SourceLocation EqualLoc = ConsumeToken();
396+
PreferredType.enterDesignatedInitializer(
397+
Tok.getLocation(), DesignatorCompletion.PreferredBaseType, Desig);
391398
return Actions.ActOnDesignatedInitializer(Desig, EqualLoc, false,
392399
ParseInitializer());
393400
}
@@ -396,6 +403,8 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator(
396403
// direct-list-initialization of the aggregate element. We allow this as an
397404
// extension from C++11 onwards (when direct-list-initialization was added).
398405
if (Tok.is(tok::l_brace) && getLangOpts().CPlusPlus11) {
406+
PreferredType.enterDesignatedInitializer(
407+
Tok.getLocation(), DesignatorCompletion.PreferredBaseType, Desig);
399408
return Actions.ActOnDesignatedInitializer(Desig, SourceLocation(), false,
400409
ParseBraceInitializer());
401410
}
@@ -453,9 +462,9 @@ ExprResult Parser::ParseBraceInitializer() {
453462
Actions, EnterExpressionEvaluationContext::InitList);
454463

455464
bool InitExprsOk = true;
456-
auto CodeCompleteDesignation = [&](const Designation &D) {
457-
Actions.CodeCompleteDesignator(PreferredType.get(T.getOpenLocation()),
458-
InitExprs, D);
465+
DesignatorCompletionInfo DesignatorCompletion{
466+
InitExprs,
467+
PreferredType.get(T.getOpenLocation()),
459468
};
460469

461470
while (1) {
@@ -476,7 +485,7 @@ ExprResult Parser::ParseBraceInitializer() {
476485
// initializer directly.
477486
ExprResult SubElt;
478487
if (MayBeDesignationStart())
479-
SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation);
488+
SubElt = ParseInitializerWithPotentialDesignator(DesignatorCompletion);
480489
else
481490
SubElt = ParseInitializer();
482491

@@ -556,17 +565,17 @@ bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs,
556565
return false;
557566
}
558567

559-
auto CodeCompleteDesignation = [&](const Designation &D) {
560-
Actions.CodeCompleteDesignator(PreferredType.get(Braces.getOpenLocation()),
561-
InitExprs, D);
568+
DesignatorCompletionInfo DesignatorCompletion{
569+
InitExprs,
570+
PreferredType.get(Braces.getOpenLocation()),
562571
};
563572
while (!isEofOrEom()) {
564573
trailingComma = false;
565574
// If we know that this cannot be a designation, just parse the nested
566575
// initializer directly.
567576
ExprResult SubElt;
568577
if (MayBeDesignationStart())
569-
SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation);
578+
SubElt = ParseInitializerWithPotentialDesignator(DesignatorCompletion);
570579
else
571580
SubElt = ParseInitializer();
572581

clang/lib/Sema/SemaCodeComplete.cpp

+47-15
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,16 @@ void PreferredTypeBuilder::enterVariableInit(SourceLocation Tok, Decl *D) {
405405
ExpectedLoc = Tok;
406406
}
407407

408+
static QualType getDesignatedType(QualType BaseType, const Designation &Desig);
409+
410+
void PreferredTypeBuilder::enterDesignatedInitializer(SourceLocation Tok,
411+
QualType BaseType,
412+
const Designation &D) {
413+
ComputeType = nullptr;
414+
Type = getDesignatedType(BaseType, D);
415+
ExpectedLoc = Tok;
416+
}
417+
408418
void PreferredTypeBuilder::enterFunctionArgument(
409419
SourceLocation Tok, llvm::function_ref<QualType()> ComputeType) {
410420
this->ComputeType = ComputeType;
@@ -4784,8 +4794,16 @@ static void AddRecordMembersCompletionResults(
47844794
// in case of specializations. Since we might not have a decl for the
47854795
// instantiation/specialization yet, e.g. dependent code.
47864796
static RecordDecl *getAsRecordDecl(const QualType BaseType) {
4787-
if (auto *RD = BaseType->getAsRecordDecl())
4797+
if (auto *RD = BaseType->getAsRecordDecl()) {
4798+
if (const auto *CTSD =
4799+
llvm::dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
4800+
// Template might not be instantiated yet, fall back to primary template
4801+
// in such cases.
4802+
if (CTSD->getTemplateSpecializationKind() == TSK_Undeclared)
4803+
RD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
4804+
}
47884805
return RD;
4806+
}
47894807

47904808
if (const auto *TST = BaseType->getAs<TemplateSpecializationType>()) {
47914809
if (const auto *TD = dyn_cast_or_null<ClassTemplateDecl>(
@@ -5754,25 +5772,39 @@ QualType Sema::ProduceCtorInitMemberSignatureHelp(
57545772
return QualType();
57555773
}
57565774

5757-
void Sema::CodeCompleteDesignator(const QualType BaseType,
5775+
static QualType getDesignatedType(QualType BaseType, const Designation &Desig) {
5776+
for (unsigned I = 0; I < Desig.getNumDesignators(); ++I) {
5777+
if (BaseType.isNull())
5778+
break;
5779+
QualType NextType;
5780+
const auto &D = Desig.getDesignator(I);
5781+
if (D.isArrayDesignator() || D.isArrayRangeDesignator()) {
5782+
if (BaseType->isArrayType())
5783+
NextType = BaseType->getAsArrayTypeUnsafe()->getElementType();
5784+
} else {
5785+
assert(D.isFieldDesignator());
5786+
auto *RD = getAsRecordDecl(BaseType);
5787+
if (RD && RD->isCompleteDefinition()) {
5788+
for (const auto &Member : RD->lookup(D.getField()))
5789+
if (const FieldDecl *FD = llvm::dyn_cast<FieldDecl>(Member)) {
5790+
NextType = FD->getType();
5791+
break;
5792+
}
5793+
}
5794+
}
5795+
BaseType = NextType;
5796+
}
5797+
return BaseType;
5798+
}
5799+
5800+
void Sema::CodeCompleteDesignator(QualType BaseType,
57585801
llvm::ArrayRef<Expr *> InitExprs,
57595802
const Designation &D) {
5803+
BaseType = getDesignatedType(BaseType, D);
57605804
if (BaseType.isNull())
57615805
return;
5762-
// FIXME: Handle nested designations, e.g. : .x.^
5763-
if (!D.empty())
5764-
return;
5765-
57665806
const auto *RD = getAsRecordDecl(BaseType);
5767-
if (!RD)
5768-
return;
5769-
if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
5770-
// Template might not be instantiated yet, fall back to primary template in
5771-
// such cases.
5772-
if (CTSD->getTemplateSpecializationKind() == TSK_Undeclared)
5773-
RD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
5774-
}
5775-
if (RD->fields().empty())
5807+
if (!RD || RD->fields().empty())
57765808
return;
57775809

57785810
CodeCompletionContext CCC(CodeCompletionContext::CCC_DotMemberAccess,

clang/test/CodeCompletion/desig-init.cpp

+13-5
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ void foo() {
1616
// CHECK-CC1-NOT: foo
1717
// CHECK-CC1-NOT: t
1818

19-
// FIXME: Handle nested designators
20-
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:11:20 %s -o - | count 0
19+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:11:20 %s -o - | FileCheck -check-prefix=CHECK-NESTED %s
20+
// CHECK-NESTED: COMPLETION: t : [#int#]t
2121

2222
Base B = {.t = 2};
2323
auto z = [](Base B) {};
@@ -29,6 +29,14 @@ void foo() {
2929
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:25:11 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC2 %s
3030
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:26:13 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC2 %s
3131
// CHECK-CC2: COMPLETION: t : [#int#]t
32+
33+
Foo G1{.b = {.t = 0}};
34+
Foo G2{.b{.t = 0}};
35+
Foo G3{b: {.t = 0}};
36+
// RUN: %clang_cc1 -code-completion-at=%s:33:17 -fsyntax-only -code-completion-patterns %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-NESTED-2 %s
37+
// RUN: %clang_cc1 -code-completion-at=%s:34:14 -fsyntax-only -code-completion-patterns %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-NESTED-2 %s
38+
// RUN: %clang_cc1 -code-completion-at=%s:35:15 -fsyntax-only -code-completion-patterns %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-NESTED-2 %s
39+
// CHECK-NESTED-2: COMPLETION: t : [#int#]t
3240
}
3341

3442
// Handle templates
@@ -41,16 +49,16 @@ struct Test<int> {
4149
};
4250
void bar() {
4351
Test<char> T{.x = 2};
44-
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:43:17 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC3 %s
52+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:51:17 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC3 %s
4553
// CHECK-CC3: COMPLETION: x : [#T#]x
4654
Test<int> X{.x = 2};
47-
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:46:16 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC4 %s
55+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:54:16 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC4 %s
4856
// CHECK-CC4: COMPLETION: x : [#int#]x
4957
// CHECK-CC4-NEXT: COMPLETION: y : [#char#]y
5058
}
5159

5260
template <typename T>
5361
void aux() {
5462
Test<T> X{.x = T(2)};
55-
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:54:14 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC3 %s
63+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:62:14 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC3 %s
5664
}

0 commit comments

Comments
 (0)