[clang] 42e9478 - [clang][CodeComplete] Support for designated initializers

Kadir Cetinkaya via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 28 07:44:10 PST 2020


Author: Kadir Cetinkaya
Date: 2020-01-28T16:34:15+01:00
New Revision: 42e9478e0bbbe3468a74d9d07275a61558b220c4

URL: https://github.com/llvm/llvm-project/commit/42e9478e0bbbe3468a74d9d07275a61558b220c4
DIFF: https://github.com/llvm/llvm-project/commit/42e9478e0bbbe3468a74d9d07275a61558b220c4.diff

LOG: [clang][CodeComplete] Support for designated initializers

Reviewers: sammccall

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D73271

Added: 
    clang/test/CodeCompletion/desig-init.cpp

Modified: 
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/Sema.h
    clang/lib/Parse/ParseDecl.cpp
    clang/lib/Parse/ParseExprCXX.cpp
    clang/lib/Parse/ParseInit.cpp
    clang/lib/Sema/SemaCodeComplete.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 187982387487..d1e97843f599 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1952,7 +1952,8 @@ class Parser : public CodeCompletionHandler {
   }
   bool MayBeDesignationStart();
   ExprResult ParseBraceInitializer();
-  ExprResult ParseInitializerWithPotentialDesignator();
+  ExprResult ParseInitializerWithPotentialDesignator(
+      llvm::function_ref<void(const Designation &)> CodeCompleteCB);
 
   //===--------------------------------------------------------------------===//
   // clang Expressions

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5a33e4aa6ffc..bc77989ad35d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11555,6 +11555,12 @@ class Sema final {
                                               IdentifierInfo *II,
                                               SourceLocation OpenParLoc);
   void CodeCompleteInitializer(Scope *S, Decl *D);
+  /// Trigger code completion for a record of \p BaseType. \p InitExprs are
+  /// expressions in the initializer list seen so far and \p D is the current
+  /// Designation being parsed.
+  void CodeCompleteDesignator(const QualType BaseType,
+                              llvm::ArrayRef<Expr *> InitExprs,
+                              const Designation &D);
   void CodeCompleteAfterIf(Scope *S);
 
   void CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS, bool EnteringContext,

diff  --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 4af993c4527f..871ca2512598 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2460,6 +2460,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
 
     InitializerScopeRAII InitScope(*this, D, ThisDecl);
 
+    PreferredType.enterVariableInit(Tok.getLocation(), ThisDecl);
     ExprResult Init(ParseBraceInitializer());
 
     InitScope.pop();

diff  --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 5b604f940ab8..1a8e0342cb08 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1866,6 +1866,7 @@ Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) {
          && "Expected '(' or '{'!");
 
   if (Tok.is(tok::l_brace)) {
+    PreferredType.enterTypeCast(Tok.getLocation(), TypeRep.get());
     ExprResult Init = ParseBraceInitializer();
     if (Init.isInvalid())
       return Init;

diff  --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp
index 5ab055130dc2..9ac2b2e6f79b 100644
--- a/clang/lib/Parse/ParseInit.cpp
+++ b/clang/lib/Parse/ParseInit.cpp
@@ -10,11 +10,14 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/Basic/TokenKinds.h"
 #include "clang/Parse/ParseDiagnostic.h"
 #include "clang/Parse/Parser.h"
 #include "clang/Parse/RAIIObjectsForParser.h"
 #include "clang/Sema/Designator.h"
+#include "clang/Sema/Ownership.h"
 #include "clang/Sema/Scope.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 using namespace clang;
 
@@ -154,7 +157,9 @@ static void CheckArrayDesignatorSyntax(Parser &P, SourceLocation Loc,
 /// initializer (because it is an expression).  We need to consider this case
 /// when parsing array designators.
 ///
-ExprResult Parser::ParseInitializerWithPotentialDesignator() {
+/// \p CodeCompleteCB is called with Designation parsed so far.
+ExprResult Parser::ParseInitializerWithPotentialDesignator(
+    llvm::function_ref<void(const Designation &)> CodeCompleteCB) {
 
   // If this is the old-style GNU extension:
   //   designation ::= identifier ':'
@@ -193,6 +198,11 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator() {
       // designator: '.' identifier
       SourceLocation DotLoc = ConsumeToken();
 
+      if (Tok.is(tok::code_completion)) {
+        CodeCompleteCB(Desig);
+        cutOffParsing();
+        return ExprError();
+      }
       if (Tok.isNot(tok::identifier)) {
         Diag(Tok.getLocation(), diag::err_expected_field_designator);
         return ExprError();
@@ -407,7 +417,6 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator() {
   return ExprError();
 }
 
-
 /// ParseBraceInitializer - Called when parsing an initializer that has a
 /// leading open brace.
 ///
@@ -444,6 +453,10 @@ ExprResult Parser::ParseBraceInitializer() {
       Actions, EnterExpressionEvaluationContext::InitList);
 
   bool InitExprsOk = true;
+  auto CodeCompleteDesignation = [&](const Designation &D) {
+    Actions.CodeCompleteDesignator(PreferredType.get(T.getOpenLocation()),
+                                   InitExprs, D);
+  };
 
   while (1) {
     // Handle Microsoft __if_exists/if_not_exists if necessary.
@@ -463,7 +476,7 @@ ExprResult Parser::ParseBraceInitializer() {
     // initializer directly.
     ExprResult SubElt;
     if (MayBeDesignationStart())
-      SubElt = ParseInitializerWithPotentialDesignator();
+      SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation);
     else
       SubElt = ParseInitializer();
 
@@ -543,13 +556,17 @@ bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs,
     return false;
   }
 
+  auto CodeCompleteDesignation = [&](const Designation &D) {
+    Actions.CodeCompleteDesignator(PreferredType.get(Braces.getOpenLocation()),
+                                   InitExprs, D);
+  };
   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();
+      SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation);
     else
       SubElt = ParseInitializer();
 

diff  --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 3348067a7669..18b1172ac1b9 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -13,6 +13,8 @@
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/QualTypeNames.h"
@@ -23,11 +25,14 @@
 #include "clang/Lex/MacroInfo.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/Sema/Designator.h"
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Overload.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaInternal.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallPtrSet.h"
@@ -36,6 +41,7 @@
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
 #include <list>
@@ -4723,6 +4729,23 @@ static void AddRecordMembersCompletionResults(
   }
 }
 
+// Returns the RecordDecl inside the BaseType, falling back to primary template
+// 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())
+    return RD;
+
+  if (const auto *TST = BaseType->getAs<TemplateSpecializationType>()) {
+    if (const auto *TD = dyn_cast_or_null<ClassTemplateDecl>(
+            TST->getTemplateName().getAsTemplateDecl())) {
+      return TD->getTemplatedDecl();
+    }
+  }
+
+  return nullptr;
+}
+
 void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
                                            Expr *OtherOpBase,
                                            SourceLocation OpLoc, bool IsArrow,
@@ -4771,6 +4794,8 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
     Base = ConvertedBase.get();
 
     QualType BaseType = Base->getType();
+    if (BaseType.isNull())
+      return false;
     ExprValueKind BaseKind = Base->getValueKind();
 
     if (IsArrow) {
@@ -4783,23 +4808,9 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
         return false;
     }
 
-    if (const RecordType *Record = BaseType->getAs<RecordType>()) {
+    if (RecordDecl *RD = getAsRecordDecl(BaseType)) {
       AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
-                                        Record->getDecl(),
-                                        std::move(AccessOpFixIt));
-    } else if (const auto *TST =
-                   BaseType->getAs<TemplateSpecializationType>()) {
-      TemplateName TN = TST->getTemplateName();
-      if (const auto *TD =
-              dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl())) {
-        CXXRecordDecl *RD = TD->getTemplatedDecl();
-        AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
-                                          RD, std::move(AccessOpFixIt));
-      }
-    } else if (const auto *ICNT = BaseType->getAs<InjectedClassNameType>()) {
-      if (auto *RD = ICNT->getDecl())
-        AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
-                                          RD, std::move(AccessOpFixIt));
+                                        RD, std::move(AccessOpFixIt));
     } else if (!IsArrow && BaseType->isObjCObjectPointerType()) {
       // Objective-C property reference.
       AddedPropertiesSet AddedProperties;
@@ -5286,6 +5297,44 @@ QualType Sema::ProduceCtorInitMemberSignatureHelp(
   return QualType();
 }
 
+void Sema::CodeCompleteDesignator(const QualType BaseType,
+                                  llvm::ArrayRef<Expr *> InitExprs,
+                                  const Designation &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())
+    return;
+
+  CodeCompletionContext CCC(CodeCompletionContext::CCC_DotMemberAccess,
+                            BaseType);
+  ResultBuilder Results(*this, CodeCompleter->getAllocator(),
+                        CodeCompleter->getCodeCompletionTUInfo(), CCC);
+
+  Results.EnterNewScope();
+  for (const auto *FD : RD->fields()) {
+    // FIXME: Make use of previous designators to mark any fields before those
+    // inaccessible, and also compute the next initializer priority.
+    ResultBuilder::Result Result(FD, Results.getBasePriority(FD));
+    Results.AddResult(Result, CurContext, /*Hiding=*/nullptr);
+  }
+  Results.ExitScope();
+  HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
+                            Results.data(), Results.size());
+}
+
 void Sema::CodeCompleteInitializer(Scope *S, Decl *D) {
   ValueDecl *VD = dyn_cast_or_null<ValueDecl>(D);
   if (!VD) {

diff  --git a/clang/test/CodeCompletion/desig-init.cpp b/clang/test/CodeCompletion/desig-init.cpp
new file mode 100644
index 000000000000..ebfd63266397
--- /dev/null
+++ b/clang/test/CodeCompletion/desig-init.cpp
@@ -0,0 +1,54 @@
+struct Base {
+  int t;
+};
+struct Foo : public Base {
+  int x;
+  Base b;
+  void foo();
+};
+
+void foo() {
+  Foo F{.x = 2, .b.t = 0};
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:11:10 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC1 %s
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:11:18 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC1 %s
+  // CHECK-CC1: COMPLETION: b : [#Base#]b
+  // CHECK-CC1-NEXT: COMPLETION: x : [#int#]x
+  // 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
+
+  Base B = {.t = 2};
+  auto z = [](Base B) {};
+  z({.t = 1});
+  z(Base{.t = 2});
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:22:14 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC2 %s
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:24:7 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC2 %s
+  // 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
+  // CHECK-CC2: COMPLETION: t : [#int#]t
+}
+
+// Handle templates
+template <typename T>
+struct Test { T x; };
+template <>
+struct Test<int> {
+  int x;
+  char y;
+};
+void bar() {
+  Test<char> T{.x = 2};
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:41: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:44: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:52:14 %s -o - -std=c++2a | FileCheck -check-prefix=CHECK-CC3 %s
+}


        


More information about the cfe-commits mailing list