//===--- SemaDeclObjC.cpp - Semantic Analysis for ObjC Declarations -------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements semantic analysis for Objective C declarations. // //===----------------------------------------------------------------------===// #include "Sema.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/Parse/Scope.h" using namespace clang; /// ObjCActOnStartOfMethodDef - This routine sets up parameters; invisible /// and user declared, in the method definition's AST. void Sema::ObjCActOnStartOfMethodDef(Scope *FnBodyScope, DeclTy *D) { assert(CurFunctionDecl == 0 && "Method parsing confused"); ObjCMethodDecl *MDecl = dyn_cast(static_cast(D)); assert(MDecl != 0 && "Not a method declarator!"); // Allow the rest of sema to find private method decl implementations. if (MDecl->isInstance()) AddInstanceMethodToGlobalPool(MDecl); else AddFactoryMethodToGlobalPool(MDecl); // Allow all of Sema to see that we are entering a method definition. CurMethodDecl = MDecl; // Create Decl objects for each parameter, entrring them in the scope for // binding to their use. struct DeclaratorChunk::ParamInfo PI; // Insert the invisible arguments, self and _cmd! PI.Ident = &Context.Idents.get("self"); PI.IdentLoc = SourceLocation(); // synthesized vars have a null location. PI.InvalidType = false; PI.AttrList = 0; PI.TypeInfo = Context.getObjCIdType().getAsOpaquePtr(); if (MDecl->isInstance()) { if (ObjCInterfaceDecl *OID = MDecl->getClassInterface()) { // There may be no interface context due to error in declaration of the // interface (which has been reported). Recover gracefully QualType selfTy = Context.getObjCInterfaceType(OID); selfTy = Context.getPointerType(selfTy); PI.TypeInfo = selfTy.getAsOpaquePtr(); } } CurMethodDecl->setSelfDecl(ActOnParamDeclarator(PI, FnBodyScope)); PI.Ident = &Context.Idents.get("_cmd"); PI.TypeInfo = Context.getObjCSelType().getAsOpaquePtr(); ActOnParamDeclarator(PI, FnBodyScope); for (unsigned i = 0, e = MDecl->getNumParams(); i != e; ++i) { ParmVarDecl *PDecl = MDecl->getParamDecl(i); PI.Ident = PDecl->getIdentifier(); PI.IdentLoc = PDecl->getLocation(); // user vars have a real location. PI.TypeInfo = PDecl->getType().getAsOpaquePtr(); MDecl->setParamDecl(i, ActOnParamDeclarator(PI, FnBodyScope)); } } Sema::DeclTy *Sema::ActOnStartClassInterface( SourceLocation AtInterfaceLoc, IdentifierInfo *ClassName, SourceLocation ClassLoc, IdentifierInfo *SuperName, SourceLocation SuperLoc, IdentifierInfo **ProtocolNames, unsigned NumProtocols, SourceLocation EndProtoLoc, AttributeList *AttrList) { assert(ClassName && "Missing class identifier"); // Check for another declaration kind with the same name. ScopedDecl *PrevDecl = LookupInterfaceDecl(ClassName); if (PrevDecl && !isa(PrevDecl)) { Diag(ClassLoc, diag::err_redefinition_different_kind, ClassName->getName()); Diag(PrevDecl->getLocation(), diag::err_previous_definition); } ObjCInterfaceDecl* IDecl = dyn_cast_or_null(PrevDecl); if (IDecl) { // Class already seen. Is it a forward declaration? if (!IDecl->isForwardDecl()) Diag(AtInterfaceLoc, diag::err_duplicate_class_def, IDecl->getName()); else { IDecl->setLocation(AtInterfaceLoc); IDecl->setForwardDecl(false); IDecl->AllocIntfRefProtocols(NumProtocols); } } else { IDecl = ObjCInterfaceDecl::Create(Context, AtInterfaceLoc, NumProtocols, ClassName); // Chain & install the interface decl into the identifier. IDecl->setNext(ClassName->getFETokenInfo()); ClassName->setFETokenInfo(IDecl); // Remember that this needs to be removed when the scope is popped. TUScope->AddDecl(IDecl); } if (SuperName) { ObjCInterfaceDecl* SuperClassEntry = 0; // Check if a different kind of symbol declared in this scope. PrevDecl = LookupInterfaceDecl(SuperName); if (PrevDecl && !isa(PrevDecl)) { Diag(SuperLoc, diag::err_redefinition_different_kind, SuperName->getName()); Diag(PrevDecl->getLocation(), diag::err_previous_definition); } else { // Check that super class is previously defined SuperClassEntry = dyn_cast_or_null(PrevDecl); if (!SuperClassEntry || SuperClassEntry->isForwardDecl()) { Diag(AtInterfaceLoc, diag::err_undef_superclass, SuperClassEntry ? SuperClassEntry->getName() : SuperName->getName(), ClassName->getName()); } } IDecl->setSuperClass(SuperClassEntry); IDecl->setLocEnd(SuperLoc); } else { // we have a root class. IDecl->setLocEnd(ClassLoc); } /// Check then save referenced protocols if (NumProtocols) { for (unsigned int i = 0; i != NumProtocols; i++) { ObjCProtocolDecl* RefPDecl = ObjCProtocols[ProtocolNames[i]]; if (!RefPDecl || RefPDecl->isForwardDecl()) Diag(ClassLoc, diag::warn_undef_protocolref, ProtocolNames[i]->getName(), ClassName->getName()); IDecl->setIntfRefProtocols(i, RefPDecl); } IDecl->setLocEnd(EndProtoLoc); } return IDecl; } /// ActOnCompatiblityAlias - this action is called after complete parsing of /// @compaatibility_alias declaration. It sets up the alias relationships. Sema::DeclTy *Sema::ActOnCompatiblityAlias( SourceLocation AtCompatibilityAliasLoc, IdentifierInfo *AliasName, SourceLocation AliasLocation, IdentifierInfo *ClassName, SourceLocation ClassLocation) { // Look for previous declaration of alias name ScopedDecl *ADecl = LookupScopedDecl(AliasName, Decl::IDNS_Ordinary, AliasLocation, TUScope); if (ADecl) { if (isa(ADecl)) { Diag(AliasLocation, diag::warn_previous_alias_decl); Diag(ADecl->getLocation(), diag::warn_previous_declaration); } else { Diag(AliasLocation, diag::err_conflicting_aliasing_type, AliasName->getName()); Diag(ADecl->getLocation(), diag::err_previous_declaration); } return 0; } // Check for class declaration ScopedDecl *CDeclU = LookupScopedDecl(ClassName, Decl::IDNS_Ordinary, ClassLocation, TUScope); ObjCInterfaceDecl *CDecl = dyn_cast_or_null(CDeclU); if (CDecl == 0) { Diag(ClassLocation, diag::warn_undef_interface, ClassName->getName()); if (CDeclU) Diag(CDeclU->getLocation(), diag::warn_previous_declaration); return 0; } // Everything checked out, instantiate a new alias declaration AST. ObjCCompatibleAliasDecl *AliasDecl = ObjCCompatibleAliasDecl::Create(Context, AtCompatibilityAliasLoc, AliasName, CDecl); // Chain & install the interface decl into the identifier. AliasDecl->setNext(AliasName->getFETokenInfo()); AliasName->setFETokenInfo(AliasDecl); return AliasDecl; } Sema::DeclTy *Sema::ActOnStartProtocolInterface( SourceLocation AtProtoInterfaceLoc, IdentifierInfo *ProtocolName, SourceLocation ProtocolLoc, IdentifierInfo **ProtoRefNames, unsigned NumProtoRefs, SourceLocation EndProtoLoc) { assert(ProtocolName && "Missing protocol identifier"); ObjCProtocolDecl *PDecl = ObjCProtocols[ProtocolName]; if (PDecl) { // Protocol already seen. Better be a forward protocol declaration if (!PDecl->isForwardDecl()) { Diag(ProtocolLoc, diag::err_duplicate_protocol_def, ProtocolName->getName()); // Just return the protocol we already had. // FIXME: don't leak the objects passed in! return PDecl; } PDecl->setForwardDecl(false); PDecl->AllocReferencedProtocols(NumProtoRefs); } else { PDecl = ObjCProtocolDecl::Create(Context, AtProtoInterfaceLoc, NumProtoRefs, ProtocolName); PDecl->setForwardDecl(false); ObjCProtocols[ProtocolName] = PDecl; } if (NumProtoRefs) { /// Check then save referenced protocols. for (unsigned int i = 0; i != NumProtoRefs; i++) { ObjCProtocolDecl* RefPDecl = ObjCProtocols[ProtoRefNames[i]]; if (!RefPDecl || RefPDecl->isForwardDecl()) Diag(ProtocolLoc, diag::warn_undef_protocolref, ProtoRefNames[i]->getName(), ProtocolName->getName()); PDecl->setReferencedProtocols(i, RefPDecl); } PDecl->setLocEnd(EndProtoLoc); } return PDecl; } /// FindProtocolDeclaration - This routine looks up protocols and /// issuer error if they are not declared. It returns list of protocol /// declarations in its 'Protocols' argument. void Sema::FindProtocolDeclaration(SourceLocation TypeLoc, IdentifierInfo **ProtocolId, unsigned NumProtocols, llvm::SmallVector &Protocols) { for (unsigned i = 0; i != NumProtocols; ++i) { ObjCProtocolDecl *PDecl = ObjCProtocols[ProtocolId[i]]; if (!PDecl) Diag(TypeLoc, diag::err_undeclared_protocol, ProtocolId[i]->getName()); else Protocols.push_back(PDecl); } } /// ActOnForwardProtocolDeclaration - Action::DeclTy * Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc, IdentifierInfo **IdentList, unsigned NumElts) { llvm::SmallVector Protocols; for (unsigned i = 0; i != NumElts; ++i) { IdentifierInfo *Ident = IdentList[i]; ObjCProtocolDecl *&PDecl = ObjCProtocols[Ident]; if (PDecl == 0) { // Not already seen? // FIXME: Pass in the location of the identifier! PDecl = ObjCProtocolDecl::Create(Context, AtProtocolLoc, 0, Ident); } Protocols.push_back(PDecl); } return ObjCForwardProtocolDecl::Create(Context, AtProtocolLoc, &Protocols[0], Protocols.size()); } Sema::DeclTy *Sema::ActOnStartCategoryInterface( SourceLocation AtInterfaceLoc, IdentifierInfo *ClassName, SourceLocation ClassLoc, IdentifierInfo *CategoryName, SourceLocation CategoryLoc, IdentifierInfo **ProtoRefNames, unsigned NumProtoRefs, SourceLocation EndProtoLoc) { ObjCInterfaceDecl *IDecl = getObjCInterfaceDecl(ClassName); ObjCCategoryDecl *CDecl = ObjCCategoryDecl::Create(Context, AtInterfaceLoc, CategoryName); CDecl->setClassInterface(IDecl); /// Check that class of this category is already completely declared. if (!IDecl || IDecl->isForwardDecl()) Diag(ClassLoc, diag::err_undef_interface, ClassName->getName()); else { /// Check for duplicate interface declaration for this category ObjCCategoryDecl *CDeclChain; for (CDeclChain = IDecl->getCategoryList(); CDeclChain; CDeclChain = CDeclChain->getNextClassCategory()) { if (CDeclChain->getIdentifier() == CategoryName) { Diag(CategoryLoc, diag::err_dup_category_def, ClassName->getName(), CategoryName->getName()); break; } } if (!CDeclChain) CDecl->insertNextClassCategory(); } if (NumProtoRefs) { llvm::SmallVector RefProtocols; /// Check and then save the referenced protocols. for (unsigned int i = 0; i != NumProtoRefs; i++) { ObjCProtocolDecl* RefPDecl = ObjCProtocols[ProtoRefNames[i]]; if (!RefPDecl || RefPDecl->isForwardDecl()) { Diag(CategoryLoc, diag::warn_undef_protocolref, ProtoRefNames[i]->getName(), CategoryName->getName()); } if (RefPDecl) RefProtocols.push_back(RefPDecl); } if (!RefProtocols.empty()) CDecl->setReferencedProtocolList(&RefProtocols[0], RefProtocols.size()); } CDecl->setLocEnd(EndProtoLoc); return CDecl; } /// ActOnStartCategoryImplementation - Perform semantic checks on the /// category implementation declaration and build an ObjCCategoryImplDecl /// object. Sema::DeclTy *Sema::ActOnStartCategoryImplementation( SourceLocation AtCatImplLoc, IdentifierInfo *ClassName, SourceLocation ClassLoc, IdentifierInfo *CatName, SourceLocation CatLoc) { ObjCInterfaceDecl *IDecl = getObjCInterfaceDecl(ClassName); ObjCCategoryImplDecl *CDecl = ObjCCategoryImplDecl::Create(Context, AtCatImplLoc, CatName, IDecl); /// Check that class of this category is already completely declared. if (!IDecl || IDecl->isForwardDecl()) 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::ActOnStartClassImplementation( SourceLocation AtClassImplLoc, IdentifierInfo *ClassName, SourceLocation ClassLoc, IdentifierInfo *SuperClassname, SourceLocation SuperClassLoc) { ObjCInterfaceDecl* IDecl = 0; // Check for another declaration kind with the same name. ScopedDecl *PrevDecl = LookupInterfaceDecl(ClassName); if (PrevDecl && !isa(PrevDecl)) { Diag(ClassLoc, diag::err_redefinition_different_kind, ClassName->getName()); Diag(PrevDecl->getLocation(), diag::err_previous_definition); } else { // Is there an interface declaration of this class; if not, warn! IDecl = dyn_cast_or_null(PrevDecl); if (!IDecl) Diag(ClassLoc, diag::warn_undef_interface, ClassName->getName()); } // Check that super class name is valid class name ObjCInterfaceDecl* SDecl = 0; if (SuperClassname) { // Check if a different kind of symbol declared in this scope. PrevDecl = LookupInterfaceDecl(SuperClassname); if (PrevDecl && !isa(PrevDecl)) { Diag(SuperClassLoc, diag::err_redefinition_different_kind, SuperClassname->getName()); Diag(PrevDecl->getLocation(), diag::err_previous_definition); } else { SDecl = dyn_cast_or_null(PrevDecl); if (!SDecl) Diag(SuperClassLoc, diag::err_undef_superclass, SuperClassname->getName(), ClassName->getName()); else if (IDecl && IDecl->getSuperClass() != SDecl) { // This implementation and its interface do not have the same // super class. Diag(SuperClassLoc, diag::err_conflicting_super_class, SDecl->getName()); Diag(SDecl->getLocation(), diag::err_previous_definition); } } } if (!IDecl) { // Legacy case of @implementation with no corresponding @interface. // Build, chain & install the interface decl into the identifier. IDecl = ObjCInterfaceDecl::Create(Context, AtClassImplLoc, 0, ClassName, false, true); IDecl->setNext(ClassName->getFETokenInfo()); ClassName->setFETokenInfo(IDecl); IDecl->setSuperClass(SDecl); IDecl->setLocEnd(ClassLoc); // Remember that this needs to be removed when the scope is popped. TUScope->AddDecl(IDecl); } ObjCImplementationDecl* IMPDecl = ObjCImplementationDecl::Create(Context, AtClassImplLoc, ClassName, IDecl, SDecl); // Check that there is no duplicate implementation of this class. if (ObjCImplementations[ClassName]) // FIXME: Don't leak everything! Diag(ClassLoc, diag::err_dup_implementation_class, ClassName->getName()); else // add it to the list. ObjCImplementations[ClassName] = IMPDecl; return IMPDecl; } void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl, ObjCIvarDecl **ivars, unsigned numIvars, SourceLocation RBrace) { assert(ImpDecl && "missing implementation decl"); ObjCInterfaceDecl* IDecl = getObjCInterfaceDecl(ImpDecl->getIdentifier()); if (!IDecl) return; /// Check case of non-existing @interface decl. /// (legacy objective-c @implementation decl without an @interface decl). /// Add implementations's ivar to the synthesize class's ivar list. if (IDecl->ImplicitInterfaceDecl()) { IDecl->addInstanceVariablesToClass(ivars, numIvars, RBrace); return; } // If implementation has empty ivar list, just return. if (numIvars == 0) return; assert(ivars && "missing @implementation ivars"); // Check interface's Ivar list against those in the implementation. // names and types must match. // unsigned j = 0; ObjCInterfaceDecl::ivar_iterator IVI = IDecl->ivar_begin(), IVE = IDecl->ivar_end(); for (; numIvars > 0 && IVI != IVE; ++IVI) { ObjCIvarDecl* ImplIvar = ivars[j++]; ObjCIvarDecl* ClsIvar = *IVI; assert (ImplIvar && "missing implementation ivar"); assert (ClsIvar && "missing class ivar"); if (ImplIvar->getCanonicalType() != ClsIvar->getCanonicalType()) { Diag(ImplIvar->getLocation(), diag::err_conflicting_ivar_type, ImplIvar->getIdentifier()->getName()); Diag(ClsIvar->getLocation(), diag::err_previous_definition, ClsIvar->getIdentifier()->getName()); } // TODO: Two mismatched (unequal width) Ivar bitfields should be diagnosed // as error. else if (ImplIvar->getIdentifier() != ClsIvar->getIdentifier()) { Diag(ImplIvar->getLocation(), diag::err_conflicting_ivar_name, ImplIvar->getIdentifier()->getName()); Diag(ClsIvar->getLocation(), diag::err_previous_definition, ClsIvar->getIdentifier()->getName()); return; } --numIvars; } if (numIvars > 0) Diag(ivars[j]->getLocation(), diag::err_inconsistant_ivar_count); else if (IVI != IVE) Diag((*IVI)->getLocation(), diag::err_inconsistant_ivar_count); } void Sema::WarnUndefinedMethod(SourceLocation ImpLoc, ObjCMethodDecl *method, bool &IncompleteImpl) { if (!IncompleteImpl) { Diag(ImpLoc, diag::warn_incomplete_impl); IncompleteImpl = true; } Diag(ImpLoc, diag::warn_undef_method_impl, method->getSelector().getName()); } /// CheckProtocolMethodDefs - This routine checks unimplemented methods /// Declared in protocol, and those referenced by it. void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc, ObjCProtocolDecl *PDecl, bool& IncompleteImpl, const llvm::DenseSet &InsMap, const llvm::DenseSet &ClsMap) { // check unimplemented instance methods. for (ObjCProtocolDecl::instmeth_iterator I = PDecl->instmeth_begin(), E = PDecl->instmeth_end(); I != E; ++I) { ObjCMethodDecl *method = *I; if (!InsMap.count(method->getSelector()) && method->getImplementationControl() != ObjCMethodDecl::Optional) WarnUndefinedMethod(ImpLoc, method, IncompleteImpl); } // check unimplemented class methods for (ObjCProtocolDecl::classmeth_iterator I = PDecl->classmeth_begin(), E = PDecl->classmeth_end(); I != E; ++I) { ObjCMethodDecl *method = *I; if (!ClsMap.count(method->getSelector()) && method->getImplementationControl() != ObjCMethodDecl::Optional) WarnUndefinedMethod(ImpLoc, method, IncompleteImpl); } // Check on this protocols's referenced protocols, recursively ObjCProtocolDecl** RefPDecl = PDecl->getReferencedProtocols(); for (unsigned i = 0; i < PDecl->getNumReferencedProtocols(); i++) CheckProtocolMethodDefs(ImpLoc, RefPDecl[i], IncompleteImpl, InsMap, ClsMap); } void Sema::ImplMethodsVsClassMethods(ObjCImplementationDecl* IMPDecl, ObjCInterfaceDecl* IDecl) { llvm::DenseSet InsMap; // Check and see if instance methods in class interface have been // implemented in the implementation class. for (ObjCImplementationDecl::instmeth_iterator I = IMPDecl->instmeth_begin(), E = IMPDecl->instmeth_end(); I != E; ++I) InsMap.insert((*I)->getSelector()); bool IncompleteImpl = false; for (ObjCInterfaceDecl::instmeth_iterator I = IDecl->instmeth_begin(), E = IDecl->instmeth_end(); I != E; ++I) if (!InsMap.count((*I)->getSelector())) WarnUndefinedMethod(IMPDecl->getLocation(), *I, IncompleteImpl); llvm::DenseSet ClsMap; // Check and see if class methods in class interface have been // implemented in the implementation class. for (ObjCImplementationDecl::classmeth_iterator I =IMPDecl->classmeth_begin(), E = IMPDecl->classmeth_end(); I != E; ++I) ClsMap.insert((*I)->getSelector()); for (ObjCInterfaceDecl::classmeth_iterator I = IDecl->classmeth_begin(), E = IDecl->classmeth_end(); I != E; ++I) if (!ClsMap.count((*I)->getSelector())) WarnUndefinedMethod(IMPDecl->getLocation(), *I, IncompleteImpl); // Check the protocol list for unimplemented methods in the @implementation // class. ObjCProtocolDecl** protocols = IDecl->getReferencedProtocols(); for (unsigned i = 0; i < IDecl->getNumIntfRefProtocols(); i++) CheckProtocolMethodDefs(IMPDecl->getLocation(), protocols[i], IncompleteImpl, InsMap, ClsMap); } /// ImplCategoryMethodsVsIntfMethods - Checks that methods declared in the /// category interface is implemented in the category @implementation. void Sema::ImplCategoryMethodsVsIntfMethods(ObjCCategoryImplDecl *CatImplDecl, ObjCCategoryDecl *CatClassDecl) { llvm::DenseSet InsMap; // Check and see if instance methods in category interface have been // implemented in its implementation class. for (ObjCCategoryImplDecl::instmeth_iterator I =CatImplDecl->instmeth_begin(), E = CatImplDecl->instmeth_end(); I != E; ++I) InsMap.insert((*I)->getSelector()); bool IncompleteImpl = false; for (ObjCCategoryDecl::instmeth_iterator I = CatClassDecl->instmeth_begin(), E = CatClassDecl->instmeth_end(); I != E; ++I) if (!InsMap.count((*I)->getSelector())) WarnUndefinedMethod(CatImplDecl->getLocation(), *I, IncompleteImpl); llvm::DenseSet ClsMap; // Check and see if class methods in category interface have been // implemented in its implementation class. for (ObjCCategoryImplDecl::classmeth_iterator I = CatImplDecl->classmeth_begin(), E = CatImplDecl->classmeth_end(); I != E; ++I) ClsMap.insert((*I)->getSelector()); for (ObjCCategoryDecl::classmeth_iterator I = CatClassDecl->classmeth_begin(), E = CatClassDecl->classmeth_end(); I != E; ++I) if (!ClsMap.count((*I)->getSelector())) WarnUndefinedMethod(CatImplDecl->getLocation(), *I, IncompleteImpl); // Check the protocol list for unimplemented methods in the @implementation // class. ObjCProtocolDecl** protocols = CatClassDecl->getReferencedProtocols(); for (unsigned i = 0; i < CatClassDecl->getNumReferencedProtocols(); i++) { ObjCProtocolDecl* PDecl = protocols[i]; CheckProtocolMethodDefs(CatImplDecl->getLocation(), PDecl, IncompleteImpl, InsMap, ClsMap); } } /// ActOnForwardClassDeclaration - Action::DeclTy * Sema::ActOnForwardClassDeclaration(SourceLocation AtClassLoc, IdentifierInfo **IdentList, unsigned NumElts) { llvm::SmallVector Interfaces; for (unsigned i = 0; i != NumElts; ++i) { // Check for another declaration kind with the same name. ScopedDecl *PrevDecl = LookupInterfaceDecl(IdentList[i]); if (PrevDecl && !isa(PrevDecl)) { Diag(AtClassLoc, diag::err_redefinition_different_kind, IdentList[i]->getName()); Diag(PrevDecl->getLocation(), diag::err_previous_definition); } ObjCInterfaceDecl *IDecl = dyn_cast_or_null(PrevDecl); if (!IDecl) { // Not already seen? Make a forward decl. IDecl = ObjCInterfaceDecl::Create(Context, AtClassLoc, 0, IdentList[i], true); // Chain & install the interface decl into the identifier. IDecl->setNext(IdentList[i]->getFETokenInfo()); IdentList[i]->setFETokenInfo(IDecl); // Remember that this needs to be removed when the scope is popped. TUScope->AddDecl(IDecl); } Interfaces.push_back(IDecl); } return ObjCClassDecl::Create(Context, AtClassLoc, &Interfaces[0], Interfaces.size()); } /// MatchTwoMethodDeclarations - Checks that two methods have matching type and /// returns true, or false, accordingly. /// TODO: Handle protocol list; such as id in type comparisons bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *Method, const ObjCMethodDecl *PrevMethod) { if (Method->getResultType().getCanonicalType() != PrevMethod->getResultType().getCanonicalType()) return false; for (unsigned i = 0, e = Method->getNumParams(); i != e; ++i) { ParmVarDecl *ParamDecl = Method->getParamDecl(i); ParmVarDecl *PrevParamDecl = PrevMethod->getParamDecl(i); if (ParamDecl->getCanonicalType() != PrevParamDecl->getCanonicalType()) return false; } return true; } void Sema::AddInstanceMethodToGlobalPool(ObjCMethodDecl *Method) { ObjCMethodList &FirstMethod = InstanceMethodPool[Method->getSelector()]; if (!FirstMethod.Method) { // Haven't seen a method with this selector name yet - add it. FirstMethod.Method = Method; FirstMethod.Next = 0; } else { // We've seen a method with this name, now check the type signature(s). bool match = MatchTwoMethodDeclarations(Method, FirstMethod.Method); for (ObjCMethodList *Next = FirstMethod.Next; !match && Next; Next = Next->Next) match = MatchTwoMethodDeclarations(Method, Next->Method); if (!match) { // We have a new signature for an existing method - add it. // This is extremely rare. Only 1% of Cocoa selectors are "overloaded". struct ObjCMethodList *OMI = new ObjCMethodList(Method, FirstMethod.Next); FirstMethod.Next = OMI; } } } void Sema::AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method) { ObjCMethodList &FirstMethod = FactoryMethodPool[Method->getSelector()]; if (!FirstMethod.Method) { // Haven't seen a method with this selector name yet - add it. FirstMethod.Method = Method; FirstMethod.Next = 0; } else { // We've seen a method with this name, now check the type signature(s). bool match = MatchTwoMethodDeclarations(Method, FirstMethod.Method); for (ObjCMethodList *Next = FirstMethod.Next; !match && Next; Next = Next->Next) match = MatchTwoMethodDeclarations(Method, Next->Method); if (!match) { // We have a new signature for an existing method - add it. // This is extremely rare. Only 1% of Cocoa selectors are "overloaded". struct ObjCMethodList *OMI = new ObjCMethodList(Method, FirstMethod.Next); FirstMethod.Next = OMI; } } } // Note: For class/category implemenations, allMethods/allProperties is // always null. void Sema::ActOnAtEnd(SourceLocation AtEndLoc, DeclTy *classDecl, DeclTy **allMethods, unsigned allNum, DeclTy **allProperties, unsigned pNum) { Decl *ClassDecl = static_cast(classDecl); // FIXME: If we don't have a ClassDecl, we have an error. We should consider // always passing in a decl. If the decl has an error, isInvalidDecl() // should be true. if (!ClassDecl) return; llvm::SmallVector insMethods; llvm::SmallVector clsMethods; llvm::DenseMap InsMap; llvm::DenseMap ClsMap; bool isInterfaceDeclKind = isa(ClassDecl) || isa(ClassDecl) || isa(ClassDecl); bool checkIdenticalMethods = isa(ClassDecl); // TODO: property declaration in category and protocols. if (pNum != 0) if (ObjCInterfaceDecl *IDecl = dyn_cast(ClassDecl)) { // FIXME: Move the memory allocation into setPropertyDecls! ObjCPropertyDecl **properties = new ObjCPropertyDecl*[pNum]; memcpy(properties, allProperties, pNum*sizeof(ObjCPropertyDecl*)); IDecl->setPropertyDecls(properties); IDecl->setNumPropertyDecl(pNum); } for (unsigned i = 0; i < allNum; i++ ) { ObjCMethodDecl *Method = cast_or_null(static_cast(allMethods[i])); if (!Method) continue; // Already issued a diagnostic. if (Method->isInstance()) { /// Check for instance method of the same name with incompatible types const ObjCMethodDecl *&PrevMethod = InsMap[Method->getSelector()]; bool match = PrevMethod ? MatchTwoMethodDeclarations(Method, PrevMethod) : false; if (isInterfaceDeclKind && PrevMethod && !match || checkIdenticalMethods && match) { Diag(Method->getLocation(), diag::error_duplicate_method_decl, Method->getSelector().getName()); Diag(PrevMethod->getLocation(), diag::err_previous_declaration); } else { insMethods.push_back(Method); InsMap[Method->getSelector()] = Method; /// The following allows us to typecheck messages to "id". AddInstanceMethodToGlobalPool(Method); } } else { /// Check for class method of the same name with incompatible types const ObjCMethodDecl *&PrevMethod = ClsMap[Method->getSelector()]; bool match = PrevMethod ? MatchTwoMethodDeclarations(Method, PrevMethod) : false; if (isInterfaceDeclKind && PrevMethod && !match || checkIdenticalMethods && match) { Diag(Method->getLocation(), diag::error_duplicate_method_decl, Method->getSelector().getName()); Diag(PrevMethod->getLocation(), diag::err_previous_declaration); } else { clsMethods.push_back(Method); ClsMap[Method->getSelector()] = Method; /// The following allows us to typecheck messages to "Class". AddFactoryMethodToGlobalPool(Method); } } } if (ObjCInterfaceDecl *I = dyn_cast(ClassDecl)) { I->addMethods(&insMethods[0], insMethods.size(), &clsMethods[0], clsMethods.size(), AtEndLoc); } else if (ObjCProtocolDecl *P = dyn_cast(ClassDecl)) { P->addMethods(&insMethods[0], insMethods.size(), &clsMethods[0], clsMethods.size(), AtEndLoc); } else if (ObjCCategoryDecl *C = dyn_cast(ClassDecl)) { C->addMethods(&insMethods[0], insMethods.size(), &clsMethods[0], clsMethods.size(), AtEndLoc); } else if (ObjCImplementationDecl *IC = dyn_cast(ClassDecl)) { IC->setLocEnd(AtEndLoc); if (ObjCInterfaceDecl* IDecl = getObjCInterfaceDecl(IC->getIdentifier())) ImplMethodsVsClassMethods(IC, IDecl); } else { ObjCCategoryImplDecl* CatImplClass = cast(ClassDecl); CatImplClass->setLocEnd(AtEndLoc); 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->getCategoryList(); Categories; Categories = Categories->getNextClassCategory()) { if (Categories->getIdentifier() == CatImplClass->getIdentifier()) { ImplCategoryMethodsVsIntfMethods(CatImplClass, Categories); break; } } } } } /// CvtQTToAstBitMask - utility routine to produce an AST bitmask for /// objective-c's type qualifier from the parser version of the same info. static Decl::ObjCDeclQualifier CvtQTToAstBitMask(ObjCDeclSpec::ObjCDeclQualifier PQTVal) { Decl::ObjCDeclQualifier ret = Decl::OBJC_TQ_None; if (PQTVal & ObjCDeclSpec::DQ_In) ret = (Decl::ObjCDeclQualifier)(ret | Decl::OBJC_TQ_In); if (PQTVal & ObjCDeclSpec::DQ_Inout) ret = (Decl::ObjCDeclQualifier)(ret | Decl::OBJC_TQ_Inout); if (PQTVal & ObjCDeclSpec::DQ_Out) ret = (Decl::ObjCDeclQualifier)(ret | Decl::OBJC_TQ_Out); if (PQTVal & ObjCDeclSpec::DQ_Bycopy) ret = (Decl::ObjCDeclQualifier)(ret | Decl::OBJC_TQ_Bycopy); if (PQTVal & ObjCDeclSpec::DQ_Byref) ret = (Decl::ObjCDeclQualifier)(ret | Decl::OBJC_TQ_Byref); if (PQTVal & ObjCDeclSpec::DQ_Oneway) ret = (Decl::ObjCDeclQualifier)(ret | Decl::OBJC_TQ_Oneway); return ret; } Sema::DeclTy *Sema::ActOnMethodDeclaration( SourceLocation MethodLoc, SourceLocation EndLoc, tok::TokenKind MethodType, DeclTy *classDecl, ObjCDeclSpec &ReturnQT, TypeTy *ReturnType, Selector Sel, // optional arguments. The number of types/arguments is obtained // from the Sel.getNumArgs(). ObjCDeclSpec *ArgQT, TypeTy **ArgTypes, IdentifierInfo **ArgNames, AttributeList *AttrList, tok::ObjCKeywordKind MethodDeclKind, bool isVariadic) { Decl *ClassDecl = static_cast(classDecl); // Make sure we can establish a context for the method. if (!ClassDecl) { Diag(MethodLoc, diag::error_missing_method_context); return 0; } llvm::SmallVector Params; for (unsigned i = 0; i < Sel.getNumArgs(); i++) { // FIXME: arg->AttrList must be stored too! QualType argType; if (ArgTypes[i]) argType = QualType::getFromOpaquePtr(ArgTypes[i]); else argType = Context.getObjCIdType(); ParmVarDecl* Param = ParmVarDecl::Create(Context, SourceLocation(/*FIXME*/), ArgNames[i], argType, VarDecl::None, 0); Param->setObjCDeclQualifier( CvtQTToAstBitMask(ArgQT[i].getObjCDeclQualifier())); Params.push_back(Param); } QualType resultDeclType; if (ReturnType) resultDeclType = QualType::getFromOpaquePtr(ReturnType); else // get the type for "id". resultDeclType = Context.getObjCIdType(); ObjCMethodDecl* ObjCMethod = ObjCMethodDecl::Create(Context, MethodLoc, EndLoc, Sel, resultDeclType, ClassDecl, AttrList, MethodType == tok::minus, isVariadic, MethodDeclKind == tok::objc_optional ? ObjCMethodDecl::Optional : ObjCMethodDecl::Required); ObjCMethod->setMethodParams(&Params[0], Sel.getNumArgs()); ObjCMethod->setObjCDeclQualifier( CvtQTToAstBitMask(ReturnQT.getObjCDeclQualifier())); const ObjCMethodDecl *PrevMethod = 0; // For implementations (which can be very "coarse grain"), we add the // method now. This allows the AST to implement lookup methods that work // incrementally (without waiting until we parse the @end). It also allows // us to flag multiple declaration errors as they occur. if (ObjCImplementationDecl *ImpDecl = dyn_cast(ClassDecl)) { if (MethodType == tok::minus) { PrevMethod = ImpDecl->getInstanceMethod(Sel); ImpDecl->addInstanceMethod(ObjCMethod); } else { PrevMethod = ImpDecl->getClassMethod(Sel); ImpDecl->addClassMethod(ObjCMethod); } } else if (ObjCCategoryImplDecl *CatImpDecl = dyn_cast(ClassDecl)) { if (MethodType == tok::minus) { PrevMethod = CatImpDecl->getInstanceMethod(Sel); CatImpDecl->addInstanceMethod(ObjCMethod); } else { PrevMethod = CatImpDecl->getClassMethod(Sel); CatImpDecl->addClassMethod(ObjCMethod); } } if (PrevMethod) { // You can never have two method definitions with the same name. Diag(ObjCMethod->getLocation(), diag::error_duplicate_method_decl, ObjCMethod->getSelector().getName()); Diag(PrevMethod->getLocation(), diag::err_previous_declaration); } return ObjCMethod; } Sema::DeclTy *Sema::ActOnAddObjCProperties(SourceLocation AtLoc, DeclTy **allProperties, unsigned NumProperties, ObjCDeclSpec &DS) { ObjCPropertyDecl *PDecl = ObjCPropertyDecl::Create(Context, AtLoc); if(DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_readonly) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readonly); if(DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_getter) { PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_getter); PDecl->setGetterName(DS.getGetterName()); } if(DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_setter) { PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_setter); PDecl->setSetterName(DS.getSetterName()); } if(DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_assign) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_assign); if(DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_readwrite) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readwrite); if(DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_retain) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_retain); if(DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_copy) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_copy); if(DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nonatomic) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nonatomic); PDecl->setNumPropertyDecls(NumProperties); if (NumProperties != 0) { ObjCIvarDecl **properties = new ObjCIvarDecl*[NumProperties]; memcpy(properties, allProperties, NumProperties*sizeof(ObjCIvarDecl*)); PDecl->setPropertyDecls(properties); } return PDecl; }