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 +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits