diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index 2a175513cba5..d4735bfb843e 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -128,6 +128,9 @@ private: // Synthesized declaration method for a property setter/getter bool IsSynthesized : 1; + + // Method has a definition. + bool IsDefined : 1; // NOTE: VC++ treats enums as signed, avoid using ImplementationControl enum /// @required/@optional @@ -171,12 +174,14 @@ private: bool isInstance = true, bool isVariadic = false, bool isSynthesized = false, + bool isDefined = false, ImplementationControl impControl = None, unsigned numSelectorArgs = 0) : NamedDecl(ObjCMethod, contextDecl, beginLoc, SelInfo), DeclContext(ObjCMethod), IsInstance(isInstance), IsVariadic(isVariadic), IsSynthesized(isSynthesized), + IsDefined(isDefined), DeclImplementation(impControl), objcDeclQualifier(OBJC_TQ_None), NumSelectorArgs(numSelectorArgs), MethodDeclType(T), ResultTInfo(ResultTInfo), @@ -203,6 +208,7 @@ public: bool isInstance = true, bool isVariadic = false, bool isSynthesized = false, + bool isDefined = false, ImplementationControl impControl = None, unsigned numSelectorArgs = 0); @@ -296,6 +302,9 @@ public: bool isSynthesized() const { return IsSynthesized; } void setSynthesized(bool isSynth) { IsSynthesized = isSynth; } + + bool isDefined() const { return IsDefined; } + void setDefined(bool isDefined) { IsDefined = isDefined; } // Related to protocols declared in @protocol void setDeclImplementation(ImplementationControl ic) { diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 1416d7195a7b..033b6c421404 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -134,6 +134,7 @@ def UnusedVariable : DiagGroup<"unused-variable">; def ReadOnlySetterAttrs : DiagGroup<"readonly-setter-attrs">; def Reorder : DiagGroup<"reorder">; def UndeclaredSelector : DiagGroup<"undeclared-selector">; +def Selector : DiagGroup<"selector">; def Protocol : DiagGroup<"protocol">; def SuperSubClassMismatch : DiagGroup<"super-class-method-mismatch">; def : DiagGroup<"variadic-macros">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 59bd8f8e384d..6fb53189cda0 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -400,6 +400,8 @@ def warn_objc_property_attr_mutually_exclusive : Warning< InGroup, DefaultIgnore; def warn_undeclared_selector : Warning< "undeclared selector %0">, InGroup, DefaultIgnore; +def warn_unimplemented_selector: Warning< + "unimplemented selector %0">, InGroup, DefaultIgnore; def warn_unimplemented_protocol_method : Warning< "method in protocol not implemented">, InGroup; diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index 9cb47aa8da25..2ca49ad675a0 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -202,6 +202,8 @@ public: return DeclGroupPtrTy(); } + virtual void DiagnoseUseOfUnimplementedSelectors() {} + /// getTypeName - Return non-null if the specified identifier is a type name /// in the current scope. /// diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 90a1cab5cf12..c23adb60d2fb 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -201,7 +201,7 @@ public: /// the EOF was encountered. bool ParseTopLevelDecl(DeclGroupPtrTy &Result); - DeclGroupPtrTy RetrievePendingObjCImpDecl(); + DeclGroupPtrTy FinishPendingObjCActions(); private: //===--------------------------------------------------------------------===// diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 6b2c90c1d187..befdc7aede38 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2299,6 +2299,7 @@ Decl *ASTNodeImporter::VisitObjCMethodDecl(ObjCMethodDecl *D) { D->isInstanceMethod(), D->isVariadic(), D->isSynthesized(), + D->isDefined(), D->getImplementationControl()); // FIXME: When we decide to merge method definitions, we'll need to diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index fce24dcf3849..c682cf18bfa0 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -339,12 +339,14 @@ ObjCMethodDecl *ObjCMethodDecl::Create(ASTContext &C, bool isInstance, bool isVariadic, bool isSynthesized, + bool isDefined, ImplementationControl impControl, unsigned numSelectorArgs) { return new (C) ObjCMethodDecl(beginLoc, endLoc, SelInfo, T, ResultTInfo, contextDecl, isInstance, - isVariadic, isSynthesized, impControl, + isVariadic, isSynthesized, isDefined, + impControl, numSelectorArgs); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index f6788c6c3096..057182d0392b 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1930,7 +1930,7 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) { D->getLocation(), D->getLocation(), cxxSelector, getContext().VoidTy, 0, - DC, true, false, true, + DC, true, false, true, false, ObjCMethodDecl::Required); D->addInstanceMethod(DTORMethod); CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, DTORMethod, false); @@ -1942,7 +1942,7 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) { D->getLocation(), D->getLocation(), cxxSelector, getContext().getObjCIdType(), 0, - DC, true, false, true, + DC, true, false, true, false, ObjCMethodDecl::Required); D->addInstanceMethod(CTORMethod); CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, CTORMethod, true); diff --git a/clang/lib/Frontend/PCHReaderDecl.cpp b/clang/lib/Frontend/PCHReaderDecl.cpp index d1f8b3aae94d..058b19dab357 100644 --- a/clang/lib/Frontend/PCHReaderDecl.cpp +++ b/clang/lib/Frontend/PCHReaderDecl.cpp @@ -324,6 +324,7 @@ void PCHDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) { MD->setInstanceMethod(Record[Idx++]); MD->setVariadic(Record[Idx++]); MD->setSynthesized(Record[Idx++]); + MD->setDefined(Record[Idx++]); MD->setDeclImplementation((ObjCMethodDecl::ImplementationControl)Record[Idx++]); MD->setObjCDeclQualifier((Decl::ObjCDeclQualifier)Record[Idx++]); MD->setNumSelectorArgs(unsigned(Record[Idx++])); diff --git a/clang/lib/Frontend/PCHWriterDecl.cpp b/clang/lib/Frontend/PCHWriterDecl.cpp index f6c6cde1577c..7c4a21adb65a 100644 --- a/clang/lib/Frontend/PCHWriterDecl.cpp +++ b/clang/lib/Frontend/PCHWriterDecl.cpp @@ -312,6 +312,7 @@ void PCHDeclWriter::VisitObjCMethodDecl(ObjCMethodDecl *D) { Record.push_back(D->isInstanceMethod()); Record.push_back(D->isVariadic()); Record.push_back(D->isSynthesized()); + Record.push_back(D->isDefined()); // FIXME: stable encoding for @required/@optional Record.push_back(D->getImplementationControl()); // FIXME: stable encoding for in/out/inout/bycopy/byref/oneway diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 68473a551d1a..a7e08562d02a 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -1314,7 +1314,8 @@ Parser::DeclPtrTy Parser::ParseObjCAtEndDeclaration(SourceRange atEnd) { return Result; } -Parser::DeclGroupPtrTy Parser::RetrievePendingObjCImpDecl() { +Parser::DeclGroupPtrTy Parser::FinishPendingObjCActions() { + Actions.DiagnoseUseOfUnimplementedSelectors(); if (PendingObjCImpDecl.empty()) return Actions.ConvertDeclToDeclGroup(DeclPtrTy()); DeclPtrTy ImpDecl = PendingObjCImpDecl.pop_back_val(); diff --git a/clang/lib/Sema/ParseAST.cpp b/clang/lib/Sema/ParseAST.cpp index bb0bd9e1cb5e..73f1167d0e68 100644 --- a/clang/lib/Sema/ParseAST.cpp +++ b/clang/lib/Sema/ParseAST.cpp @@ -92,7 +92,7 @@ void clang::ParseAST(Preprocessor &PP, ASTConsumer *Consumer, Consumer->HandleTopLevelDecl(ADecl.getAsVal()); }; // Check for any pending objective-c implementation decl. - while ((ADecl = P.RetrievePendingObjCImpDecl())) + while ((ADecl = P.FinishPendingObjCActions())) Consumer->HandleTopLevelDecl(ADecl.getAsVal()); // Process any TopLevelDecls generated by #pragma weak. diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 490ff65cd9a5..0540dd9e0c54 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -606,6 +606,11 @@ public: MethodPool InstanceMethodPool; MethodPool FactoryMethodPool; + /// Method selectors used in a @selector expression. Used for implementation + /// of -Wselector. + llvm::DenseMap ReferencedSelectors; + + MethodPool::iterator ReadMethodPool(Selector Sel, bool isInstance); /// Private Helper predicate to check for 'self'. @@ -798,6 +803,8 @@ public: DeclGroupPtrTy ConvertDeclToDeclGroup(DeclPtrTy Ptr); + void DiagnoseUseOfUnimplementedSelectors(); + virtual TypeTy *getTypeName(IdentifierInfo &II, SourceLocation NameLoc, Scope *S, CXXScopeSpec *SS, bool isClassName = false, @@ -1656,7 +1663,7 @@ public: /// unit are added to a global pool. This allows us to efficiently associate /// a selector with a method declaraation for purposes of typechecking /// messages sent to "id" (where the class of the object is unknown). - void AddInstanceMethodToGlobalPool(ObjCMethodDecl *Method); + void AddInstanceMethodToGlobalPool(ObjCMethodDecl *Method, bool impl=false); /// LookupInstanceMethodInGlobalPool - Returns the method and warns if /// there are multiple signatures. @@ -1665,10 +1672,15 @@ public: /// LookupFactoryMethodInGlobalPool - Returns the method and warns if /// there are multiple signatures. - ObjCMethodDecl *LookupFactoryMethodInGlobalPool(Selector Sel, SourceRange R); + ObjCMethodDecl *LookupFactoryMethodInGlobalPool(Selector Sel, SourceRange R, + bool warn=true); + + /// LookupImplementedMethodInGlobalPool - Returns the method which has an + /// implementation. + ObjCMethodDecl *LookupImplementedMethodInGlobalPool(Selector Sel); /// AddFactoryMethodToGlobalPool - Same as above, but for factory methods. - void AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method); + void AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method, bool impl=false); /// CollectIvarsToConstructOrDestruct - Collect those ivars which require /// initialization. diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 21aeb59a0886..5f2f37eb0b0c 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -32,10 +32,10 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, DeclPtrTy D) { // Allow the rest of sema to find private method decl implementations. if (MDecl->isInstanceMethod()) - AddInstanceMethodToGlobalPool(MDecl); + AddInstanceMethodToGlobalPool(MDecl, true); else - AddFactoryMethodToGlobalPool(MDecl); - + AddFactoryMethodToGlobalPool(MDecl, true); + // Allow all of Sema to see that we are entering a method definition. PushDeclContext(FnBodyScope, MDecl); PushFunctionScope(); @@ -1130,7 +1130,7 @@ Sema::MethodPool::iterator Sema::ReadMethodPool(Selector Sel, return FactoryMethodPool.insert(std::make_pair(Sel, Methods.second)).first; } -void Sema::AddInstanceMethodToGlobalPool(ObjCMethodDecl *Method) { +void Sema::AddInstanceMethodToGlobalPool(ObjCMethodDecl *Method, bool impl) { llvm::DenseMap::iterator Pos = InstanceMethodPool.find(Method->getSelector()); if (Pos == InstanceMethodPool.end()) { @@ -1140,7 +1140,7 @@ void Sema::AddInstanceMethodToGlobalPool(ObjCMethodDecl *Method) { Pos = InstanceMethodPool.insert(std::make_pair(Method->getSelector(), ObjCMethodList())).first; } - + Method->setDefined(impl); ObjCMethodList &Entry = Pos->second; if (Entry.Method == 0) { // Haven't seen a method with this selector name yet - add it. @@ -1152,8 +1152,10 @@ void Sema::AddInstanceMethodToGlobalPool(ObjCMethodDecl *Method) { // We've seen a method with this name, see if we have already seen this type // signature. for (ObjCMethodList *List = &Entry; List; List = List->Next) - if (MatchTwoMethodDeclarations(Method, List->Method)) + if (MatchTwoMethodDeclarations(Method, List->Method)) { + List->Method->setDefined(impl); return; + } // We have a new signature for an existing method - add it. // This is extremely rare. Only 1% of Cocoa selectors are "overloaded". @@ -1194,7 +1196,7 @@ ObjCMethodDecl *Sema::LookupInstanceMethodInGlobalPool(Selector Sel, return MethList.Method; } -void Sema::AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method) { +void Sema::AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method, bool impl) { llvm::DenseMap::iterator Pos = FactoryMethodPool.find(Method->getSelector()); if (Pos == FactoryMethodPool.end()) { @@ -1204,32 +1206,31 @@ void Sema::AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method) { Pos = FactoryMethodPool.insert(std::make_pair(Method->getSelector(), ObjCMethodList())).first; } - - ObjCMethodList &FirstMethod = Pos->second; - if (!FirstMethod.Method) { + Method->setDefined(impl); + ObjCMethodList &Entry = Pos->second; + if (!Entry.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". - ObjCMethodList *Mem = BumpAlloc.Allocate(); - ObjCMethodList *OMI = new (Mem) ObjCMethodList(Method, FirstMethod.Next); - FirstMethod.Next = OMI; + Entry.Method = Method; + Entry.Next = 0; + return; + } + // We've seen a method with this name, see if we have already seen this type + // signature. + for (ObjCMethodList *List = &Entry; List; List = List->Next) + if (MatchTwoMethodDeclarations(Method, List->Method)) { + List->Method->setDefined(impl); + return; } - } + + // We have a new signature for an existing method - add it. + // This is extremely rare. Only 1% of Cocoa selectors are "overloaded". + ObjCMethodList *Mem = BumpAlloc.Allocate(); + Entry.Next = new (Mem) ObjCMethodList(Method, Entry.Next); } ObjCMethodDecl *Sema::LookupFactoryMethodInGlobalPool(Selector Sel, - SourceRange R) { + SourceRange R, + bool warn) { llvm::DenseMap::iterator Pos = FactoryMethodPool.find(Sel); if (Pos == FactoryMethodPool.end()) { @@ -1246,7 +1247,7 @@ ObjCMethodDecl *Sema::LookupFactoryMethodInGlobalPool(Selector Sel, for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) // This checks if the methods differ by size & alignment. if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, true)) - issueWarning = true; + issueWarning = warn; } if (issueWarning && (MethList.Method && MethList.Next)) { Diag(R.getBegin(), diag::warn_multiple_method_decl) << Sel << R; @@ -1259,6 +1260,18 @@ ObjCMethodDecl *Sema::LookupFactoryMethodInGlobalPool(Selector Sel, return MethList.Method; } +ObjCMethodDecl *Sema::LookupImplementedMethodInGlobalPool(Selector Sel) { + SourceRange SR; + ObjCMethodDecl *Method = LookupInstanceMethodInGlobalPool(Sel, + SR, false); + if (Method && Method->isDefined()) + return Method; + Method = LookupFactoryMethodInGlobalPool(Sel, SR, false); + if (Method && Method->isDefined()) + return Method; + return 0; +} + /// CompareMethodParamsInBaseAndSuper - This routine compares methods with /// identical selector names in current and its super classes and issues /// a warning if any of their argument types are incompatible. @@ -1540,7 +1553,7 @@ Sema::DeclPtrTy Sema::ActOnMethodDeclaration( ResultTInfo, cast(ClassDecl), MethodType == tok::minus, isVariadic, - false, + false, false, MethodDeclKind == tok::objc_optional ? ObjCMethodDecl::Optional : ObjCMethodDecl::Required); @@ -1849,3 +1862,15 @@ void ObjCImplementationDecl::setIvarInitializers(ASTContext &C, } } +void Sema::DiagnoseUseOfUnimplementedSelectors() { + if (ReferencedSelectors.empty()) + return; + for (llvm::DenseMap::iterator S = + ReferencedSelectors.begin(), + E = ReferencedSelectors.end(); S != E; ++S) { + Selector Sel = (*S).first; + if (!LookupImplementedMethodInGlobalPool(Sel)) + Diag((*S).second, diag::warn_unimplemented_selector) << Sel; + } + return; +} diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index 9f43471e0ade..5132464d244f 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -163,6 +163,11 @@ Sema::ExprResult Sema::ParseObjCSelectorExpression(Selector Sel, if (!Method) Diag(SelLoc, diag::warn_undeclared_selector) << Sel; + llvm::DenseMap::iterator Pos + = ReferencedSelectors.find(Sel); + if (Pos == ReferencedSelectors.end()) + ReferencedSelectors.insert(std::make_pair(Sel, SelLoc)); + QualType Ty = Context.getObjCSelType(); return new (Context) ObjCSelectorExpr(Ty, Sel, AtLoc, RParenLoc); } diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index e7358edec3e2..6a7d8dbee128 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -1084,7 +1084,8 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, // for this class. GetterMethod = ObjCMethodDecl::Create(Context, property->getLocation(), property->getLocation(), property->getGetterName(), - property->getType(), 0, CD, true, false, true, + property->getType(), 0, CD, true, false, true, + false, (property->getPropertyImplementation() == ObjCPropertyDecl::Optional) ? ObjCMethodDecl::Optional : @@ -1112,6 +1113,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, property->getLocation(), property->getSetterName(), Context.VoidTy, 0, CD, true, false, true, + false, (property->getPropertyImplementation() == ObjCPropertyDecl::Optional) ? ObjCMethodDecl::Optional :