diff options
-rw-r--r-- | include/clang/Parse/Action.h | 24 | ||||
-rw-r--r-- | include/clang/Sema/CodeCompleteConsumer.h | 12 | ||||
-rw-r--r-- | lib/Parse/ParseObjc.cpp | 11 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 6 | ||||
-rw-r--r-- | lib/Sema/SemaCodeComplete.cpp | 327 | ||||
-rw-r--r-- | test/Index/complete-method-decls.m | 15 |
6 files changed, 258 insertions, 137 deletions
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index a07c99d9f0..576f14915d 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -3043,7 +3043,7 @@ public: /// parsed. virtual void CodeCompleteObjCProtocolReferences(IdentifierLocPair *Protocols, unsigned NumProtocols) { } - + /// \brief Code completion for a protocol declaration or definition, after /// the @protocol but before any identifier. /// @@ -3133,6 +3133,28 @@ public: TypeTy *ReturnType, DeclPtrTy IDecl) { } + + /// \brief Code completion for a selector identifier within an Objective-C + /// method declaration. + /// + /// \param S The scope in which this code completion occurs. + /// + /// \param IsInstanceMethod Whether we are parsing an instance method (or, + /// if false, a class method). + /// + /// \param ReturnType If non-NULL, the specified return type of the method + /// being declared or defined. + /// + /// \param SelIdents The identifiers that occurred in the selector for the + /// method declaration prior to the code completion point. + /// + /// \param NumSelIdents The number of identifiers provided by SelIdents. + virtual void CodeCompleteObjCMethodDeclSelector(Scope *S, + bool IsInstanceMethod, + TypeTy *ReturnType, + IdentifierInfo **SelIdents, + unsigned NumSelIdents) { } + //@} }; diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index fd000b83df..1d9d250731 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -355,6 +355,10 @@ public: /// method, etc.) should be considered "informative". bool AllParametersAreInformative : 1; + /// \brief Whether we're completing a declaration of the given entity, + /// rather than a use of that entity. + bool DeclaringEntity : 1; + /// \brief If the result should have a nested-name-specifier, this is it. /// When \c QualifierIsInformative, the nested-name-specifier is /// informative rather than required. @@ -368,7 +372,7 @@ public: Priority(getPriorityFromDecl(Declaration)), StartParameter(0), Hidden(false), QualifierIsInformative(QualifierIsInformative), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), - Qualifier(Qualifier) { + DeclaringEntity(false), Qualifier(Qualifier) { } /// \brief Build a result that refers to a keyword or symbol. @@ -376,21 +380,21 @@ public: : Kind(RK_Keyword), Keyword(Keyword), Priority(Priority), StartParameter(0), Hidden(false), QualifierIsInformative(0), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), - Qualifier(0) { } + DeclaringEntity(false), Qualifier(0) { } /// \brief Build a result that refers to a macro. Result(IdentifierInfo *Macro, unsigned Priority = CCP_Macro) : Kind(RK_Macro), Macro(Macro), Priority(Priority), StartParameter(0), Hidden(false), QualifierIsInformative(0), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), - Qualifier(0) { } + DeclaringEntity(false), Qualifier(0) { } /// \brief Build a result that refers to a pattern. Result(CodeCompletionString *Pattern, unsigned Priority = CCP_CodePattern) : Kind(RK_Pattern), Pattern(Pattern), Priority(Priority), StartParameter(0), Hidden(false), QualifierIsInformative(0), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), - Qualifier(0) { } + DeclaringEntity(false), Qualifier(0) { } /// \brief Retrieve the declaration stored in this result. NamedDecl *getDeclaration() const { diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 849e57799a..79e432d611 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -868,6 +868,17 @@ Parser::DeclPtrTy Parser::ParseObjCMethodDecl(SourceLocation mLoc, ArgInfos.push_back(ArgInfo); KeyIdents.push_back(SelIdent); + // Code completion for the next piece of the selector. + if (Tok.is(tok::code_completion)) { + ConsumeCodeCompletionToken(); + Actions.CodeCompleteObjCMethodDeclSelector(getCurScope(), + mType == tok::minus, + ReturnType, + KeyIdents.data(), + KeyIdents.size()); + break; + } + // Check for another keyword selector. SourceLocation Loc; SelIdent = ParseObjCSelectorPiece(Loc); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index cbbaa1d64e..f4f873df2f 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -4572,6 +4572,12 @@ public: bool IsInstanceMethod, TypeTy *ReturnType, DeclPtrTy IDecl); + virtual void CodeCompleteObjCMethodDeclSelector(Scope *S, + bool IsInstanceMethod, + TypeTy *ReturnType, + IdentifierInfo **SelIdents, + unsigned NumSelIdents); + //@} //===--------------------------------------------------------------------===// diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 3e482532ca..8df9eace27 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -130,6 +130,8 @@ namespace { /// different levels of, e.g., the inheritance hierarchy. std::list<ShadowMap> ShadowMaps; + void AdjustResultPriorityForPreferredType(Result &R); + public: explicit ResultBuilder(Sema &SemaRef, LookupFilter Filter = 0) : SemaRef(SemaRef), Filter(Filter), AllowNestedNameSpecifiers(false) { } @@ -470,6 +472,134 @@ bool ResultBuilder::CheckHiddenResult(Result &R, DeclContext *CurContext, return false; } +enum SimplifiedTypeClass { + STC_Arithmetic, + STC_Array, + STC_Block, + STC_Function, + STC_ObjectiveC, + STC_Other, + STC_Pointer, + STC_Record, + STC_Void +}; + +/// \brief A simplified classification of types used to determine whether two +/// types are "similar enough" when adjusting priorities. +static SimplifiedTypeClass getSimplifiedTypeClass(CanQualType T) { + switch (T->getTypeClass()) { + case Type::Builtin: + switch (cast<BuiltinType>(T)->getKind()) { + case BuiltinType::Void: + return STC_Void; + + case BuiltinType::NullPtr: + return STC_Pointer; + + case BuiltinType::Overload: + case BuiltinType::Dependent: + case BuiltinType::UndeducedAuto: + return STC_Other; + + case BuiltinType::ObjCId: + case BuiltinType::ObjCClass: + case BuiltinType::ObjCSel: + return STC_ObjectiveC; + + default: + return STC_Arithmetic; + } + return STC_Other; + + case Type::Complex: + return STC_Arithmetic; + + case Type::Pointer: + return STC_Pointer; + + case Type::BlockPointer: + return STC_Block; + + case Type::LValueReference: + case Type::RValueReference: + return getSimplifiedTypeClass(T->getAs<ReferenceType>()->getPointeeType()); + + case Type::ConstantArray: + case Type::IncompleteArray: + case Type::VariableArray: + case Type::DependentSizedArray: + return STC_Array; + + case Type::DependentSizedExtVector: + case Type::Vector: + case Type::ExtVector: + return STC_Arithmetic; + + case Type::FunctionProto: + case Type::FunctionNoProto: + return STC_Function; + + case Type::Record: + return STC_Record; + + case Type::Enum: + return STC_Arithmetic; + + case Type::ObjCObject: + case Type::ObjCInterface: + case Type::ObjCObjectPointer: + return STC_ObjectiveC; + + default: + return STC_Other; + } +} + +/// \brief Get the type that a given expression will have if this declaration +/// is used as an expression in its "typical" code-completion form. +static QualType getDeclUsageType(ASTContext &C, NamedDecl *ND) { + ND = cast<NamedDecl>(ND->getUnderlyingDecl()); + + if (TypeDecl *Type = dyn_cast<TypeDecl>(ND)) + return C.getTypeDeclType(Type); + if (ObjCInterfaceDecl *Iface = dyn_cast<ObjCInterfaceDecl>(ND)) + return C.getObjCInterfaceType(Iface); + + QualType T; + if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) + T = Function->getResultType(); + else if (ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(ND)) + T = Method->getResultType(); + else if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND)) + T = FunTmpl->getTemplatedDecl()->getResultType(); + else if (EnumConstantDecl *Enumerator = dyn_cast<EnumConstantDecl>(ND)) + T = C.getTypeDeclType(cast<EnumDecl>(Enumerator->getDeclContext())); + else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND)) + T = Property->getType(); + else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND)) + T = Value->getType(); + else + return QualType(); + + return T.getNonReferenceType(); +} + +void ResultBuilder::AdjustResultPriorityForPreferredType(Result &R) { + QualType T = getDeclUsageType(SemaRef.Context, R.Declaration); + if (T.isNull()) + return; + + CanQualType TC = SemaRef.Context.getCanonicalType(T); + // Check for exactly-matching types (modulo qualifiers). + if (SemaRef.Context.hasSameUnqualifiedType(PreferredType, TC)) + R.Priority /= CCF_ExactTypeMatch; + // Check for nearly-matching types, based on classification of each. + else if ((getSimplifiedTypeClass(PreferredType) + == getSimplifiedTypeClass(TC)) && + !(PreferredType->isEnumeralType() && TC->isEnumeralType())) + R.Priority /= CCF_SimilarTypeMatch; +} + void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) { assert(!ShadowMaps.empty() && "Must enter into a results scope"); @@ -554,8 +684,9 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) { if (AsNestedNameSpecifier) { R.StartsNestedNameSpecifier = true; R.Priority = CCP_NestedNameSpecifier; - } - + } else if (!PreferredType.isNull()) + AdjustResultPriorityForPreferredType(R); + // If this result is supposed to have an informative qualifier, add one. if (R.QualifierIsInformative && !R.Qualifier && !R.StartsNestedNameSpecifier) { @@ -575,118 +706,6 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) { Results.push_back(R); } -enum SimplifiedTypeClass { - STC_Arithmetic, - STC_Array, - STC_Block, - STC_Function, - STC_ObjectiveC, - STC_Other, - STC_Pointer, - STC_Record, - STC_Void -}; - -/// \brief A simplified classification of types used to determine whether two -/// types are "similar enough" when adjusting priorities. -static SimplifiedTypeClass getSimplifiedTypeClass(CanQualType T) { - switch (T->getTypeClass()) { - case Type::Builtin: - switch (cast<BuiltinType>(T)->getKind()) { - case BuiltinType::Void: - return STC_Void; - - case BuiltinType::NullPtr: - return STC_Pointer; - - case BuiltinType::Overload: - case BuiltinType::Dependent: - case BuiltinType::UndeducedAuto: - return STC_Other; - - case BuiltinType::ObjCId: - case BuiltinType::ObjCClass: - case BuiltinType::ObjCSel: - return STC_ObjectiveC; - - default: - return STC_Arithmetic; - } - return STC_Other; - - case Type::Complex: - return STC_Arithmetic; - - case Type::Pointer: - return STC_Pointer; - - case Type::BlockPointer: - return STC_Block; - - case Type::LValueReference: - case Type::RValueReference: - return getSimplifiedTypeClass(T->getAs<ReferenceType>()->getPointeeType()); - - case Type::ConstantArray: - case Type::IncompleteArray: - case Type::VariableArray: - case Type::DependentSizedArray: - return STC_Array; - - case Type::DependentSizedExtVector: - case Type::Vector: - case Type::ExtVector: - return STC_Arithmetic; - - case Type::FunctionProto: - case Type::FunctionNoProto: - return STC_Function; - - case Type::Record: - return STC_Record; - - case Type::Enum: - return STC_Arithmetic; - - case Type::ObjCObject: - case Type::ObjCInterface: - case Type::ObjCObjectPointer: - return STC_ObjectiveC; - - default: - return STC_Other; - } -} - -/// \brief Get the type that a given expression will have if this declaration -/// is used as an expression in its "typical" code-completion form. -static QualType getDeclUsageType(ASTContext &C, NamedDecl *ND) { - ND = cast<NamedDecl>(ND->getUnderlyingDecl()); - - if (TypeDecl *Type = dyn_cast<TypeDecl>(ND)) - return C.getTypeDeclType(Type); - if (ObjCInterfaceDecl *Iface = dyn_cast<ObjCInterfaceDecl>(ND)) - return C.getObjCInterfaceType(Iface); - - QualType T; - if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) - T = Function->getResultType(); - else if (ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(ND)) - T = Method->getResultType(); - else if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND)) - T = FunTmpl->getTemplatedDecl()->getResultType(); - else if (EnumConstantDecl *Enumerator = dyn_cast<EnumConstantDecl>(ND)) - T = C.getTypeDeclType(cast<EnumDecl>(Enumerator->getDeclContext())); - else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND)) - T = Property->getType(); - else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND)) - T = Value->getType(); - else - return QualType(); - - return T.getNonReferenceType(); -} - void ResultBuilder::AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding, bool InBaseClass = false) { if (R.Kind != Result::RK_Declaration) { @@ -740,20 +759,8 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext, if (InBaseClass) R.Priority += CCD_InBaseClass; - if (!PreferredType.isNull()) { - if (ValueDecl *Value = dyn_cast<ValueDecl>(R.Declaration)) { - CanQualType T = SemaRef.Context.getCanonicalType( - getDeclUsageType(SemaRef.Context, Value)); - // Check for exactly-matching types (modulo qualifiers). - if (SemaRef.Context.hasSameUnqualifiedType(PreferredType, T)) - R.Priority /= CCF_ExactTypeMatch; - // Check for nearly-matching types, based on classification of each. - else if ((getSimplifiedTypeClass(PreferredType) - == getSimplifiedTypeClass(T)) && - !(PreferredType->isEnumeralType() && T->isEnumeralType())) - R.Priority /= CCF_SimilarTypeMatch; - } - } + if (!PreferredType.isNull()) + AdjustResultPriorityForPreferredType(R); // Insert this result into the set of results. Results.push_back(R); @@ -1955,9 +1962,9 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(Idx)) Keyword += II->getName().str(); Keyword += ":"; - if (Idx < StartParameter || AllParametersAreInformative) { + if (Idx < StartParameter || AllParametersAreInformative) Result->AddInformativeChunk(Keyword); - } else if (Idx == StartParameter) + else if (Idx == StartParameter) Result->AddTypedTextChunk(Keyword); else Result->AddTextChunk(Keyword); @@ -1972,14 +1979,18 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { Arg = "(" + Arg + ")"; if (IdentifierInfo *II = (*P)->getIdentifier()) Arg += II->getName().str(); - if (AllParametersAreInformative) + if (DeclaringEntity) + Result->AddTextChunk(Arg); + else if (AllParametersAreInformative) Result->AddInformativeChunk(Arg); else Result->AddPlaceholderChunk(Arg); } if (Method->isVariadic()) { - if (AllParametersAreInformative) + if (DeclaringEntity) + Result->AddTextChunk(", ..."); + else if (AllParametersAreInformative) Result->AddInformativeChunk(", ..."); else Result->AddPlaceholderChunk(", ..."); @@ -4133,3 +4144,55 @@ void Sema::CodeCompleteObjCMethodDecl(Scope *S, HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); } + +void Sema::CodeCompleteObjCMethodDeclSelector(Scope *S, + bool IsInstanceMethod, + TypeTy *ReturnTy, + IdentifierInfo **SelIdents, + unsigned NumSelIdents) { + llvm::DenseMap<Selector, ObjCMethodList> &Pool + = IsInstanceMethod? InstanceMethodPool : FactoryMethodPool; + + // If we have an external source, load the entire class method + // pool from the PCH file. + if (ExternalSource) { + for (uint32_t I = 0, N = ExternalSource->GetNumExternalSelectors(); + I != N; ++I) { + Selector Sel = ExternalSource->GetExternalSelector(I); + if (Sel.isNull() || InstanceMethodPool.count(Sel) || + FactoryMethodPool.count(Sel)) + continue; + + ReadMethodPool(Sel, IsInstanceMethod); + } + } + + // Build the set of methods we can see. + typedef CodeCompleteConsumer::Result Result; + ResultBuilder Results(*this); + + if (ReturnTy) + Results.setPreferredType(GetTypeFromParser(ReturnTy).getNonReferenceType()); + + Results.EnterNewScope(); + for (llvm::DenseMap<Selector, ObjCMethodList>::iterator M = Pool.begin(), + MEnd = Pool.end(); + M != MEnd; + ++M) { + for (ObjCMethodList *MethList = &M->second; MethList && MethList->Method; + MethList = MethList->Next) { + if (!isAcceptableObjCMethod(MethList->Method, MK_Any, SelIdents, + NumSelIdents)) + continue; + + Result R(MethList->Method, 0); + R.StartParameter = NumSelIdents; + R.AllParametersAreInformative = false; + R.DeclaringEntity = true; + Results.MaybeAddResult(R, CurContext); + } + } + + Results.ExitScope(); + HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); +} diff --git a/test/Index/complete-method-decls.m b/test/Index/complete-method-decls.m index 51229d8d07..802b360f00 100644 --- a/test/Index/complete-method-decls.m +++ b/test/Index/complete-method-decls.m @@ -42,6 +42,16 @@ - (id)categoryFunction:(int)x { return self; } @end +@interface C +- (int)first:(int)x second:(float)y third:(double)z; +- (id)first:(int)x second2:(float)y third:(double)z; +- (void*)first:(int)x second3:(float)y third:(double)z; +@end + +@interface D +- (int)first:(int)x second4:(float)y third:(double)z; +@end + // RUN: c-index-test -code-completion-at=%s:17:3 %s | FileCheck -check-prefix=CHECK-CC1 %s // CHECK-CC1: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText abc} // CHECK-CC1: NotImplemented:{LeftParen (}{Text int}{RightParen )}{TypedText getInt} @@ -79,4 +89,9 @@ // CHECK-CC6: NotImplemented:{TypedText initWithTwoInts}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace }{Text second}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text y}{HorizontalSpace }{LeftBrace {}{VerticalSpace // RUN: c-index-test -code-completion-at=%s:42:3 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC7 %s // CHECK-CC7: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText categoryFunction}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace }{LeftBrace {}{VerticalSpace +// RUN: c-index-test -code-completion-at=%s:52:21 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC8 %s +// CHECK-CC8: ObjCInstanceMethodDecl:{ResultType id}{Informative first:}{TypedText second2:}{Text (float)y}{HorizontalSpace }{Text third:}{Text (double)z} (20) +// CHECK-CC8: ObjCInstanceMethodDecl:{ResultType void *}{Informative first:}{TypedText second3:}{Text (float)y}{HorizontalSpace }{Text third:}{Text (double)z} (20) +// CHECK-CC8: ObjCInstanceMethodDecl:{ResultType int}{Informative first:}{TypedText second:}{Text (float)y}{HorizontalSpace }{Text third:}{Text (double)z} (5) + |