Skip to content

Commit

Permalink
[CodeComplete] Guess type for designated initializers
Browse files Browse the repository at this point in the history
This enables:
 - completion in { .x.^ }
 - completion in { .x = { .^ } }
 - type-based ranking of candidates for { .x = ^ }

Differential Revision: https://reviews.llvm.org/D96058
  • Loading branch information
sam-mccall committed Feb 4, 2021
1 parent fcb90cb commit eb4ab33
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 32 deletions.
7 changes: 5 additions & 2 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -2018,8 +2018,11 @@ class Parser : public CodeCompletionHandler {
}
bool MayBeDesignationStart();
ExprResult ParseBraceInitializer();
ExprResult ParseInitializerWithPotentialDesignator(
llvm::function_ref<void(const Designation &)> CodeCompleteCB);
struct DesignatorCompletionInfo {
SmallVectorImpl<Expr *> &InitExprs;
QualType PreferredBaseType;
};
ExprResult ParseInitializerWithPotentialDesignator(DesignatorCompletionInfo);

//===--------------------------------------------------------------------===//
// clang Expressions
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ class PreferredTypeBuilder {
void enterCondition(Sema &S, SourceLocation Tok);
void enterReturn(Sema &S, SourceLocation Tok);
void enterVariableInit(SourceLocation Tok, Decl *D);
/// Handles e.g. BaseType{ .D = Tok...
void enterDesignatedInitializer(SourceLocation Tok, QualType BaseType,
const Designation &D);
/// Computing a type for the function argument may require running
/// overloading, so we postpone its computation until it is actually needed.
///
Expand Down
29 changes: 19 additions & 10 deletions clang/lib/Parse/ParseInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,9 @@ static void CheckArrayDesignatorSyntax(Parser &P, SourceLocation Loc,
///
/// \p CodeCompleteCB is called with Designation parsed so far.
ExprResult Parser::ParseInitializerWithPotentialDesignator(
llvm::function_ref<void(const Designation &)> CodeCompleteCB) {
DesignatorCompletionInfo DesignatorCompletion) {
if (!getPreprocessor().isCodeCompletionEnabled())
DesignatorCompletion.PreferredBaseType = QualType(); // skip field lookup

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

Designation D;
D.AddDesignator(Designator::getField(FieldName, SourceLocation(), NameLoc));
PreferredType.enterDesignatedInitializer(
Tok.getLocation(), DesignatorCompletion.PreferredBaseType, D);
return Actions.ActOnDesignatedInitializer(D, ColonLoc, true,
ParseInitializer());
}
Expand All @@ -199,7 +203,8 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator(
SourceLocation DotLoc = ConsumeToken();

if (Tok.is(tok::code_completion)) {
CodeCompleteCB(Desig);
Actions.CodeCompleteDesignator(DesignatorCompletion.PreferredBaseType,
DesignatorCompletion.InitExprs, Desig);
cutOffParsing();
return ExprError();
}
Expand Down Expand Up @@ -388,6 +393,8 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator(
// Handle a normal designator sequence end, which is an equal.
if (Tok.is(tok::equal)) {
SourceLocation EqualLoc = ConsumeToken();
PreferredType.enterDesignatedInitializer(
Tok.getLocation(), DesignatorCompletion.PreferredBaseType, Desig);
return Actions.ActOnDesignatedInitializer(Desig, EqualLoc, false,
ParseInitializer());
}
Expand All @@ -396,6 +403,8 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator(
// direct-list-initialization of the aggregate element. We allow this as an
// extension from C++11 onwards (when direct-list-initialization was added).
if (Tok.is(tok::l_brace) && getLangOpts().CPlusPlus11) {
PreferredType.enterDesignatedInitializer(
Tok.getLocation(), DesignatorCompletion.PreferredBaseType, Desig);
return Actions.ActOnDesignatedInitializer(Desig, SourceLocation(), false,
ParseBraceInitializer());
}
Expand Down Expand Up @@ -453,9 +462,9 @@ ExprResult Parser::ParseBraceInitializer() {
Actions, EnterExpressionEvaluationContext::InitList);

bool InitExprsOk = true;
auto CodeCompleteDesignation = [&](const Designation &D) {
Actions.CodeCompleteDesignator(PreferredType.get(T.getOpenLocation()),
InitExprs, D);
DesignatorCompletionInfo DesignatorCompletion{
InitExprs,
PreferredType.get(T.getOpenLocation()),
};

while (1) {
Expand All @@ -476,7 +485,7 @@ ExprResult Parser::ParseBraceInitializer() {
// initializer directly.
ExprResult SubElt;
if (MayBeDesignationStart())
SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation);
SubElt = ParseInitializerWithPotentialDesignator(DesignatorCompletion);
else
SubElt = ParseInitializer();

Expand Down Expand Up @@ -556,17 +565,17 @@ bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs,
return false;
}

auto CodeCompleteDesignation = [&](const Designation &D) {
Actions.CodeCompleteDesignator(PreferredType.get(Braces.getOpenLocation()),
InitExprs, D);
DesignatorCompletionInfo DesignatorCompletion{
InitExprs,
PreferredType.get(Braces.getOpenLocation()),
};
while (!isEofOrEom()) {
trailingComma = false;
// If we know that this cannot be a designation, just parse the nested
// initializer directly.
ExprResult SubElt;
if (MayBeDesignationStart())
SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation);
SubElt = ParseInitializerWithPotentialDesignator(DesignatorCompletion);
else
SubElt = ParseInitializer();

Expand Down
62 changes: 47 additions & 15 deletions clang/lib/Sema/SemaCodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,16 @@ void PreferredTypeBuilder::enterVariableInit(SourceLocation Tok, Decl *D) {
ExpectedLoc = Tok;
}

static QualType getDesignatedType(QualType BaseType, const Designation &Desig);

void PreferredTypeBuilder::enterDesignatedInitializer(SourceLocation Tok,
QualType BaseType,
const Designation &D) {
ComputeType = nullptr;
Type = getDesignatedType(BaseType, D);
ExpectedLoc = Tok;
}

void PreferredTypeBuilder::enterFunctionArgument(
SourceLocation Tok, llvm::function_ref<QualType()> ComputeType) {
this->ComputeType = ComputeType;
Expand Down Expand Up @@ -4784,8 +4794,16 @@ static void AddRecordMembersCompletionResults(
// in case of specializations. Since we might not have a decl for the
// instantiation/specialization yet, e.g. dependent code.
static RecordDecl *getAsRecordDecl(const QualType BaseType) {
if (auto *RD = BaseType->getAsRecordDecl())
if (auto *RD = BaseType->getAsRecordDecl()) {
if (const auto *CTSD =
llvm::dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
// Template might not be instantiated yet, fall back to primary template
// in such cases.
if (CTSD->getTemplateSpecializationKind() == TSK_Undeclared)
RD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
}
return RD;
}

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

void Sema::CodeCompleteDesignator(const QualType BaseType,
static QualType getDesignatedType(QualType BaseType, const Designation &Desig) {
for (unsigned I = 0; I < Desig.getNumDesignators(); ++I) {
if (BaseType.isNull())
break;
QualType NextType;
const auto &D = Desig.getDesignator(I);
if (D.isArrayDesignator() || D.isArrayRangeDesignator()) {
if (BaseType->isArrayType())
NextType = BaseType->getAsArrayTypeUnsafe()->getElementType();
} else {
assert(D.isFieldDesignator());
auto *RD = getAsRecordDecl(BaseType);
if (RD && RD->isCompleteDefinition()) {
for (const auto &Member : RD->lookup(D.getField()))
if (const FieldDecl *FD = llvm::dyn_cast<FieldDecl>(Member)) {
NextType = FD->getType();
break;
}
}
}
BaseType = NextType;
}
return BaseType;
}

void Sema::CodeCompleteDesignator(QualType BaseType,
llvm::ArrayRef<Expr *> InitExprs,
const Designation &D) {
BaseType = getDesignatedType(BaseType, D);
if (BaseType.isNull())
return;
// FIXME: Handle nested designations, e.g. : .x.^
if (!D.empty())
return;

const auto *RD = getAsRecordDecl(BaseType);
if (!RD)
return;
if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
// Template might not be instantiated yet, fall back to primary template in
// such cases.
if (CTSD->getTemplateSpecializationKind() == TSK_Undeclared)
RD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
}
if (RD->fields().empty())
if (!RD || RD->fields().empty())
return;

CodeCompletionContext CCC(CodeCompletionContext::CCC_DotMemberAccess,
Expand Down
18 changes: 13 additions & 5 deletions clang/test/CodeCompletion/desig-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ void foo() {
// CHECK-CC1-NOT: foo
// CHECK-CC1-NOT: t

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

Base B = {.t = 2};
auto z = [](Base B) {};
Expand All @@ -29,6 +29,14 @@ void foo() {
// 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
// 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
// CHECK-CC2: COMPLETION: t : [#int#]t

Foo G1{.b = {.t = 0}};
Foo G2{.b{.t = 0}};
Foo G3{b: {.t = 0}};
// 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
// 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
// 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
// CHECK-NESTED-2: COMPLETION: t : [#int#]t
}

// Handle templates
Expand All @@ -41,16 +49,16 @@ struct Test<int> {
};
void bar() {
Test<char> T{.x = 2};
// 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
// 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
// CHECK-CC3: COMPLETION: x : [#T#]x
Test<int> X{.x = 2};
// 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
// 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
// CHECK-CC4: COMPLETION: x : [#int#]x
// CHECK-CC4-NEXT: COMPLETION: y : [#char#]y
}

template <typename T>
void aux() {
Test<T> X{.x = T(2)};
// 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
// 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
}

0 comments on commit eb4ab33

Please sign in to comment.