From: Douglas Gregor Date: Wed, 15 Feb 2012 15:34:24 +0000 (+0000) Subject: Implement code completion support for lambda capture lists. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=81f3bff7c202d688c9298bc049fdb5b6f77057b1;p=clang Implement code completion support for lambda capture lists. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150583 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 1363121341..5ac2cd560b 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -6304,6 +6304,8 @@ public: void CodeCompleteConstructorInitializer(Decl *Constructor, CXXCtorInitializer** Initializers, unsigned NumInitializers); + void CodeCompleteLambdaIntroducer(Scope *S, LambdaIntroducer &Intro, + bool AfterAmpersand); void CodeCompleteObjCAtDirective(Scope *S); void CodeCompleteObjCAtVisibility(Scope *S); diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 380237082b..5996e2a264 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -615,7 +615,7 @@ ExprResult Parser::TryParseLambdaExpression() { /// ParseLambdaExpression - Parse a lambda introducer. /// /// Returns a DiagnosticID if it hit something unexpected. -llvm::Optional Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro) { +llvm::Optional Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro){ typedef llvm::Optional DiagResult; assert(Tok.is(tok::l_square) && "Lambda expressions begin with '['."); @@ -640,13 +640,33 @@ llvm::Optional Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro) while (Tok.isNot(tok::r_square)) { if (!first) { - if (Tok.isNot(tok::comma)) + if (Tok.isNot(tok::comma)) { + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteLambdaIntroducer(getCurScope(), Intro, + /*AfterAmpersand=*/false); + ConsumeCodeCompletionToken(); + break; + } + return DiagResult(diag::err_expected_comma_or_rsquare); + } ConsumeToken(); } - first = false; + if (Tok.is(tok::code_completion)) { + // If we're in Objective-C++ and we have a bare '[', then this is more + // likely to be a message receiver. + if (getLang().ObjC1 && first) + Actions.CodeCompleteObjCMessageReceiver(getCurScope()); + else + Actions.CodeCompleteLambdaIntroducer(getCurScope(), Intro, + /*AfterAmpersand=*/false); + ConsumeCodeCompletionToken(); + break; + } + first = false; + // Parse capture. LambdaCaptureKind Kind = LCK_ByCopy; SourceLocation Loc; @@ -660,6 +680,13 @@ llvm::Optional Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro) if (Tok.is(tok::amp)) { Kind = LCK_ByRef; ConsumeToken(); + + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteLambdaIntroducer(getCurScope(), Intro, + /*AfterAmpersand=*/true); + ConsumeCodeCompletionToken(); + break; + } } if (Tok.is(tok::identifier)) { @@ -687,7 +714,7 @@ llvm::Optional Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro) return DiagResult(); } -/// TryParseLambdaExpression - Tentatively parse a lambda introducer. +/// TryParseLambdaIntroducer - Tentatively parse a lambda introducer. /// /// Returns true if it hit something unexpected. bool Parser::TryParseLambdaIntroducer(LambdaIntroducer &Intro) { diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 7d384486cb..e74e70c725 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -274,9 +274,9 @@ namespace { /// of the shadow maps), or replace an existing result (for, e.g., a /// redeclaration). /// - /// \param CurContext the result to add (if it is unique). + /// \param R the result to add (if it is unique). /// - /// \param R the context in which this result will be named. + /// \param CurContext the context in which this result will be named. void MaybeAddResult(Result R, DeclContext *CurContext = 0); /// \brief Add a new result to this result set, where we already know @@ -325,6 +325,7 @@ namespace { bool IsMember(NamedDecl *ND) const; bool IsObjCIvar(NamedDecl *ND) const; bool IsObjCMessageReceiver(NamedDecl *ND) const; + bool IsObjCMessageReceiverOrLambdaCapture(NamedDecl *ND) const; bool IsObjCCollection(NamedDecl *ND) const; bool IsImpossibleToSatisfy(NamedDecl *ND) const; //@} @@ -1152,6 +1153,17 @@ bool ResultBuilder::IsObjCMessageReceiver(NamedDecl *ND) const { return isObjCReceiverType(SemaRef.Context, T); } +bool ResultBuilder::IsObjCMessageReceiverOrLambdaCapture(NamedDecl *ND) const { + if (IsObjCMessageReceiver(ND)) + return true; + + VarDecl *Var = dyn_cast(ND); + if (!Var) + return false; + + return Var->hasLocalStorage() && !Var->hasAttr(); +} + bool ResultBuilder::IsObjCCollection(NamedDecl *ND) const { if ((SemaRef.getLangOptions().CPlusPlus && !IsOrdinaryName(ND)) || (!SemaRef.getLangOptions().CPlusPlus && !IsOrdinaryNonTypeName(ND))) @@ -1423,6 +1435,23 @@ static const char *GetCompletionTypeString(QualType T, return Allocator.CopyString(Result); } +/// \brief Add a completion for "this", if we're in a member function. +static void addThisCompletion(Sema &S, ResultBuilder &Results) { + QualType ThisTy = S.getCurrentThisType(); + if (ThisTy.isNull()) + return; + + CodeCompletionAllocator &Allocator = Results.getAllocator(); + CodeCompletionBuilder Builder(Allocator); + PrintingPolicy Policy = getCompletionPrintingPolicy(S); + Builder.AddResultTypeChunk(GetCompletionTypeString(ThisTy, + S.Context, + Policy, + Allocator)); + Builder.AddTypedTextChunk("this"); + Results.AddResult(CodeCompletionResult(Builder.TakeString())); +} + /// \brief Add language constructs that show up for "ordinary" names. static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, Scope *S, @@ -1758,15 +1787,7 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, case Sema::PCC_Expression: { if (SemaRef.getLangOptions().CPlusPlus) { // 'this', if we're in a non-static member function. - QualType ThisTy = SemaRef.getCurrentThisType(); - if (!ThisTy.isNull()) { - Builder.AddResultTypeChunk(GetCompletionTypeString(ThisTy, - SemaRef.Context, - Policy, - Allocator)); - Builder.AddTypedTextChunk("this"); - Results.AddResult(Result(Builder.TakeString())); - } + addThisCompletion(SemaRef, Results); // true Builder.AddResultTypeChunk("bool"); @@ -3665,7 +3686,6 @@ void Sema::CodeCompleteCase(Scope *S) { kind = CodeCompletionContext::CCC_OtherWithMacros; } - HandleCodeCompleteResults(this, CodeCompleter, kind, Results.data(),Results.size()); @@ -4181,6 +4201,60 @@ void Sema::CodeCompleteConstructorInitializer(Decl *ConstructorD, Results.data(), Results.size()); } +/// \brief Determine whether this scope denotes a namespace. +static bool isNamespaceScope(Scope *S) { + DeclContext *DC = static_cast(S->getEntity()); + if (!DC) + return false; + + return DC->isFileContext(); +} + +void Sema::CodeCompleteLambdaIntroducer(Scope *S, LambdaIntroducer &Intro, + bool AfterAmpersand) { + ResultBuilder Results(*this, CodeCompleter->getAllocator(), + CodeCompletionContext::CCC_Other); + Results.EnterNewScope(); + + // Note what has already been captured. + llvm::SmallPtrSet Known; + bool IncludedThis = false; + for (SmallVectorImpl::iterator C = Intro.Captures.begin(), + CEnd = Intro.Captures.end(); + C != CEnd; ++C) { + if (C->Kind == LCK_This) { + IncludedThis = true; + continue; + } + + Known.insert(C->Id); + } + + // Look for other capturable variables. + for (; S && !isNamespaceScope(S); S = S->getParent()) { + for (Scope::decl_iterator D = S->decl_begin(), DEnd = S->decl_end(); + D != DEnd; ++D) { + VarDecl *Var = dyn_cast(*D); + if (!Var || + !Var->hasLocalStorage() || + Var->hasAttr()) + continue; + + if (Known.insert(Var->getIdentifier())) + Results.AddResult(CodeCompletionResult(Var), CurContext, 0, false); + } + } + + // Add 'this', if it would be valid. + if (!IncludedThis && !AfterAmpersand && Intro.Default != LCD_ByCopy) + addThisCompletion(*this, Results); + + Results.ExitScope(); + + HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(), + Results.data(), Results.size()); +} + // Macro that expands to @Keyword or Keyword, depending on whether NeedAt is // true or false. #define OBJC_AT_KEYWORD_NAME(NeedAt,Keyword) NeedAt? "@" #Keyword : #Keyword @@ -4980,7 +5054,9 @@ void Sema::CodeCompleteObjCMessageReceiver(Scope *S) { typedef CodeCompletionResult Result; ResultBuilder Results(*this, CodeCompleter->getAllocator(), CodeCompletionContext::CCC_ObjCMessageReceiver, - &ResultBuilder::IsObjCMessageReceiver); + getLangOptions().CPlusPlus0x + ? &ResultBuilder::IsObjCMessageReceiverOrLambdaCapture + : &ResultBuilder::IsObjCMessageReceiver); CodeCompletionDeclConsumer Consumer(Results, CurContext); Results.EnterNewScope(); @@ -4997,6 +5073,9 @@ void Sema::CodeCompleteObjCMessageReceiver(Scope *S) { AddSuperSendCompletion(*this, /*NeedSuperKeyword=*/true, 0, 0, Results); } + if (getLangOptions().CPlusPlus0x) + addThisCompletion(*this, Results); + Results.ExitScope(); if (CodeCompleter->includeMacros()) diff --git a/test/Index/complete-lambdas.cpp b/test/Index/complete-lambdas.cpp new file mode 100644 index 0000000000..ba337e45d3 --- /dev/null +++ b/test/Index/complete-lambdas.cpp @@ -0,0 +1,43 @@ +// This test is line- and column-sensitive. See below for run lines. + +int global; + +struct X { + static int member; + void f(int zed) { + int local; + static int local_static; + [=] { + int inner_local; + [local, this, inner_local] { + } + }(); + } +}; + + +// RUN: c-index-test -code-completion-at=%s:12:8 -std=c++11 %s | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: VarDecl:{ResultType int}{TypedText inner_local} (34) +// CHECK-CC1-NEXT: VarDecl:{ResultType int}{TypedText local} (34) +// CHECK-CC1-NEXT: NotImplemented:{ResultType X *}{TypedText this} (40) +// CHECK-CC1-NEXT: ParmDecl:{ResultType int}{TypedText zed} (34) + +// RUN: c-index-test -code-completion-at=%s:12:15 -std=c++11 %s | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: VarDecl:{ResultType int}{TypedText inner_local} (34) +// CHECK-CC2-NEXT: NotImplemented:{ResultType X *}{TypedText this} (40) +// CHECK-CC2-NEXT: ParmDecl:{ResultType int}{TypedText zed} (34) + +// RUN: c-index-test -code-completion-at=%s:12:21 -std=c++11 %s | FileCheck -check-prefix=CHECK-CC3 %s +// CHECK-CC3: VarDecl:{ResultType int}{TypedText inner_local} (34) +// CHECK-CC3-NEXT: ParmDecl:{ResultType int}{TypedText zed} (34) + +// RUN: c-index-test -code-completion-at=%s:12:8 -x objective-c++ -std=c++11 %s | FileCheck -check-prefix=CHECK-CC4 %s +// CHECK-CC4: TypedefDecl:{TypedText Class} (50) +// CHECK-CC4: TypedefDecl:{TypedText id} (50) +// CHECK-CC4: VarDecl:{ResultType int}{TypedText inner_local} (34) +// CHECK-CC4: VarDecl:{ResultType int}{TypedText local} (34) +// CHECK-CC4: NotImplemented:{ResultType X *}{TypedText this} (40) +// CHECK-CC4: ParmDecl:{ResultType int}{TypedText zed} (34) + +// RUN: c-index-test -code-completion-at=%s:12:15 -x objective-c++ -std=c++11 %s | FileCheck -check-prefix=CHECK-CC2 %s +// RUN: c-index-test -code-completion-at=%s:12:21 -x objective-c++ -std=c++11 %s | FileCheck -check-prefix=CHECK-CC3 %s