diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2018,8 +2018,11 @@ } bool MayBeDesignationStart(); ExprResult ParseBraceInitializer(); - ExprResult ParseInitializerWithPotentialDesignator( - llvm::function_ref CodeCompleteCB); + struct DesignatorCompletionInfo { + SmallVectorImpl &InitExprs; + QualType PreferredBaseType; + }; + ExprResult ParseInitializerWithPotentialDesignator(DesignatorCompletionInfo); //===--------------------------------------------------------------------===// // clang Expressions diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -298,6 +298,9 @@ 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 --- a/clang/lib/Parse/ParseInit.cpp +++ b/clang/lib/Parse/ParseInit.cpp @@ -159,7 +159,9 @@ /// /// \p CodeCompleteCB is called with Designation parsed so far. ExprResult Parser::ParseInitializerWithPotentialDesignator( - llvm::function_ref 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 @@ 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 @@ 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 @@ // 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 @@ // 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 @@ 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 @@ // initializer directly. ExprResult SubElt; if (MayBeDesignationStart()) - SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation); + SubElt = ParseInitializerWithPotentialDesignator(DesignatorCompletion); else SubElt = ParseInitializer(); @@ -556,9 +565,9 @@ 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 @@ // 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 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -405,6 +405,16 @@ 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 ComputeType) { this->ComputeType = ComputeType; @@ -4784,8 +4794,16 @@ // 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(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()) { if (const auto *TD = dyn_cast_or_null( @@ -5754,25 +5772,39 @@ 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(Member)) { + NextType = FD->getType(); + break; + } + } + } + BaseType = NextType; + } + return BaseType; +} + +void Sema::CodeCompleteDesignator(QualType BaseType, llvm::ArrayRef 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(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 --- a/clang/test/CodeCompletion/desig-init.cpp +++ b/clang/test/CodeCompletion/desig-init.cpp @@ -16,8 +16,8 @@ // 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 @@ // 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 @@ }; void bar() { Test 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 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 @@ template void aux() { Test 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 }