[clang] eb4ab33 - [CodeComplete] Guess type for designated initializers
Sam McCall via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 4 13:17:49 PST 2021
Author: Sam McCall
Date: 2021-02-04T22:14:49+01:00
New Revision: eb4ab3358cd4dc834a761191b5531b38114f7b13
URL: https://github.com/llvm/llvm-project/commit/eb4ab3358cd4dc834a761191b5531b38114f7b13
DIFF: https://github.com/llvm/llvm-project/commit/eb4ab3358cd4dc834a761191b5531b38114f7b13.diff
LOG: [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
Added:
Modified:
clang/include/clang/Parse/Parser.h
clang/include/clang/Sema/Sema.h
clang/lib/Parse/ParseInit.cpp
clang/lib/Sema/SemaCodeComplete.cpp
clang/test/CodeCompletion/desig-init.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 61996f0d5699..f8b746446e7e 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -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
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2fca81d25345..ea20ada56abc 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -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.
///
diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp
index 9ac2b2e6f79b..50e1f1eaba4d 100644
--- a/clang/lib/Parse/ParseInit.cpp
+++ b/clang/lib/Parse/ParseInit.cpp
@@ -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 ':'
@@ -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());
}
@@ -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();
}
@@ -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());
}
@@ -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());
}
@@ -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) {
@@ -476,7 +485,7 @@ ExprResult Parser::ParseBraceInitializer() {
// initializer directly.
ExprResult SubElt;
if (MayBeDesignationStart())
- SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation);
+ SubElt = ParseInitializerWithPotentialDesignator(DesignatorCompletion);
else
SubElt = ParseInitializer();
@@ -556,9 +565,9 @@ 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;
@@ -566,7 +575,7 @@ bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs,
// initializer directly.
ExprResult SubElt;
if (MayBeDesignationStart())
- SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation);
+ SubElt = ParseInitializerWithPotentialDesignator(DesignatorCompletion);
else
SubElt = ParseInitializer();
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index c2785fd60fc2..ece204c001e7 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -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;
@@ -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>(
@@ -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,
diff --git a/clang/test/CodeCompletion/desig-init.cpp b/clang/test/CodeCompletion/desig-init.cpp
index fbcaeb303e50..8a66f4554217 100644
--- a/clang/test/CodeCompletion/desig-init.cpp
+++ b/clang/test/CodeCompletion/desig-init.cpp
@@ -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) {};
@@ -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
@@ -41,10 +49,10 @@ 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
}
@@ -52,5 +60,5 @@ void bar() {
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
}
More information about the cfe-commits
mailing list