forked from OSchip/llvm-project
[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
This commit is contained in:
parent
fcb90cbd3b
commit
eb4ab3358c
|
@ -2018,8 +2018,11 @@ private:
|
|||
}
|
||||
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
|
||||
|
|
|
@ -298,6 +298,9 @@ public:
|
|||
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.
|
||||
///
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue