Skip to content

Commit 42e9478

Browse files
committed
[clang][CodeComplete] Support for designated initializers
Reviewers: sammccall Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D73271
1 parent 993e3c9 commit 42e9478

File tree

7 files changed

+150
-21
lines changed

7 files changed

+150
-21
lines changed

clang/include/clang/Parse/Parser.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1952,7 +1952,8 @@ class Parser : public CodeCompletionHandler {
19521952
}
19531953
bool MayBeDesignationStart();
19541954
ExprResult ParseBraceInitializer();
1955-
ExprResult ParseInitializerWithPotentialDesignator();
1955+
ExprResult ParseInitializerWithPotentialDesignator(
1956+
llvm::function_ref<void(const Designation &)> CodeCompleteCB);
19561957

19571958
//===--------------------------------------------------------------------===//
19581959
// clang Expressions

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11555,6 +11555,12 @@ class Sema final {
1155511555
IdentifierInfo *II,
1155611556
SourceLocation OpenParLoc);
1155711557
void CodeCompleteInitializer(Scope *S, Decl *D);
11558+
/// Trigger code completion for a record of \p BaseType. \p InitExprs are
11559+
/// expressions in the initializer list seen so far and \p D is the current
11560+
/// Designation being parsed.
11561+
void CodeCompleteDesignator(const QualType BaseType,
11562+
llvm::ArrayRef<Expr *> InitExprs,
11563+
const Designation &D);
1155811564
void CodeCompleteAfterIf(Scope *S);
1155911565

1156011566
void CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS, bool EnteringContext,

clang/lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,6 +2460,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
24602460

24612461
InitializerScopeRAII InitScope(*this, D, ThisDecl);
24622462

2463+
PreferredType.enterVariableInit(Tok.getLocation(), ThisDecl);
24632464
ExprResult Init(ParseBraceInitializer());
24642465

24652466
InitScope.pop();

clang/lib/Parse/ParseExprCXX.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,6 +1866,7 @@ Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) {
18661866
&& "Expected '(' or '{'!");
18671867

18681868
if (Tok.is(tok::l_brace)) {
1869+
PreferredType.enterTypeCast(Tok.getLocation(), TypeRep.get());
18691870
ExprResult Init = ParseBraceInitializer();
18701871
if (Init.isInvalid())
18711872
return Init;

clang/lib/Parse/ParseInit.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "clang/Basic/TokenKinds.h"
1314
#include "clang/Parse/ParseDiagnostic.h"
1415
#include "clang/Parse/Parser.h"
1516
#include "clang/Parse/RAIIObjectsForParser.h"
1617
#include "clang/Sema/Designator.h"
18+
#include "clang/Sema/Ownership.h"
1719
#include "clang/Sema/Scope.h"
20+
#include "llvm/ADT/STLExtras.h"
1821
#include "llvm/ADT/SmallString.h"
1922
using namespace clang;
2023

@@ -154,7 +157,9 @@ static void CheckArrayDesignatorSyntax(Parser &P, SourceLocation Loc,
154157
/// initializer (because it is an expression). We need to consider this case
155158
/// when parsing array designators.
156159
///
157-
ExprResult Parser::ParseInitializerWithPotentialDesignator() {
160+
/// \p CodeCompleteCB is called with Designation parsed so far.
161+
ExprResult Parser::ParseInitializerWithPotentialDesignator(
162+
llvm::function_ref<void(const Designation &)> CodeCompleteCB) {
158163

159164
// If this is the old-style GNU extension:
160165
// designation ::= identifier ':'
@@ -193,6 +198,11 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator() {
193198
// designator: '.' identifier
194199
SourceLocation DotLoc = ConsumeToken();
195200

201+
if (Tok.is(tok::code_completion)) {
202+
CodeCompleteCB(Desig);
203+
cutOffParsing();
204+
return ExprError();
205+
}
196206
if (Tok.isNot(tok::identifier)) {
197207
Diag(Tok.getLocation(), diag::err_expected_field_designator);
198208
return ExprError();
@@ -407,7 +417,6 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator() {
407417
return ExprError();
408418
}
409419

410-
411420
/// ParseBraceInitializer - Called when parsing an initializer that has a
412421
/// leading open brace.
413422
///
@@ -444,6 +453,10 @@ ExprResult Parser::ParseBraceInitializer() {
444453
Actions, EnterExpressionEvaluationContext::InitList);
445454

446455
bool InitExprsOk = true;
456+
auto CodeCompleteDesignation = [&](const Designation &D) {
457+
Actions.CodeCompleteDesignator(PreferredType.get(T.getOpenLocation()),
458+
InitExprs, D);
459+
};
447460

448461
while (1) {
449462
// Handle Microsoft __if_exists/if_not_exists if necessary.
@@ -463,7 +476,7 @@ ExprResult Parser::ParseBraceInitializer() {
463476
// initializer directly.
464477
ExprResult SubElt;
465478
if (MayBeDesignationStart())
466-
SubElt = ParseInitializerWithPotentialDesignator();
479+
SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation);
467480
else
468481
SubElt = ParseInitializer();
469482

@@ -543,13 +556,17 @@ bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs,
543556
return false;
544557
}
545558

559+
auto CodeCompleteDesignation = [&](const Designation &D) {
560+
Actions.CodeCompleteDesignator(PreferredType.get(Braces.getOpenLocation()),
561+
InitExprs, D);
562+
};
546563
while (!isEofOrEom()) {
547564
trailingComma = false;
548565
// If we know that this cannot be a designation, just parse the nested
549566
// initializer directly.
550567
ExprResult SubElt;
551568
if (MayBeDesignationStart())
552-
SubElt = ParseInitializerWithPotentialDesignator();
569+
SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation);
553570
else
554571
SubElt = ParseInitializer();
555572

clang/lib/Sema/SemaCodeComplete.cpp

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "clang/AST/DeclBase.h"
1414
#include "clang/AST/DeclCXX.h"
1515
#include "clang/AST/DeclObjC.h"
16+
#include "clang/AST/DeclTemplate.h"
17+
#include "clang/AST/Expr.h"
1618
#include "clang/AST/ExprCXX.h"
1719
#include "clang/AST/ExprObjC.h"
1820
#include "clang/AST/QualTypeNames.h"
@@ -23,11 +25,14 @@
2325
#include "clang/Lex/MacroInfo.h"
2426
#include "clang/Lex/Preprocessor.h"
2527
#include "clang/Sema/CodeCompleteConsumer.h"
28+
#include "clang/Sema/Designator.h"
2629
#include "clang/Sema/Lookup.h"
2730
#include "clang/Sema/Overload.h"
2831
#include "clang/Sema/Scope.h"
2932
#include "clang/Sema/ScopeInfo.h"
33+
#include "clang/Sema/Sema.h"
3034
#include "clang/Sema/SemaInternal.h"
35+
#include "llvm/ADT/ArrayRef.h"
3136
#include "llvm/ADT/DenseSet.h"
3237
#include "llvm/ADT/SmallBitVector.h"
3338
#include "llvm/ADT/SmallPtrSet.h"
@@ -36,6 +41,7 @@
3641
#include "llvm/ADT/StringSwitch.h"
3742
#include "llvm/ADT/Twine.h"
3843
#include "llvm/ADT/iterator_range.h"
44+
#include "llvm/Support/Casting.h"
3945
#include "llvm/Support/Path.h"
4046
#include "llvm/Support/raw_ostream.h"
4147
#include <list>
@@ -4723,6 +4729,23 @@ static void AddRecordMembersCompletionResults(
47234729
}
47244730
}
47254731

4732+
// Returns the RecordDecl inside the BaseType, falling back to primary template
4733+
// in case of specializations. Since we might not have a decl for the
4734+
// instantiation/specialization yet, e.g. dependent code.
4735+
static RecordDecl *getAsRecordDecl(const QualType BaseType) {
4736+
if (auto *RD = BaseType->getAsRecordDecl())
4737+
return RD;
4738+
4739+
if (const auto *TST = BaseType->getAs<TemplateSpecializationType>()) {
4740+
if (const auto *TD = dyn_cast_or_null<ClassTemplateDecl>(
4741+
TST->getTemplateName().getAsTemplateDecl())) {
4742+
return TD->getTemplatedDecl();
4743+
}
4744+
}
4745+
4746+
return nullptr;
4747+
}
4748+
47264749
void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
47274750
Expr *OtherOpBase,
47284751
SourceLocation OpLoc, bool IsArrow,
@@ -4771,6 +4794,8 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
47714794
Base = ConvertedBase.get();
47724795

47734796
QualType BaseType = Base->getType();
4797+
if (BaseType.isNull())
4798+
return false;
47744799
ExprValueKind BaseKind = Base->getValueKind();
47754800

47764801
if (IsArrow) {
@@ -4783,23 +4808,9 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
47834808
return false;
47844809
}
47854810

4786-
if (const RecordType *Record = BaseType->getAs<RecordType>()) {
4811+
if (RecordDecl *RD = getAsRecordDecl(BaseType)) {
47874812
AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
4788-
Record->getDecl(),
4789-
std::move(AccessOpFixIt));
4790-
} else if (const auto *TST =
4791-
BaseType->getAs<TemplateSpecializationType>()) {
4792-
TemplateName TN = TST->getTemplateName();
4793-
if (const auto *TD =
4794-
dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl())) {
4795-
CXXRecordDecl *RD = TD->getTemplatedDecl();
4796-
AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
4797-
RD, std::move(AccessOpFixIt));
4798-
}
4799-
} else if (const auto *ICNT = BaseType->getAs<InjectedClassNameType>()) {
4800-
if (auto *RD = ICNT->getDecl())
4801-
AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
4802-
RD, std::move(AccessOpFixIt));
4813+
RD, std::move(AccessOpFixIt));
48034814
} else if (!IsArrow && BaseType->isObjCObjectPointerType()) {
48044815
// Objective-C property reference.
48054816
AddedPropertiesSet AddedProperties;
@@ -5286,6 +5297,44 @@ QualType Sema::ProduceCtorInitMemberSignatureHelp(
52865297
return QualType();
52875298
}
52885299

5300+
void Sema::CodeCompleteDesignator(const QualType BaseType,
5301+
llvm::ArrayRef<Expr *> InitExprs,
5302+
const Designation &D) {
5303+
if (BaseType.isNull())
5304+
return;
5305+
// FIXME: Handle nested designations, e.g. : .x.^
5306+
if (!D.empty())
5307+
return;
5308+
5309+
const auto *RD = getAsRecordDecl(BaseType);
5310+
if (!RD)
5311+
return;
5312+
if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
5313+
// Template might not be instantiated yet, fall back to primary template in
5314+
// such cases.
5315+
if (CTSD->getTemplateSpecializationKind() == TSK_Undeclared)
5316+
RD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
5317+
}
5318+
if (RD->fields().empty())
5319+
return;
5320+
5321+
CodeCompletionContext CCC(CodeCompletionContext::CCC_DotMemberAccess,
5322+
BaseType);
5323+
ResultBuilder Results(*this, CodeCompleter->getAllocator(),
5324+
CodeCompleter->getCodeCompletionTUInfo(), CCC);
5325+
5326+
Results.EnterNewScope();
5327+
for (const auto *FD : RD->fields()) {
5328+
// FIXME: Make use of previous designators to mark any fields before those
5329+
// inaccessible, and also compute the next initializer priority.
5330+
ResultBuilder::Result Result(FD, Results.getBasePriority(FD));
5331+
Results.AddResult(Result, CurContext, /*Hiding=*/nullptr);
5332+
}
5333+
Results.ExitScope();
5334+
HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
5335+
Results.data(), Results.size());
5336+
}
5337+
52895338
void Sema::CodeCompleteInitializer(Scope *S, Decl *D) {
52905339
ValueDecl *VD = dyn_cast_or_null<ValueDecl>(D);
52915340
if (!VD) {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
struct Base {
2+
int t;
3+
};
4+
struct Foo : public Base {
5+
int x;
6+
Base b;
7+
void foo();
8+
};
9+
10+
void foo() {
11+
Foo F{.x = 2, .b.t = 0};
12+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:11:10 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC1 %s
13+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:11:18 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC1 %s
14+
// CHECK-CC1: COMPLETION: b : [#Base#]b
15+
// CHECK-CC1-NEXT: COMPLETION: x : [#int#]x
16+
// CHECK-CC1-NOT: foo
17+
// CHECK-CC1-NOT: t
18+
19+
// FIXME: Handle nested designators
20+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:11:20 %s -o - | count 0
21+
22+
Base B = {.t = 2};
23+
auto z = [](Base B) {};
24+
z({.t = 1});
25+
z(Base{.t = 2});
26+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:22:14 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC2 %s
27+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:24:7 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC2 %s
28+
// 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
29+
// CHECK-CC2: COMPLETION: t : [#int#]t
30+
}
31+
32+
// Handle templates
33+
template <typename T>
34+
struct Test { T x; };
35+
template <>
36+
struct Test<int> {
37+
int x;
38+
char y;
39+
};
40+
void bar() {
41+
Test<char> T{.x = 2};
42+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:41:17 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC3 %s
43+
// CHECK-CC3: COMPLETION: x : [#T#]x
44+
Test<int> X{.x = 2};
45+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:44:16 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC4 %s
46+
// CHECK-CC4: COMPLETION: x : [#int#]x
47+
// CHECK-CC4-NEXT: COMPLETION: y : [#char#]y
48+
}
49+
50+
template <typename T>
51+
void aux() {
52+
Test<T> X{.x = T(2)};
53+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:52:14 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC3 %s
54+
}

0 commit comments

Comments
 (0)