[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