Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4568,6 +4568,12 @@ // of a ComparisonCategoryType enumerator. llvm::SmallBitVector FullyCheckedComparisonCategories; + /// Tries to get decleration for a member field. + ValueDecl *tryLookupCtorInitMemberDecl(CXXRecordDecl *ClassDecl, + CXXScopeSpec &SS, + ParsedType TemplateTypeTy, + IdentifierInfo *MemberOrBase); + public: /// Lookup the specified comparison category types in the standard /// library, an check the VarDecls possibly returned by the operator<=> @@ -10254,6 +10260,12 @@ SourceLocation Loc, ArrayRef Args, SourceLocation OpenParLoc); + QualType ProduceCtorInitMemberSignatureHelp(Scope *S, Decl *ConstructorDecl, + CXXScopeSpec SS, + ParsedType TemplateTypeTy, + ArrayRef ArgExprs, + IdentifierInfo *II, + SourceLocation OpenParLoc); void CodeCompleteInitializer(Scope *S, Decl *D); void CodeCompleteReturn(Scope *S); void CodeCompleteAfterIf(Scope *S); @@ -10794,7 +10806,6 @@ /// The template function declaration to be late parsed. Decl *D; }; - } // end namespace clang namespace llvm { Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -3449,6 +3449,7 @@ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); + // FIXME: Add support for signature help inside initializer lists. ExprResult InitList = ParseBraceInitializer(); if (InitList.isInvalid()) return true; @@ -3466,7 +3467,22 @@ // Parse the optional expression-list. ExprVector ArgExprs; CommaLocsTy CommaLocs; - if (Tok.isNot(tok::r_paren) && ParseExpressionList(ArgExprs, CommaLocs)) { + if (Tok.isNot(tok::r_paren) && + ParseExpressionList(ArgExprs, CommaLocs, [&] { + if (CalledSignatureHelp) + return; + QualType PreferredType = Actions.ProduceCtorInitMemberSignatureHelp( + getCurScope(), ConstructorDecl, SS, TemplateTypeTy, ArgExprs, II, + T.getOpenLocation()); + CalledSignatureHelp = true; + Actions.CodeCompleteExpression(getCurScope(), PreferredType); + })) { + if (PP.isCodeCompletionReached() && !CalledSignatureHelp) { + Actions.ProduceCtorInitMemberSignatureHelp( + getCurScope(), ConstructorDecl, SS, TemplateTypeTy, ArgExprs, II, + T.getOpenLocation()); + CalledSignatureHelp = true; + } SkipUntil(tok::r_paren, StopAtSemi); return true; } Index: lib/Sema/SemaCodeComplete.cpp =================================================================== --- lib/Sema/SemaCodeComplete.cpp +++ lib/Sema/SemaCodeComplete.cpp @@ -4586,6 +4586,25 @@ return ProduceSignatureHelp(*this, S, Results, Args.size(), OpenParLoc); } +QualType Sema::ProduceCtorInitMemberSignatureHelp( + Scope *S, Decl *ConstructorDecl, CXXScopeSpec SS, ParsedType TemplateTypeTy, + ArrayRef ArgExprs, IdentifierInfo *II, SourceLocation OpenParLoc) { + if (!CodeCompleter) + return QualType(); + + CXXConstructorDecl *Constructor = + dyn_cast(ConstructorDecl); + if (!Constructor) + return QualType(); + // FIXME: Add support for Base class constructors as well. + if (ValueDecl *MemberDecl = tryLookupCtorInitMemberDecl( + Constructor->getParent(), SS, TemplateTypeTy, II)) + return ProduceConstructorSignatureHelp(getCurScope(), MemberDecl->getType(), + MemberDecl->getLocation(), ArgExprs, + OpenParLoc); + return QualType(); +} + void Sema::CodeCompleteInitializer(Scope *S, Decl *D) { ValueDecl *VD = dyn_cast_or_null(D); if (!VD) { Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -3777,6 +3777,24 @@ } +ValueDecl *Sema::tryLookupCtorInitMemberDecl(CXXRecordDecl *ClassDecl, + CXXScopeSpec &SS, + ParsedType TemplateTypeTy, + IdentifierInfo *MemberOrBase) { + if (!SS.getScopeRep() && !TemplateTypeTy) { + // Look for a member, first. + DeclContext::lookup_result Result = ClassDecl->lookup(MemberOrBase); + if (!Result.empty()) { + ValueDecl *Member; + if ((Member = dyn_cast(Result.front())) || + (Member = dyn_cast(Result.front()))) { + return Member; + } + } + } + return nullptr; +} + /// Handle a C++ member initializer. MemInitResult Sema::BuildMemInitializer(Decl *ConstructorD, @@ -3820,21 +3838,14 @@ // of a single identifier refers to the class member. A // mem-initializer-id for the hidden base class may be specified // using a qualified name. ] - if (!SS.getScopeRep() && !TemplateTypeTy) { - // Look for a member, first. - DeclContext::lookup_result Result = ClassDecl->lookup(MemberOrBase); - if (!Result.empty()) { - ValueDecl *Member; - if ((Member = dyn_cast(Result.front())) || - (Member = dyn_cast(Result.front()))) { - if (EllipsisLoc.isValid()) - Diag(EllipsisLoc, diag::err_pack_expansion_member_init) - << MemberOrBase - << SourceRange(IdLoc, Init->getSourceRange().getEnd()); - - return BuildMemberInitializer(Member, Init, IdLoc); - } - } + if (ValueDecl *Member = tryLookupCtorInitMemberDecl( + ClassDecl, SS, TemplateTypeTy, MemberOrBase)) { + if (EllipsisLoc.isValid()) + Diag(EllipsisLoc, diag::err_pack_expansion_member_init) + << MemberOrBase + << SourceRange(IdLoc, Init->getSourceRange().getEnd()); + + return BuildMemberInitializer(Member, Init, IdLoc); } // It didn't name a member, so see if it names a class. QualType BaseType; Index: test/CodeCompletion/ctor-initializer.cpp =================================================================== --- test/CodeCompletion/ctor-initializer.cpp +++ test/CodeCompletion/ctor-initializer.cpp @@ -64,3 +64,26 @@ // CHECK-CC8: COMPLETION: Pattern : member2(<#args#> int member1, member2; }; + +struct Base2 { + Base2(int); +}; + +struct Composition1 { + Composition1() : b2_elem() {} + // RUN: %clang_cc1 -fsyntax-only -std=c++98 -code-completion-at=%s:73:28 %s -o - | FileCheck -check-prefix=CHECK-CC9 %s + // RUN: %clang_cc1 -fsyntax-only -std=c++14 -code-completion-at=%s:73:28 %s -o - | FileCheck -check-prefix=CHECK-CC9 %s + // CHECK-CC9: OVERLOAD: Base2(<#int#>) + // CHECK-CC9: OVERLOAD: Base2(<#const Base2 &#>) + Composition1(Base2); + Base2 b2_elem; +}; + +struct Composition2 { + Composition2() : c1_elem(Base2(1)) {} + // RUN: %clang_cc1 -fsyntax-only -std=c++98 -code-completion-at=%s:83:34 %s -o - | FileCheck -check-prefix=CHECK-CC9 %s + // RUN: %clang_cc1 -fsyntax-only -std=c++14 -code-completion-at=%s:83:34 %s -o - | FileCheck -check-prefix=CHECK-CC9 %s + // RUN: %clang_cc1 -fsyntax-only -std=c++98 -code-completion-at=%s:83:35 %s -o - | FileCheck -check-prefix=CHECK-CC9 %s + // RUN: %clang_cc1 -fsyntax-only -std=c++14 -code-completion-at=%s:83:35 %s -o - | FileCheck -check-prefix=CHECK-CC9 %s + Composition1 c1_elem; +};