From: Fariborz Jahanian Date: Thu, 27 Sep 2007 18:57:03 +0000 (+0000) Subject: Patch for method implementation. It populates ObjcImplementationDecl object with... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d0b015461a819913efa1161c56a8b897d7e8cdb2;p=clang Patch for method implementation. It populates ObjcImplementationDecl object with method implementation declarations . It checks and warns on those methods declared in class interface and not implemented. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@42412 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/AST/Decl.cpp b/AST/Decl.cpp index 83762e9465..10da7b163e 100644 --- a/AST/Decl.cpp +++ b/AST/Decl.cpp @@ -364,4 +364,23 @@ void ObjcCategoryDecl::ObjcAddCatMethods(ObjcMethodDecl **insMethods, } } +/// ObjcAddImplMethods - Insert instance and methods declarations into +/// ObjcImplementationDecl's InsMethods and ClsMethods fields. +/// +void ObjcImplementationDecl::ObjcAddImplMethods(ObjcMethodDecl **insMethods, + unsigned numInsMembers, + ObjcMethodDecl **clsMethods, + unsigned numClsMembers) { + NumInsMethods = numInsMembers; + if (numInsMembers) { + InsMethods = new ObjcMethodDecl*[numInsMembers]; + memcpy(InsMethods, insMethods, numInsMembers*sizeof(ObjcMethodDecl*)); + } + NumClsMethods = numClsMembers; + if (numClsMembers) { + ClsMethods = new ObjcMethodDecl*[numClsMembers]; + memcpy(ClsMethods, clsMethods, numClsMembers*sizeof(ObjcMethodDecl*)); + } +} + diff --git a/Parse/ParseObjc.cpp b/Parse/ParseObjc.cpp index 9da15fafd2..d8361b07df 100644 --- a/Parse/ParseObjc.cpp +++ b/Parse/ParseObjc.cpp @@ -882,12 +882,22 @@ Parser::DeclTy *Parser::ParseObjCAtImplementationDeclaration( if (Tok.getKind() == tok::l_brace) ParseObjCClassInstanceVariables(ImplClsType/*FIXME*/); // we have ivars - return 0; + return ImplClsType; } Parser::DeclTy *Parser::ParseObjCAtEndDeclaration(SourceLocation atLoc) { assert(Tok.isObjCAtKeyword(tok::objc_end) && "ParseObjCAtEndDeclaration(): Expected @end"); ConsumeToken(); // the "end" identifier + if (ObjcImpDecl) { + // Checking is not necessary except that a parse error might have caused + // @implementation not to have been parsed to completion and ObjcImpDecl + // could be 0. + /// Insert collected methods declarations into the @interface object. + Actions.ObjcAddMethodsToClass(ObjcImpDecl, + &AllImplMethods[0],AllImplMethods.size()); + ObjcImpDecl = 0; + AllImplMethods.clear(); + } return 0; } @@ -1056,7 +1066,7 @@ void Parser::ParseObjCInstanceMethodDefinition() { assert(Tok.getKind() == tok::minus && "ParseObjCInstanceMethodDefinition(): Expected '-'"); // FIXME: @optional/@protocol?? - ParseObjCMethodPrototype(ObjcImpDecl); + AllImplMethods.push_back(ParseObjCMethodPrototype(ObjcImpDecl)); // parse optional ';' if (Tok.getKind() == tok::semi) ConsumeToken(); @@ -1075,7 +1085,7 @@ void Parser::ParseObjCClassMethodDefinition() { assert(Tok.getKind() == tok::plus && "ParseObjCClassMethodDefinition(): Expected '+'"); // FIXME: @optional/@protocol?? - ParseObjCMethodPrototype(ObjcImpDecl); + AllImplMethods.push_back(ParseObjCMethodPrototype(ObjcImpDecl)); // parse optional ';' if (Tok.getKind() == tok::semi) ConsumeToken(); diff --git a/Parse/Parser.cpp b/Parse/Parser.cpp index b9bcc929a5..362ca35512 100644 --- a/Parse/Parser.cpp +++ b/Parse/Parser.cpp @@ -23,6 +23,7 @@ Parser::Parser(Preprocessor &pp, Action &actions) NumCachedScopes = 0; ParenCount = BracketCount = BraceCount = 0; ObjcImpDecl = 0; + AllImplMethods.clear(); } /// Out-of-line virtual destructor to provide home for Action class. diff --git a/Sema/Sema.h b/Sema/Sema.h index a486d4fa73..45d1a2dc33 100644 --- a/Sema/Sema.h +++ b/Sema/Sema.h @@ -390,6 +390,8 @@ public: virtual void ActOnImpleIvarVsClassIvars(DeclTy *ClassDecl, DeclTy **Fields, unsigned NumFields); + virtual void ActOnImplMethodsVsClassMethods(DeclTy *ImplClass, DeclTy *Class); + virtual DeclTy *ObjcBuildMethodDeclaration(SourceLocation MethodLoc, tok::TokenKind MethodType, TypeTy *ReturnType, SelectorInfo *Sel, // optional arguments. The number of types/arguments is obtained diff --git a/Sema/SemaDecl.cpp b/Sema/SemaDecl.cpp index dd8fe22cc9..381003f86a 100644 --- a/Sema/SemaDecl.cpp +++ b/Sema/SemaDecl.cpp @@ -1174,6 +1174,49 @@ void Sema::ActOnImpleIvarVsClassIvars(DeclTy *ClassDecl, } +void Sema::ActOnImplMethodsVsClassMethods(DeclTy* ImplClassDecl, + DeclTy* ClassDecl) { + ObjcImplementationDecl* IMPDecl = + cast(static_cast(ImplClassDecl)); + assert(IMPDecl && "missing implmentation class decl"); + + ObjcInterfaceDecl* IDecl = + cast(static_cast(ClassDecl)); + assert(IDecl && "missing interface class decl"); + + llvm::DenseMap Map; + // Check and see if instance methods in class interface have been + // implemented in the implementation class. + ObjcMethodDecl **methods = IMPDecl->getInsMethods(); + for (int i=0; i < IMPDecl->getNumInsMethods(); i++) { + Map[methods[i]->getSelector()] = 'a'; + } + + methods = IDecl->getInsMethods(); + for (int j = 0; j < IDecl->getNumInsMethods(); j++) + if (!Map.count(methods[j]->getSelector())) { + llvm::SmallString<128> buf; + Diag(methods[j]->getLocation(), diag::warn_undef_method_impl, + methods[j]->getSelector()->getName(buf)); + } + Map.clear(); + // Check and see if class methods in class interface have been + // implemented in the implementation class. + methods = IMPDecl->getClsMethods(); + for (int i=0; i < IMPDecl->getNumClsMethods(); i++) { + Map[methods[i]->getSelector()] = 'a'; + } + + methods = IDecl->getClsMethods(); + for (int j = 0; j < IDecl->getNumClsMethods(); j++) + if (!Map.count(methods[j]->getSelector())) { + llvm::SmallString<128> buf; + Diag(methods[j]->getLocation(), diag::warn_undef_method_impl, + methods[j]->getSelector()->getName(buf)); + } + return; +} + /// ObjcClassDeclaration - /// Scope will always be top level file scope. Action::DeclTy * @@ -1560,7 +1603,17 @@ void Sema::ObjcAddMethodsToClass(DeclTy *ClassDecl, static_cast(ClassDecl)); Category->ObjcAddCatMethods(&insMethods[0], insMethods.size(), &clsMethods[0], clsMethods.size()); - } + } + else if (isa(static_cast(ClassDecl))) { + ObjcImplementationDecl* ImplClass = cast( + static_cast(ClassDecl)); + ImplClass->ObjcAddImplMethods(&insMethods[0], insMethods.size(), + &clsMethods[0], clsMethods.size()); + ObjcInterfaceDecl* IDecl = + Context.getObjCInterfaceDecl(ImplClass->getIdentifier()); + if (IDecl) + ActOnImplMethodsVsClassMethods(ImplClass, IDecl); + } else assert(0 && "Sema::ObjcAddMethodsToClass(): Unknown DeclTy"); return; diff --git a/clang.xcodeproj/project.pbxproj b/clang.xcodeproj/project.pbxproj index 04dad73923..fca0464b47 100644 --- a/clang.xcodeproj/project.pbxproj +++ b/clang.xcodeproj/project.pbxproj @@ -733,6 +733,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 ea8121326e..b3c30e5334 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -591,6 +591,12 @@ public: ObjcIvarDecl **getIntfDeclIvars() const { return Ivars; } int getIntfDeclNumIvars() const { return NumIvars; } + ObjcMethodDecl** getInsMethods() const { return InsMethods; } + int getNumInsMethods() const { return NumInsMethods; } + + ObjcMethodDecl** getClsMethods() const { return ClsMethods; } + int getNumClsMethods() const { return NumClsMethods; } + void ObjcAddInstanceVariablesToClass(ObjcIvarDecl **ivars, unsigned numIvars); @@ -677,6 +683,9 @@ private: /// List of attributes for this method declaration. AttributeList *MethodAttrs; + + /// Loc - location of this declaration. + SourceLocation Loc; /// instance (true) or class (false) method. bool IsInstance : 1; @@ -690,7 +699,7 @@ public: Decl *PrevDecl = 0) : Decl(ObjcMethod), Selector(SelInfo), MethodDeclType(T), ParamInfo(paramInfo), NumMethodParams(numParams), - MethodAttrs(M), IsInstance(isInstance) {} + MethodAttrs(M), Loc(L), IsInstance(isInstance) {} #if 0 ObjcMethodDecl(Kind DK, SourceLocation L, IdentifierInfo &SelId, QualType T, ParmVarDecl **paramInfo = 0, int numParams=-1, @@ -701,6 +710,7 @@ public: MethodAttrs(M), IsInstance(isInstance) {} #endif virtual ~ObjcMethodDecl(); + SelectorInfo *getSelector() const { return Selector; } QualType getMethodType() const { return MethodDeclType; } unsigned getNumMethodParams() const { return NumMethodParams; } ParmVarDecl *getMethodParamDecl(unsigned i) { @@ -710,6 +720,7 @@ public: void setMethodParams(ParmVarDecl **NewParamInfo, unsigned NumParams); AttributeList *getMethodAttrs() const {return MethodAttrs;} + SourceLocation getLocation() const { return Loc; } bool isInstance() const { return IsInstance; } // Related to protocols declared in @protocol void setDeclImplementation(ImplementationControl ic) @@ -890,13 +901,19 @@ class ObjcImplementationDecl : public TypeDecl { void ObjcAddInstanceVariablesToClassImpl(ObjcIvarDecl **ivars, unsigned numIvars); - void ObjcAddMethods(ObjcMethodDecl **insMethods, unsigned numInsMembers, - ObjcMethodDecl **clsMethods, unsigned numClsMembers); + void ObjcAddImplMethods(ObjcMethodDecl **insMethods, unsigned numInsMembers, + ObjcMethodDecl **clsMethods, unsigned numClsMembers); ObjcInterfaceDecl *getImplSuperClass() const { return SuperClass; } void setImplSuperClass(ObjcInterfaceDecl * superCls) { SuperClass = superCls; } + + ObjcMethodDecl **getInsMethods() const { return InsMethods; } + int getNumInsMethods() const { return NumInsMethods; } + + ObjcMethodDecl **getClsMethods() const { return ClsMethods; } + int getNumClsMethods() const { return NumClsMethods; } static bool classof(const Decl *D) { return D->getKind() == ObjcImplementation; diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index c394663dd9..937684c7f6 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -432,7 +432,8 @@ DIAG(err_inconsistant_ivar, ERROR, "inconsistent instance variable specification") DIAG(err_conflicting_ivar_type, ERROR, "conflicting instance variable type") - +DIAG(warn_undef_method_impl, WARNING, + "method definition for '%0' not found") //===----------------------------------------------------------------------===// // Semantic Analysis diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 3e9a687ef4..48ae4bea84 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -451,6 +451,10 @@ public: DeclTy **Fields, unsigned NumFields) { return; } + virtual void ActOnImplMethodsVsClassMethods(DeclTy *ImplClassDecl, + DeclTy *ClassDecl) { + return; + } virtual DeclTy *ObjcStartProtoInterface(Scope* S, SourceLocation AtProtoInterfaceLoc, IdentifierInfo *ProtocolName, SourceLocation ProtocolLoc, diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 243b88f9ff..3982028ff2 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -265,6 +265,8 @@ private: DeclTy *ParseObjCAtProtocolDeclaration(SourceLocation atLoc); DeclTy *ObjcImpDecl; + /// Vector is used to collect method decls for each @implementation + llvm::SmallVector AllImplMethods; DeclTy *ParseObjCAtImplementationDeclaration(SourceLocation atLoc); DeclTy *ParseObjCAtEndDeclaration(SourceLocation atLoc); DeclTy *ParseObjCAtAliasDeclaration(SourceLocation atLoc); diff --git a/test/Sema/method-undefined-warn-1.m b/test/Sema/method-undefined-warn-1.m new file mode 100644 index 0000000000..2c7cdadcf0 --- /dev/null +++ b/test/Sema/method-undefined-warn-1.m @@ -0,0 +1,42 @@ +@interface INTF +- (void) meth; +- (void) meth : (int) arg1; +- (int) int_meth; // expected-warning {{method definition for 'int_meth' not found}} ++ (int) cls_meth; // expected-warning {{method definition for 'cls_meth' not found}} ++ (void) cls_meth1 : (int) arg1; // expected-warning {{method definition for 'cls_meth1:' not found}} +@end + +@implementation INTF +- (void) meth {} +- (void) meth : (int) arg2{} +- (void) cls_meth1 : (int) arg2{} +@end + + +@interface INTF1 +- (void) meth; +- (void) meth : (int) arg1; +- (int) int_meth; // expected-warning {{method definition for 'int_meth' not found}} ++ (int) cls_meth; // expected-warning {{method definition for 'cls_meth' not found}} ++ (void) cls_meth1 : (int) arg1; // expected-warning {{method definition for 'cls_meth1:' not found}} +@end + +@implementation INTF1 +- (void) meth {} +- (void) meth : (int) arg2{} +- (void) cls_meth1 : (int) arg2{} +@end + + +@interface INTF2 +- (void) meth; +- (void) meth : (int) arg1; +- (void) cls_meth1 : (int) arg1; +@end + +@implementation INTF2 +- (void) meth {} +- (void) meth : (int) arg2{} +- (void) cls_meth1 : (int) arg2{} +@end +