From: Douglas Gregor Date: Tue, 21 Sep 2010 16:06:22 +0000 (+0000) Subject: Add code completion for C++ constructors wherever we see the class (or X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6f942b2cabf32b96f9901b889d8e44a34e0e7c62;p=clang Add code completion for C++ constructors wherever we see the class (or class template) and are in a context where we can have a value. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@114441 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index d9014f13ea..85f3c91649 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -256,6 +256,10 @@ public: /// \brief Retrieve the type of the base object in a member-access /// expression. QualType getBaseType() const { return BaseType; } + + /// \brief Determines whether we want C++ constructors as results within this + /// context. + bool wantConstructorResults() const; }; diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp index 3322782d87..e298d23aa0 100644 --- a/lib/Sema/CodeCompleteConsumer.cpp +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -27,6 +27,46 @@ using namespace clang; using llvm::StringRef; +//===----------------------------------------------------------------------===// +// Code completion context implementation +//===----------------------------------------------------------------------===// + +bool CodeCompletionContext::wantConstructorResults() const { + switch (Kind) { + case CCC_Other: + case CCC_Statement: + case CCC_Expression: + case CCC_ObjCMessageReceiver: + case CCC_ParenthesizedExpression: + return true; + + case CCC_TopLevel: + case CCC_ObjCInterface: + case CCC_ObjCImplementation: + case CCC_ObjCIvarList: + case CCC_ClassStructUnion: + case CCC_MemberAccess: + case CCC_EnumTag: + case CCC_UnionTag: + case CCC_ClassOrStructTag: + case CCC_ObjCProtocolName: + case CCC_Namespace: + case CCC_Type: + case CCC_Name: + case CCC_PotentiallyQualifiedName: + case CCC_MacroName: + case CCC_MacroNameUse: + case CCC_PreprocessorExpression: + case CCC_PreprocessorDirective: + case CCC_NaturalLanguage: + case CCC_SelectorName: + case CCC_TypeQualifiers: + return false; + } + + return false; +} + //===----------------------------------------------------------------------===// // Code completion string implementation //===----------------------------------------------------------------------===// diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index ab37b6ccbc..87474952d5 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -152,6 +152,8 @@ namespace { void AdjustResultPriorityForDecl(Result &R); + void MaybeAddConstructorResults(Result R); + public: explicit ResultBuilder(Sema &SemaRef, LookupFilter Filter = 0) : SemaRef(SemaRef), Filter(Filter), AllowNestedNameSpecifiers(false), @@ -480,11 +482,7 @@ bool ResultBuilder::isInterestingDecl(NamedDecl *ND, return false; } } - - // C++ constructors are never found by name lookup. - if (isa(ND)) - return false; - + if (Filter == &ResultBuilder::IsNestedNameSpecifier || ((isa(ND) || isa(ND)) && Filter != &ResultBuilder::IsNamespace && @@ -663,6 +661,42 @@ void ResultBuilder::AdjustResultPriorityForDecl(Result &R) { } } +void ResultBuilder::MaybeAddConstructorResults(Result R) { + if (!SemaRef.getLangOptions().CPlusPlus || !R.Declaration || + !CompletionContext.wantConstructorResults()) + return; + + ASTContext &Context = SemaRef.Context; + NamedDecl *D = R.Declaration; + CXXRecordDecl *Record = 0; + if (ClassTemplateDecl *ClassTemplate = dyn_cast(D)) + Record = ClassTemplate->getTemplatedDecl(); + else if ((Record = dyn_cast(D))) { + // Skip specializations and partial specializations. + if (isa(Record)) + return; + } else { + // There are no constructors here. + return; + } + + Record = Record->getDefinition(); + if (!Record) + return; + + + QualType RecordTy = Context.getTypeDeclType(Record); + DeclarationName ConstructorName + = Context.DeclarationNames.getCXXConstructorName( + Context.getCanonicalType(RecordTy)); + for (DeclContext::lookup_result Ctors = Record->lookup(ConstructorName); + Ctors.first != Ctors.second; ++Ctors.first) { + R.Declaration = *Ctors.first; + R.CursorKind = getCursorKindForDecl(R.Declaration); + Results.push_back(R); + } +} + void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) { assert(!ShadowMaps.empty() && "Must enter into a results scope"); @@ -685,6 +719,10 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) { if (!isInterestingDecl(R.Declaration, AsNestedNameSpecifier)) return; + // C++ constructors are never found by name lookup. + if (isa(R.Declaration)) + return; + ShadowMap &SMap = ShadowMaps.back(); ShadowMapEntry::iterator I, IEnd; ShadowMap::iterator NamePos = SMap.find(R.Declaration->getDeclName()); @@ -767,6 +805,9 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) { // map. SMap[R.Declaration->getDeclName()].Add(R.Declaration, Results.size()); Results.push_back(R); + + if (!AsNestedNameSpecifier) + MaybeAddConstructorResults(R); } void ResultBuilder::AddResult(Result R, DeclContext *CurContext, @@ -787,6 +828,10 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext, if (!isInterestingDecl(R.Declaration, AsNestedNameSpecifier)) return; + // C++ constructors are never found by name lookup. + if (isa(R.Declaration)) + return; + if (Hiding && CheckHiddenResult(R, CurContext, Hiding)) return; @@ -840,6 +885,9 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext, // Insert this result into the set of results. Results.push_back(R); + + if (!AsNestedNameSpecifier) + MaybeAddConstructorResults(R); } void ResultBuilder::AddResult(Result R) { @@ -1725,9 +1773,14 @@ static void AddResultTypeChunk(ASTContext &Context, CodeCompletionString *Result) { if (!ND) return; - + + // Skip constructors and conversion functions, which have their return types + // built into their names. + if (isa(ND) || isa(ND)) + return; + // Determine the type of the declaration (if it has a type). - QualType T; + QualType T; if (FunctionDecl *Function = dyn_cast(ND)) T = Function->getResultType(); else if (ObjCMethodDecl *Method = dyn_cast(ND)) @@ -2019,6 +2072,54 @@ static void AddFunctionTypeQualsToCompletionString(CodeCompletionString *Result, Result->AddInformativeChunk(QualsStr); } +/// \brief Add the name of the given declaration +static void AddTypedNameChunk(ASTContext &Context, NamedDecl *ND, + CodeCompletionString *Result) { + typedef CodeCompletionString::Chunk Chunk; + + DeclarationName Name = ND->getDeclName(); + if (!Name) + return; + + switch (Name.getNameKind()) { + case DeclarationName::Identifier: + case DeclarationName::CXXConversionFunctionName: + case DeclarationName::CXXOperatorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXLiteralOperatorName: + Result->AddTypedTextChunk(ND->getNameAsString()); + break; + + case DeclarationName::CXXUsingDirective: + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + break; + + case DeclarationName::CXXConstructorName: { + CXXRecordDecl *Record = 0; + QualType Ty = Name.getCXXNameType(); + if (const RecordType *RecordTy = Ty->getAs()) + Record = cast(RecordTy->getDecl()); + else if (const InjectedClassNameType *InjectedTy + = Ty->getAs()) + Record = InjectedTy->getDecl(); + else { + Result->AddTypedTextChunk(ND->getNameAsString()); + break; + } + + Result->AddTypedTextChunk(Record->getNameAsString()); + if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) { + Result->AddChunk(Chunk(CodeCompletionString::CK_LeftAngle)); + AddTemplateParameterChunks(Context, Template, Result); + Result->AddChunk(Chunk(CodeCompletionString::CK_RightAngle)); + } + break; + } + } +} + /// \brief If possible, create a new code completion string for the given /// result. /// @@ -2027,7 +2128,7 @@ static void AddFunctionTypeQualsToCompletionString(CodeCompletionString *Result, /// result is all that is needed. CodeCompletionString * CodeCompletionResult::CreateCodeCompletionString(Sema &S, - CodeCompletionString *Result) { + CodeCompletionString *Result) { typedef CodeCompletionString::Chunk Chunk; if (Kind == RK_Pattern) @@ -2092,7 +2193,7 @@ CodeCompletionResult::CreateCodeCompletionString(Sema &S, if (FunctionDecl *Function = dyn_cast(ND)) { AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative, S.Context); - Result->AddTypedTextChunk(Function->getNameAsString()); + AddTypedNameChunk(S.Context, ND, Result); Result->AddChunk(Chunk(CodeCompletionString::CK_LeftParen)); AddFunctionParameterChunks(S.Context, Function, Result); Result->AddChunk(Chunk(CodeCompletionString::CK_RightParen)); @@ -2104,8 +2205,8 @@ CodeCompletionResult::CreateCodeCompletionString(Sema &S, AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative, S.Context); FunctionDecl *Function = FunTmpl->getTemplatedDecl(); - Result->AddTypedTextChunk(Function->getNameAsString()); - + AddTypedNameChunk(S.Context, Function, Result); + // Figure out which template parameters are deduced (or have default // arguments). llvm::SmallVector Deduced; diff --git a/test/Index/code-completion.cpp b/test/Index/code-completion.cpp index ddbf58e375..ae72280065 100644 --- a/test/Index/code-completion.cpp +++ b/test/Index/code-completion.cpp @@ -41,7 +41,7 @@ Z::operator int() const { // CHECK-MEMBER: FieldDecl:{ResultType int}{Text X::}{TypedText member} // CHECK-MEMBER: FieldDecl:{ResultType float}{Text Y::}{TypedText member} // CHECK-MEMBER: CXXMethod:{ResultType void}{Informative Y::}{TypedText memfunc}{LeftParen (}{Optional {Placeholder int i}}{RightParen )} -// CHECK-MEMBER: CXXConversion:{ResultType int}{TypedText operator int}{LeftParen (}{RightParen )}{Informative const} +// CHECK-MEMBER: CXXConversion:{TypedText operator int}{LeftParen (}{RightParen )}{Informative const} // CHECK-MEMBER: CXXMethod:{ResultType Z &}{TypedText operator=}{LeftParen (}{Placeholder const Z &}{RightParen )} // CHECK-MEMBER: CXXMethod:{ResultType X &}{Text X::}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} // CHECK-MEMBER: CXXMethod:{ResultType Y &}{Text Y::}{TypedText operator=}{LeftParen (}{Placeholder const Y &}{RightParen )} diff --git a/test/Index/complete-exprs.c b/test/Index/complete-exprs.c index 1619696494..34bae16484 100644 --- a/test/Index/complete-exprs.c +++ b/test/Index/complete-exprs.c @@ -25,7 +25,7 @@ void f5(float f) { } // RUN: c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s -// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s +// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s // CHECK-CC1: NotImplemented:{TypedText __PRETTY_FUNCTION__} (65) // CHECK-CC1: macro definition:{TypedText __VERSION__} (70) // CHECK-CC1: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (12) (unavailable) @@ -34,7 +34,7 @@ void f5(float f) { // CHECK-CC1: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30) // RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s // RUN: c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s -// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s +// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s // CHECK-CC3: macro definition:{TypedText __VERSION__} (70) // CHECK-CC3: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (50) // CHECK-CC3-NOT: NotImplemented:{TypedText float} @@ -59,7 +59,7 @@ void f5(float f) { // CHECK-CC6: NotImplemented:{TypedText volatile} (50) // RUN: c-index-test -code-completion-at=%s:24:4 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC7 %s -// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:24:4 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC7 %s +// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:24:4 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC7 %s // CHECK-CC7: ParmDecl:{ResultType float}{TypedText f} (8) // CHECK-CC7: VarDecl:{ResultType struct X}{TypedText f1} (50) (deprecated) // CHECK-CC7: FunctionDecl:{ResultType void}{TypedText f2}{LeftParen (}{RightParen )} (50) diff --git a/test/Index/complete-exprs.cpp b/test/Index/complete-exprs.cpp new file mode 100644 index 0000000000..1c99e7e7f8 --- /dev/null +++ b/test/Index/complete-exprs.cpp @@ -0,0 +1,37 @@ +// Line- and column-sensitive test; run lines follow. + +class string { + public: + string(); + string(const char *); + string(const char *, int n); +}; + +template +class vector { +public: + vector(const T &, unsigned n); + + template + vector(InputIterator first, InputIterator last); +}; + +void f() { + +} + +// RUN: c-index-test -code-completion-at=%s:20:2 %s | FileCheck -check-prefix=CHECK-CC1 %s +// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:20:2 %s | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: ClassDecl:{TypedText string} (50) +// CHECK-CC1: CXXConstructor:{TypedText string}{LeftParen (}{RightParen )} (50) +// CHECK-CC1: CXXConstructor:{TypedText string}{LeftParen (}{Placeholder const char *}{RightParen )} (50) +// CHECK-CC1: CXXConstructor:{TypedText string}{LeftParen (}{Placeholder const char *}{Comma , }{Placeholder int n}{RightParen )} (50) +// CHECK-CC1: ClassTemplate:{TypedText vector}{LeftAngle <}{Placeholder typename T}{RightAngle >} (50) +// CHECK-CC1: CXXConstructor:{TypedText vector}{LeftAngle <}{Placeholder typename T}{RightAngle >}{LeftParen (}{Placeholder T const &}{Comma , }{Placeholder unsigned int n}{RightParen )} (50) +// CHECK-CC1: FunctionTemplate:{ResultType void}{TypedText vector}{LeftAngle <}{Placeholder typename T}{RightAngle >}{LeftParen (}{Placeholder InputIterator first}{Comma , }{Placeholder InputIterator last}{RightParen )} (50) + +// RUN: c-index-test -code-completion-at=%s:19:1 %s | FileCheck -check-prefix=CHECK-CC2 %s +// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:19:1 %s | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: ClassDecl:{TypedText string} (50) +// CHECK-CC2-NOT: CXXConstructor +// CHECK-CC2: ClassTemplate:{TypedText vector}{LeftAngle <}{Placeholder typename T}{RightAngle >} (50)