[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