diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-11-19 07:41:15 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-11-19 07:41:15 +0000 |
commit | 4ad9685b3e2d5e2923c9cda7baaf7973ef0b1c62 (patch) | |
tree | fb5b73019b0ee0653b36a251f2601e5df2e25f13 | |
parent | 217acbfa3524d5805fda7900b26c1e779443588d (diff) |
Objective-C code completion within properties after "setter = " or
"getter = ", to provide suitable method names.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89334 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Parse/Action.h | 40 | ||||
-rw-r--r-- | include/clang/Parse/Parser.h | 3 | ||||
-rw-r--r-- | include/clang/Sema/CodeCompleteConsumer.h | 18 | ||||
-rw-r--r-- | lib/Parse/ParseObjc.cpp | 17 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 7 | ||||
-rw-r--r-- | lib/Sema/SemaCodeComplete.cpp | 170 |
6 files changed, 215 insertions, 40 deletions
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 6ebdf07958..3d3232b6f6 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -2330,7 +2330,45 @@ public: /// /// \param S the scope in which the operator keyword occurs. virtual void CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) { } - + + /// \brief Code completion for the getter of an Objective-C property + /// declaration. + /// + /// This code completion action is invoked when the code-completion + /// token is found after the "getter = " in a property declaration. + /// + /// \param S the scope in which the property is being declared. + /// + /// \param ClassDecl the Objective-C class or category in which the property + /// is being defined. + /// + /// \param Methods the set of methods declared thus far within \p ClassDecl. + /// + /// \param NumMethods the number of methods in \p Methods + virtual void CodeCompleteObjCPropertyGetter(Scope *S, DeclPtrTy ClassDecl, + DeclPtrTy *Methods, + unsigned NumMethods) { + } + + /// \brief Code completion for the setter of an Objective-C property + /// declaration. + /// + /// This code completion action is invoked when the code-completion + /// token is found after the "setter = " in a property declaration. + /// + /// \param S the scope in which the property is being declared. + /// + /// \param ClassDecl the Objective-C class or category in which the property + /// is being defined. + /// + /// \param Methods the set of methods declared thus far within \p ClassDecl. + /// + /// \param NumMethods the number of methods in \p Methods + virtual void CodeCompleteObjCPropertySetter(Scope *S, DeclPtrTy ClassDecl, + DeclPtrTy *Methods, + unsigned NumMethods) { + } + /// \brief Code completion for an ObjC message expression that refers to /// a class method. /// diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index e7dabdbd5b..f7ccccac09 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -810,7 +810,8 @@ private: DeclPtrTy ParseObjCMethodDecl(SourceLocation mLoc, tok::TokenKind mType, DeclPtrTy classDecl, tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword); - void ParseObjCPropertyAttribute(ObjCDeclSpec &DS); + void ParseObjCPropertyAttribute(ObjCDeclSpec &DS, DeclPtrTy ClassDecl, + DeclPtrTy *Methods, unsigned NumMethods); DeclPtrTy ParseObjCMethodDefinition(); diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index 3c5428bdb9..ed747613c3 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -270,7 +270,11 @@ public: /// \brief Whether this declaration is the beginning of a /// nested-name-specifier and, therefore, should be followed by '::'. bool StartsNestedNameSpecifier : 1; - + + /// \brief Whether all parameters (of a function, Objective-C + /// method, etc.) should be considered "informative". + bool AllParametersAreInformative : 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. @@ -283,25 +287,29 @@ public: : Kind(RK_Declaration), Declaration(Declaration), Rank(Rank), StartParameter(0), Hidden(false), QualifierIsInformative(QualifierIsInformative), - StartsNestedNameSpecifier(false), Qualifier(Qualifier) { } + StartsNestedNameSpecifier(false), AllParametersAreInformative(false), + Qualifier(Qualifier) { } /// \brief Build a result that refers to a keyword or symbol. Result(const char *Keyword, unsigned Rank) : Kind(RK_Keyword), Keyword(Keyword), Rank(Rank), StartParameter(0), Hidden(false), QualifierIsInformative(0), - StartsNestedNameSpecifier(false), Qualifier(0) { } + StartsNestedNameSpecifier(false), AllParametersAreInformative(false), + Qualifier(0) { } /// \brief Build a result that refers to a macro. Result(IdentifierInfo *Macro, unsigned Rank) : Kind(RK_Macro), Macro(Macro), Rank(Rank), StartParameter(0), Hidden(false), QualifierIsInformative(0), - StartsNestedNameSpecifier(false), Qualifier(0) { } + StartsNestedNameSpecifier(false), AllParametersAreInformative(false), + Qualifier(0) { } /// \brief Build a result that refers to a pattern. Result(CodeCompletionString *Pattern, unsigned Rank) : Kind(RK_Pattern), Pattern(Pattern), Rank(Rank), StartParameter(0), Hidden(false), QualifierIsInformative(0), - StartsNestedNameSpecifier(false), Qualifier(0) { } + StartsNestedNameSpecifier(false), AllParametersAreInformative(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 9943428bc5..5ba1dd17bd 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -326,7 +326,8 @@ void Parser::ParseObjCInterfaceDeclList(DeclPtrTy interfaceDecl, ObjCDeclSpec OCDS; // Parse property attribute list, if any. if (Tok.is(tok::l_paren)) - ParseObjCPropertyAttribute(OCDS); + ParseObjCPropertyAttribute(OCDS, interfaceDecl, + allMethods.data(), allMethods.size()); struct ObjCPropertyCallback : FieldCallback { Parser &P; @@ -425,7 +426,9 @@ void Parser::ParseObjCInterfaceDeclList(DeclPtrTy interfaceDecl, /// copy /// nonatomic /// -void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) { +void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS, DeclPtrTy ClassDecl, + DeclPtrTy *Methods, + unsigned NumMethods) { assert(Tok.getKind() == tok::l_paren); SourceLocation LHSLoc = ConsumeParen(); // consume '(' @@ -462,6 +465,16 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) { tok::r_paren)) return; + if (Tok.is(tok::code_completion)) { + if (II->getNameStart()[0] == 's') + Actions.CodeCompleteObjCPropertySetter(CurScope, ClassDecl, + Methods, NumMethods); + else + Actions.CodeCompleteObjCPropertyGetter(CurScope, ClassDecl, + Methods, NumMethods); + ConsumeToken(); + } + if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_expected_ident); SkipUntil(tok::r_paren); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 0da822ec2e..ad4c90bdc2 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -3647,6 +3647,13 @@ public: virtual void CodeCompleteOperatorName(Scope *S); virtual void CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS); + virtual void CodeCompleteObjCPropertyGetter(Scope *S, DeclPtrTy ClassDecl, + DeclPtrTy *Methods, + unsigned NumMethods); + virtual void CodeCompleteObjCPropertySetter(Scope *S, DeclPtrTy ClassDecl, + DeclPtrTy *Methods, + unsigned NumMethods); + virtual void CodeCompleteObjCClassMessage(Scope *S, IdentifierInfo *FName, SourceLocation FNameLoc, IdentifierInfo **SelIdents, diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 59060336fd..6dbb4426c2 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -967,7 +967,7 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(Idx)) Keyword += II->getName().str(); Keyword += ":"; - if (Idx < StartParameter) { + if (Idx < StartParameter || AllParametersAreInformative) { Result->AddInformativeChunk(Keyword); } else if (Idx == StartParameter) Result->AddTypedTextChunk(Keyword); @@ -984,7 +984,10 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { Arg = "(" + Arg + ")"; if (IdentifierInfo *II = (*P)->getIdentifier()) Arg += II->getName().str(); - Result->AddPlaceholderChunk(Arg); + if (AllParametersAreInformative) + Result->AddInformativeChunk(Arg); + else + Result->AddPlaceholderChunk(Arg); } return Result; @@ -1721,6 +1724,35 @@ void Sema::CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) { HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); } +/// \brief Descripts the kind of Objective-C method that we want to find +/// via code completion. +enum ObjCMethodKind { + MK_Any, //< Any kind of method, provided it means other specified criteria. + MK_ZeroArgSelector, //< Zero-argument (unary) selector. + MK_OneArgSelector //< One-argument selector. +}; + +static bool isAcceptableObjCMethod(ObjCMethodDecl *Method, + ObjCMethodKind WantKind, + IdentifierInfo **SelIdents, + unsigned NumSelIdents) { + Selector Sel = Method->getSelector(); + if (NumSelIdents > Sel.getNumArgs()) + return false; + + switch (WantKind) { + case MK_Any: break; + case MK_ZeroArgSelector: return Sel.isUnarySelector(); + case MK_OneArgSelector: return Sel.getNumArgs() == 1; + } + + for (unsigned I = 0; I != NumSelIdents; ++I) + if (SelIdents[I] != Sel.getIdentifierInfoForSlot(I)) + return false; + + return true; +} + /// \brief Add all of the Objective-C methods in the given Objective-C /// container to the set of results. /// @@ -1740,6 +1772,7 @@ void Sema::CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) { /// \param Results the structure into which we'll add results. static void AddObjCMethods(ObjCContainerDecl *Container, bool WantInstanceMethods, + ObjCMethodKind WantKind, IdentifierInfo **SelIdents, unsigned NumSelIdents, DeclContext *CurContext, @@ -1751,20 +1784,12 @@ static void AddObjCMethods(ObjCContainerDecl *Container, if ((*M)->isInstanceMethod() == WantInstanceMethods) { // Check whether the selector identifiers we've been given are a // subset of the identifiers for this particular method. - Selector Sel = (*M)->getSelector(); - if (NumSelIdents > Sel.getNumArgs()) - continue; - - bool Failed = false; - for (unsigned I = 0; I != NumSelIdents && !Failed; ++I) - if (SelIdents[I] != Sel.getIdentifierInfoForSlot(I)) - Failed = true; - - if (Failed) + if (!isAcceptableObjCMethod(*M, WantKind, SelIdents, NumSelIdents)) continue; - + Result R = Result(*M, 0); R.StartParameter = NumSelIdents; + R.AllParametersAreInformative = (WantKind != MK_Any); Results.MaybeAddResult(R, CurContext); } } @@ -1778,14 +1803,14 @@ static void AddObjCMethods(ObjCContainerDecl *Container, for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(), E = Protocols.end(); I != E; ++I) - AddObjCMethods(*I, WantInstanceMethods, SelIdents, NumSelIdents, + AddObjCMethods(*I, WantInstanceMethods, WantKind, SelIdents, NumSelIdents, CurContext, Results); // Add methods in categories. for (ObjCCategoryDecl *CatDecl = IFace->getCategoryList(); CatDecl; CatDecl = CatDecl->getNextClassCategory()) { - AddObjCMethods(CatDecl, WantInstanceMethods, SelIdents, NumSelIdents, - CurContext, Results); + AddObjCMethods(CatDecl, WantInstanceMethods, WantKind, SelIdents, + NumSelIdents, CurContext, Results); // Add a categories protocol methods. const ObjCList<ObjCProtocolDecl> &Protocols @@ -1793,24 +1818,104 @@ static void AddObjCMethods(ObjCContainerDecl *Container, for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(), E = Protocols.end(); I != E; ++I) - AddObjCMethods(*I, WantInstanceMethods, SelIdents, NumSelIdents, - CurContext, Results); + AddObjCMethods(*I, WantInstanceMethods, WantKind, SelIdents, + NumSelIdents, CurContext, Results); // Add methods in category implementations. if (ObjCCategoryImplDecl *Impl = CatDecl->getImplementation()) - AddObjCMethods(Impl, WantInstanceMethods, SelIdents, NumSelIdents, - CurContext, Results); + AddObjCMethods(Impl, WantInstanceMethods, WantKind, SelIdents, + NumSelIdents, CurContext, Results); } // Add methods in superclass. if (IFace->getSuperClass()) - AddObjCMethods(IFace->getSuperClass(), WantInstanceMethods, SelIdents, - NumSelIdents, CurContext,Results); + AddObjCMethods(IFace->getSuperClass(), WantInstanceMethods, WantKind, + SelIdents, NumSelIdents, CurContext, Results); // Add methods in our implementation, if any. if (ObjCImplementationDecl *Impl = IFace->getImplementation()) - AddObjCMethods(Impl, WantInstanceMethods, SelIdents, NumSelIdents, - CurContext, Results); + AddObjCMethods(Impl, WantInstanceMethods, WantKind, SelIdents, + NumSelIdents, CurContext, Results); +} + + +void Sema::CodeCompleteObjCPropertyGetter(Scope *S, DeclPtrTy ClassDecl, + DeclPtrTy *Methods, + unsigned NumMethods) { + typedef CodeCompleteConsumer::Result Result; + + // Try to find the interface where getters might live. + ObjCInterfaceDecl *Class + = dyn_cast_or_null<ObjCInterfaceDecl>(ClassDecl.getAs<Decl>()); + if (!Class) { + if (ObjCCategoryDecl *Category + = dyn_cast_or_null<ObjCCategoryDecl>(ClassDecl.getAs<Decl>())) + Class = Category->getClassInterface(); + + if (!Class) + return; + } + + // Find all of the potential getters. + ResultBuilder Results(*this); + Results.EnterNewScope(); + + // FIXME: We need to do this because Objective-C methods don't get + // pushed into DeclContexts early enough. Argh! + for (unsigned I = 0; I != NumMethods; ++I) { + if (ObjCMethodDecl *Method + = dyn_cast_or_null<ObjCMethodDecl>(Methods[I].getAs<Decl>())) + if (Method->isInstanceMethod() && + isAcceptableObjCMethod(Method, MK_ZeroArgSelector, 0, 0)) { + Result R = Result(Method, 0); + R.AllParametersAreInformative = true; + Results.MaybeAddResult(R, CurContext); + } + } + + AddObjCMethods(Class, true, MK_ZeroArgSelector, 0, 0, CurContext, Results); + Results.ExitScope(); + HandleCodeCompleteResults(this, CodeCompleter,Results.data(),Results.size()); +} + +void Sema::CodeCompleteObjCPropertySetter(Scope *S, DeclPtrTy ObjCImplDecl, + DeclPtrTy *Methods, + unsigned NumMethods) { + typedef CodeCompleteConsumer::Result Result; + + // Try to find the interface where setters might live. + ObjCInterfaceDecl *Class + = dyn_cast_or_null<ObjCInterfaceDecl>(ObjCImplDecl.getAs<Decl>()); + if (!Class) { + if (ObjCCategoryDecl *Category + = dyn_cast_or_null<ObjCCategoryDecl>(ObjCImplDecl.getAs<Decl>())) + Class = Category->getClassInterface(); + + if (!Class) + return; + } + + // Find all of the potential getters. + ResultBuilder Results(*this); + Results.EnterNewScope(); + + // FIXME: We need to do this because Objective-C methods don't get + // pushed into DeclContexts early enough. Argh! + for (unsigned I = 0; I != NumMethods; ++I) { + if (ObjCMethodDecl *Method + = dyn_cast_or_null<ObjCMethodDecl>(Methods[I].getAs<Decl>())) + if (Method->isInstanceMethod() && + isAcceptableObjCMethod(Method, MK_OneArgSelector, 0, 0)) { + Result R = Result(Method, 0); + R.AllParametersAreInformative = true; + Results.MaybeAddResult(R, CurContext); + } + } + + AddObjCMethods(Class, true, MK_OneArgSelector, 0, 0, CurContext, Results); + + Results.ExitScope(); + HandleCodeCompleteResults(this, CodeCompleter,Results.data(),Results.size()); } void Sema::CodeCompleteObjCClassMessage(Scope *S, IdentifierInfo *FName, @@ -1873,7 +1978,8 @@ void Sema::CodeCompleteObjCClassMessage(Scope *S, IdentifierInfo *FName, // superclasses, categories, implementation, etc. ResultBuilder Results(*this); Results.EnterNewScope(); - AddObjCMethods(CDecl, false, SelIdents, NumSelIdents, CurContext, Results); + AddObjCMethods(CDecl, false, MK_Any, SelIdents, NumSelIdents, CurContext, + Results); Results.ExitScope(); // This also suppresses remaining diagnostics. @@ -1910,8 +2016,8 @@ void Sema::CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver, ReceiverType->isObjCQualifiedClassType()) { if (ObjCMethodDecl *CurMethod = getCurMethodDecl()) { if (ObjCInterfaceDecl *ClassDecl = CurMethod->getClassInterface()) - AddObjCMethods(ClassDecl, false, SelIdents, NumSelIdents, CurContext, - Results); + AddObjCMethods(ClassDecl, false, MK_Any, SelIdents, NumSelIdents, + CurContext, Results); } } // Handle messages to a qualified ID ("id<foo>"). @@ -1921,20 +2027,22 @@ void Sema::CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver, for (ObjCObjectPointerType::qual_iterator I = QualID->qual_begin(), E = QualID->qual_end(); I != E; ++I) - AddObjCMethods(*I, true, SelIdents, NumSelIdents, CurContext, Results); + AddObjCMethods(*I, true, MK_Any, SelIdents, NumSelIdents, CurContext, + Results); } // Handle messages to a pointer to interface type. else if (const ObjCObjectPointerType *IFacePtr = ReceiverType->getAsObjCInterfacePointerType()) { // Search the class, its superclasses, etc., for instance methods. - AddObjCMethods(IFacePtr->getInterfaceDecl(), true, SelIdents, NumSelIdents, - CurContext, Results); + AddObjCMethods(IFacePtr->getInterfaceDecl(), true, MK_Any, SelIdents, + NumSelIdents, CurContext, Results); // Search protocols for instance methods. for (ObjCObjectPointerType::qual_iterator I = IFacePtr->qual_begin(), E = IFacePtr->qual_end(); I != E; ++I) - AddObjCMethods(*I, true, SelIdents, NumSelIdents, CurContext, Results); + AddObjCMethods(*I, true, MK_Any, SelIdents, NumSelIdents, CurContext, + Results); } Results.ExitScope(); |