From: Fariborz Jahanian Date: Tue, 2 Oct 2007 16:38:50 +0000 (+0000) Subject: This patch introduces the ObjcCategoryImplDecl class and does the checking related to X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8f3fde00ad4d4f943321e338b914ae4740711c84;p=clang This patch introduces the ObjcCategoryImplDecl class and does the checking related to unimplemented methods in category implementation. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@42531 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/AST/Decl.cpp b/AST/Decl.cpp index ddf4187a0e..d23f3cb6f3 100644 --- a/AST/Decl.cpp +++ b/AST/Decl.cpp @@ -34,6 +34,7 @@ static unsigned nForwardProtocolDecls = 0; static unsigned nCategoryDecls = 0; static unsigned nIvarDecls = 0; static unsigned nObjcImplementationDecls = 0; +static unsigned nObjcCategoryImpl = 0; static bool StatSwitch = false; @@ -136,6 +137,10 @@ void Decl::PrintStats() { nObjcImplementationDecls, (int)sizeof(ObjcImplementationDecl), int(nObjcImplementationDecls*sizeof(ObjcImplementationDecl))); + fprintf(stderr, " %d class implementation decls, %d each (%d bytes)\n", + nObjcCategoryImpl, (int)sizeof(ObjcCategoryImplDecl), + int(nObjcCategoryImpl*sizeof(ObjcCategoryImplDecl))); + fprintf(stderr, "Total bytes = %d\n", int(nFuncs*sizeof(FunctionDecl)+nBlockVars*sizeof(BlockVarDecl)+ nFileVars*sizeof(FileVarDecl)+nParmVars*sizeof(ParmVarDecl)+ @@ -199,6 +204,9 @@ void Decl::addDeclKind(const Kind k) { case ObjcImplementation: nObjcImplementationDecls++; break; + case ObjcCategoryImpl: + nObjcCategoryImpl++; + break; } } @@ -344,7 +352,7 @@ void ObjcProtocolDecl::ObjcAddProtoMethods(ObjcMethodDecl **insMethods, } /// ObjcAddCat - Insert instance and methods declarations into -/// ObjcProtocolDecl's CatInsMethods and CatClsMethods fields. +/// ObjcCategoryDecl's CatInsMethods and CatClsMethods fields. /// void ObjcCategoryDecl::ObjcAddCatMethods(ObjcMethodDecl **insMethods, unsigned numInsMembers, @@ -362,6 +370,25 @@ void ObjcCategoryDecl::ObjcAddCatMethods(ObjcMethodDecl **insMethods, } } +/// ObjcAddCatImplMethods - Insert instance and methods declarations into +/// ObjcCategoryImplDecl's CatInsMethods and CatClsMethods fields. +/// +void ObjcCategoryImplDecl::ObjcAddCatImplMethods(ObjcMethodDecl **insMethods, + unsigned numInsMembers, + ObjcMethodDecl **clsMethods, + unsigned numClsMembers) { + NumCatInsMethods = numInsMembers; + if (numInsMembers) { + CatInsMethods = new ObjcMethodDecl*[numInsMembers]; + memcpy(CatInsMethods, insMethods, numInsMembers*sizeof(ObjcMethodDecl*)); + } + NumCatClsMethods = numClsMembers; + if (numClsMembers) { + CatClsMethods = new ObjcMethodDecl*[numClsMembers]; + memcpy(CatClsMethods, clsMethods, numClsMembers*sizeof(ObjcMethodDecl*)); + } +} + /// ObjcAddImplMethods - Insert instance and methods declarations into /// ObjcImplementationDecl's InsMethods and ClsMethods fields. /// diff --git a/Parse/ParseObjc.cpp b/Parse/ParseObjc.cpp index ea536599ff..cda2ab5fe6 100644 --- a/Parse/ParseObjc.cpp +++ b/Parse/ParseObjc.cpp @@ -934,7 +934,10 @@ Parser::DeclTy *Parser::ParseObjCAtImplementationDeclaration( return 0; } rparenLoc = ConsumeParen(); - return 0; + DeclTy *ImplCatType = Actions.ObjcStartCategoryImplementation(CurScope, + atLoc, nameId, nameLoc, categoryId, + categoryLoc); + return ImplCatType; } // We have a class implementation SourceLocation superClassLoc; diff --git a/Sema/Sema.h b/Sema/Sema.h index 488d105ded..74f79d4f3f 100644 --- a/Sema/Sema.h +++ b/Sema/Sema.h @@ -49,6 +49,8 @@ namespace clang { class ObjcInterfaceDecl; class ObjcProtocolDecl; class ObjcImplementationDecl; + class ObjcCategoryImplDecl; + class ObjcCategoryDecl; /// Sema - This implements semantic analysis and AST building for C. class Sema : public Action { @@ -219,6 +221,11 @@ private: void ImplMethodsVsClassMethods(ObjcImplementationDecl* IMPDecl, ObjcInterfaceDecl* IDecl); + /// ImplCategoryMethodsVsIntfMethods - Checks that methods declared in the + /// category interface is implemented in the category @implementation. + void ImplCategoryMethodsVsIntfMethods(ObjcCategoryImplDecl *CatImplDecl, + ObjcCategoryDecl *CatClassDecl); + //===--------------------------------------------------------------------===// // Statement Parsing Callbacks: SemaStmt.cpp. public: @@ -395,6 +402,13 @@ public: IdentifierInfo *SuperClassname, SourceLocation SuperClassLoc); + virtual DeclTy *ObjcStartCategoryImplementation(Scope* S, + SourceLocation AtCatImplLoc, + IdentifierInfo *ClassName, + SourceLocation ClassLoc, + IdentifierInfo *CatName, + SourceLocation CatLoc); + virtual DeclTy *ObjcClassDeclaration(Scope *S, SourceLocation AtClassLoc, IdentifierInfo **IdentList, unsigned NumElts); diff --git a/Sema/SemaDecl.cpp b/Sema/SemaDecl.cpp index 20226cb9b4..f92bee64dc 100644 --- a/Sema/SemaDecl.cpp +++ b/Sema/SemaDecl.cpp @@ -1068,6 +1068,25 @@ Sema::DeclTy *Sema::ObjcStartCatInterface(Scope* S, return CDecl; } +/// ObjcStartCategoryImplementation - Perform semantic checks on the +/// category implementation declaration and build an ObjcCategoryImplDecl +/// object. +Sema::DeclTy *Sema::ObjcStartCategoryImplementation(Scope* S, + SourceLocation AtCatImplLoc, + IdentifierInfo *ClassName, SourceLocation ClassLoc, + IdentifierInfo *CatName, SourceLocation CatLoc) { + ObjcInterfaceDecl *IDecl = getObjCInterfaceDecl(S, ClassName, ClassLoc); + ObjcCategoryImplDecl *CDecl = new ObjcCategoryImplDecl(AtCatImplLoc, + ClassName, IDecl, + CatName); + /// Check that class of this category is already completely declared. + if (!IDecl || IDecl->getIsForwardDecl()) + Diag(ClassLoc, diag::err_undef_interface, ClassName->getName()); + /// TODO: Check that CatName, category name, is not used in another + // implementation. + return CDecl; +} + Sema::DeclTy *Sema::ObjcStartClassImplementation(Scope *S, SourceLocation AtClassImplLoc, IdentifierInfo *ClassName, SourceLocation ClassLoc, @@ -1255,7 +1274,51 @@ void Sema::ImplMethodsVsClassMethods(ObjcImplementationDecl* IMPDecl, ObjcProtocolDecl* PDecl = protocols[i]; CheckProtocolMethodDefs(PDecl, InsMap, ClsMap); } - return; +} + +/// ImplCategoryMethodsVsIntfMethods - Checks that methods declared in the +/// category interface is implemented in the category @implementation. +void Sema::ImplCategoryMethodsVsIntfMethods(ObjcCategoryImplDecl *CatImplDecl, + ObjcCategoryDecl *CatClassDecl) { + llvm::DenseMap InsMap; + // Check and see if instance methods in category interface have been + // implemented in its implementation class. + ObjcMethodDecl **methods = CatImplDecl->getCatInsMethods(); + for (int i=0; i < CatImplDecl->getNumCatInsMethods(); i++) { + InsMap[methods[i]->getSelector().getAsOpaquePtr()] = 'a'; + } + + methods = CatClassDecl->getCatInsMethods(); + for (int j = 0; j < CatClassDecl->getNumCatInsMethods(); j++) + if (!InsMap.count(methods[j]->getSelector().getAsOpaquePtr())) { + llvm::SmallString<128> buf; + Diag(methods[j]->getLocation(), diag::warn_undef_method_impl, + methods[j]->getSelector().getName(buf)); + } + llvm::DenseMap ClsMap; + // Check and see if class methods in category interface have been + // implemented in its implementation class. + methods = CatImplDecl->getCatClsMethods(); + for (int i=0; i < CatImplDecl->getNumCatClsMethods(); i++) { + ClsMap[methods[i]->getSelector().getAsOpaquePtr()] = 'a'; + } + + methods = CatClassDecl->getCatClsMethods(); + for (int j = 0; j < CatClassDecl->getNumCatClsMethods(); j++) + if (!ClsMap.count(methods[j]->getSelector().getAsOpaquePtr())) { + llvm::SmallString<128> buf; + Diag(methods[j]->getLocation(), diag::warn_undef_method_impl, + methods[j]->getSelector().getName(buf)); + } + + // Check the protocol list for unimplemented methods in the @implementation + // class. + ObjcProtocolDecl** protocols = CatClassDecl->getCatReferencedProtocols(); + for (int i = 0; i < CatClassDecl->getNumCatReferencedProtocols(); i++) { + ObjcProtocolDecl* PDecl = protocols[i]; + CheckProtocolMethodDefs(PDecl, InsMap, ClsMap); + } + } /// ObjcClassDeclaration - @@ -1647,6 +1710,24 @@ void Sema::ObjcAddMethodsToClass(Scope* S, DeclTy *ClassDecl, if (IDecl) ImplMethodsVsClassMethods(ImplClass, IDecl); } + else if (isa(static_cast(ClassDecl))) { + ObjcCategoryImplDecl* CatImplClass = cast( + static_cast(ClassDecl)); + CatImplClass->ObjcAddCatImplMethods(&insMethods[0], insMethods.size(), + &clsMethods[0], clsMethods.size()); + ObjcInterfaceDecl* IDecl = CatImplClass->getClassInterface(); + // Find category interface decl and then check that all methods declared + // in this interface is implemented in the category @implementation. + if (IDecl) { + for (ObjcCategoryDecl *Categories = IDecl->getListCategories(); + Categories; Categories = Categories->getNextClassCategory()) { + if (Categories->getCatName() == CatImplClass->getObjcCatName()) { + ImplCategoryMethodsVsIntfMethods(CatImplClass, Categories); + break; + } + } + } + } else assert(0 && "Sema::ObjcAddMethodsToClass(): Unknown DeclTy"); return; diff --git a/clang.xcodeproj/project.pbxproj b/clang.xcodeproj/project.pbxproj index 314ed3ea3c..6389e5fbc4 100644 --- a/clang.xcodeproj/project.pbxproj +++ b/clang.xcodeproj/project.pbxproj @@ -737,6 +737,7 @@ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "clang" */; + compatibilityVersion = "Xcode 2.4"; hasScannedForEncodings = 1; mainGroup = 08FB7794FE84155DC02AAC07 /* clang */; projectDirPath = ""; diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 5102a99d25..0b0a8580cc 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -35,7 +35,7 @@ public: Function, BlockVariable, FileVariable, ParmVariable, EnumConstant, // Concrete sub-classes of TypeDecl Typedef, Struct, Union, Class, Enum, ObjcInterface, ObjcClass, ObjcMethod, - ObjcProtocol, ObjcForwardProtocol, ObjcCategory, + ObjcProtocol, ObjcForwardProtocol, ObjcCategory, ObjcCategoryImpl, ObjcImplementation, // Concrete sub-class of Decl Field, ObjcIvar diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 9deb1dc533..c85a2f77a8 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -448,6 +448,17 @@ public: CatReferencedProtocols[idx] = OID; } + ObjcProtocolDecl **getCatReferencedProtocols() const { + return CatReferencedProtocols; + } + int getNumCatReferencedProtocols() const { return NumCatReferencedProtocols; } + + ObjcMethodDecl **getCatInsMethods() const { return CatInsMethods; } + int getNumCatInsMethods() const { return NumCatInsMethods; } + + ObjcMethodDecl **getCatClsMethods() const { return CatClsMethods; } + int getNumCatClsMethods() const { return NumCatClsMethods; } + void ObjcAddCatMethods(ObjcMethodDecl **insMethods, unsigned numInsMembers, ObjcMethodDecl **clsMethods, unsigned numClsMembers); @@ -466,6 +477,56 @@ public: static bool classof(const ObjcCategoryDecl *D) { return true; } }; +/// ObjcCategoryImplDecl - An object of this class encapsulates a category +/// @implementation declaration. +class ObjcCategoryImplDecl : public Decl { + /// Class interface for this category implementation + ObjcInterfaceDecl *ClassInterface; + + /// Category name + IdentifierInfo *ObjcCatName; + + /// category instance methods being implemented + ObjcMethodDecl **CatInsMethods; // Null if category is not implementing any + int NumCatInsMethods; // -1 if category is not implementing any + + /// category class methods being implemented + ObjcMethodDecl **CatClsMethods; // Null if category is not implementing any + int NumCatClsMethods; // -1 if category is not implementing any + + public: + ObjcCategoryImplDecl(SourceLocation L, IdentifierInfo *Id, + ObjcInterfaceDecl *classInterface, + IdentifierInfo *catName) + : Decl(ObjcCategoryImpl), + ClassInterface(classInterface), + ObjcCatName(catName), + CatInsMethods(0), NumCatInsMethods(-1), + CatClsMethods(0), NumCatClsMethods(-1) {} + + ObjcInterfaceDecl *getClassInterface() const { + return ClassInterface; + } + + IdentifierInfo *getObjcCatName() const { return ObjcCatName; } + + ObjcMethodDecl **getCatInsMethods() const { return CatInsMethods; } + int getNumCatInsMethods() const { return NumCatInsMethods; } + + ObjcMethodDecl **getCatClsMethods() const { return CatClsMethods; } + int getNumCatClsMethods() const { return NumCatClsMethods; } + + + void ObjcAddCatImplMethods( + ObjcMethodDecl **insMethods, unsigned numInsMembers, + ObjcMethodDecl **clsMethods, unsigned numClsMembers); + + static bool classof(const Decl *D) { + return D->getKind() == ObjcCategoryImpl; + } + static bool classof(const ObjcCategoryImplDecl *D) { return true; } +}; + /// ObjcImplementationDecl - Represents a class definition - this is where /// method definitions are specified. For example: /// diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 34561f7a2e..0376860160 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -472,6 +472,14 @@ public: SourceLocation SuperClassLoc) { return 0; } + virtual DeclTy *ObjcStartCategoryImplementation(Scope* S, + SourceLocation AtCatImplLoc, + IdentifierInfo *ClassName, + SourceLocation ClassLoc, + IdentifierInfo *CatName, + SourceLocation CatLoc) { + return 0; + } virtual DeclTy *ObjcBuildMethodDeclaration(SourceLocation MethodLoc, tok::TokenKind MethodType, TypeTy *ReturnType, Selector Sel, // optional arguments. The number of types/arguments is obtained diff --git a/test/Sema/method-undef-category-warn-1.m b/test/Sema/method-undef-category-warn-1.m new file mode 100644 index 0000000000..e71cfdb47c --- /dev/null +++ b/test/Sema/method-undef-category-warn-1.m @@ -0,0 +1,26 @@ +@interface MyClass1 +@end + +@protocol P +- (void) Pmeth; // expected-warning {{method definition for 'Pmeth' not found}} +- (void) Pmeth1; // expected-warning {{method definition for 'Pmeth1' not found}} +@end + +@interface MyClass1(CAT)

+- (void) meth2; // expected-warning {{method definition for 'meth2' not found}} +@end + +@implementation MyClass1(CAT) +- (void) Pmeth1{} +@end + +@interface MyClass1(DOG)

+- (void)ppp; // expected-warning {{method definition for 'ppp' not found}} +@end + +@implementation MyClass1(DOG) +- (void) Pmeth {} +@end + +@implementation MyClass1(CAT1) +@end