Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1378,8 +1378,11 @@ "several methods with selector %0 of mismatched types are found " "for the @selector expression">, InGroup, DefaultIgnore; -def err_direct_selector_expression: Error< +def err_direct_selector_expression : Error< "@selector expression formed with direct selector %0">; +def warn_potentially_direct_selector_expression : Warning< + "@selector expression formed with potentially direct selector %0">, + InGroup>; def err_objc_kindof_nonobject : Error< "'__kindof' specifier cannot be applied to non-object type %0">; Index: clang/lib/Sema/SemaExprObjC.cpp =================================================================== --- clang/lib/Sema/SemaExprObjC.cpp +++ clang/lib/Sema/SemaExprObjC.cpp @@ -1227,36 +1227,59 @@ return; } } - -static void HelperToDiagnoseDirectSelectorsExpr(Sema &S, SourceLocation AtLoc, - Selector Sel, +static void HelperToDiagnoseDirectSelectorsExpr(Sema &S, Selector Sel, ObjCMethodList &MethList, - bool &onlyDirect) { + bool &onlyDirect, + bool &anyDirect) { ObjCMethodList *M = &MethList; for (M = M->getNext(); M; M = M->getNext()) { ObjCMethodDecl *Method = M->getMethod(); if (Method->getSelector() != Sel) continue; - if (!Method->isDirectMethod()) + if (Method->isDirectMethod()) + anyDirect = true; + else onlyDirect = false; } } -static void DiagnoseDirectSelectorsExpr(Sema &S, SourceLocation AtLoc, - Selector Sel, bool &onlyDirect) { +static void DiagnoseDirectSelectorsExpr(Sema &S, Selector Sel, + bool &onlyDirect, bool &anyDirect) { for (Sema::GlobalMethodPool::iterator b = S.MethodPool.begin(), e = S.MethodPool.end(); b != e; b++) { // first, instance methods ObjCMethodList &InstMethList = b->second.first; - HelperToDiagnoseDirectSelectorsExpr(S, AtLoc, Sel, InstMethList, - onlyDirect); + HelperToDiagnoseDirectSelectorsExpr(S, Sel, InstMethList, + onlyDirect, anyDirect); // second, class methods ObjCMethodList &ClsMethList = b->second.second; - HelperToDiagnoseDirectSelectorsExpr(S, AtLoc, Sel, ClsMethList, onlyDirect); + HelperToDiagnoseDirectSelectorsExpr(S, Sel, ClsMethList, + onlyDirect, anyDirect); } } +static ObjCMethodDecl *findDirectMethodInCurrentClass(Sema &S, Selector Sel) { + auto *MD = S.getCurMethodDecl(); + if (!MD) + return nullptr; + ObjCInterfaceDecl *IFace = MD->getClassInterface(); + + // The language enforce that only one direct method is present in a given + // class, so we just need to find one method in the current class to know + // whether Sel is potentially direct in this context. + if (ObjCMethodDecl *MD = IFace->lookupMethod(Sel, /*isInstance=*/true)) + return MD->isDirectMethod() ? MD : nullptr; + if (ObjCMethodDecl *MD = IFace->lookupPrivateMethod(Sel, /*isInstance=*/true)) + return MD->isDirectMethod() ? MD : nullptr; + if (ObjCMethodDecl *MD = IFace->lookupMethod(Sel, /*isInstance=*/false)) + return MD->isDirectMethod() ? MD : nullptr; + if (ObjCMethodDecl *MD = IFace->lookupPrivateMethod(Sel, /*isInstance=*/false)) + return MD->isDirectMethod() ? MD : nullptr; + + return nullptr; +} + ExprResult Sema::ParseObjCSelectorExpression(Selector Sel, SourceLocation AtLoc, SourceLocation SelLoc, @@ -1280,15 +1303,27 @@ } else Diag(SelLoc, diag::warn_undeclared_selector) << Sel; } else { - bool onlyDirect = Method->isDirectMethod(); - DiagnoseDirectSelectorsExpr(*this, AtLoc, Sel, onlyDirect); DiagnoseMismatchedSelectors(*this, AtLoc, Method, LParenLoc, RParenLoc, WarnMultipleSelectors); + + bool onlyDirect = Method->isDirectMethod(); + bool anyDirect = onlyDirect; + DiagnoseDirectSelectorsExpr(*this, Sel, onlyDirect, anyDirect); + if (onlyDirect) { Diag(AtLoc, diag::err_direct_selector_expression) << Method->getSelector(); Diag(Method->getLocation(), diag::note_direct_method_declared_at) << Method->getDeclName(); + } else if (anyDirect) { + // If we saw any direct methods, see if we see a direct member of the + // current class. If so, the @selector will likely be used to refer to + // this direct method. + if (ObjCMethodDecl *DirectMethod = findDirectMethodInCurrentClass(*this, Sel)) { + Diag(AtLoc, diag::warn_potentially_direct_selector_expression) << Sel; + Diag(DirectMethod->getLocation(), diag::note_direct_method_declared_at) + << DirectMethod->getDeclName(); + } } } Index: clang/test/SemaObjC/potentially-direct-selector.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/potentially-direct-selector.m @@ -0,0 +1,127 @@ +// RUN: %clang_cc1 %s -Wpotentially-direct-selector -verify + +#define NS_DIRECT __attribute__((objc_direct)) + +@interface Dummies +-(void)inBase; +-(void)inBaseImpl; +-(void)inBaseCat; +-(void)inBaseCatImpl; +-(void)inDerived; +-(void)inDerivedImpl; +-(void)inDerivedCat; +-(void)inDerivedCatImpl; ++(void)inBaseClass; ++(void)inDerivedClass; ++(void)inDerivedCatClass; +@end + +__attribute__((objc_root_class)) +@interface Base +-(void)inBase NS_DIRECT; // expected-note 2 {{direct method}} ++(void)inBaseClass NS_DIRECT; // expected-note 2 {{direct method}} +@end + +@implementation Base +-(void)inBaseImpl NS_DIRECT { // expected-note 2 {{direct method}} +} +-(void)inBase {} ++(void)inBaseClass {} +@end + +@interface Base (Cat) +-(void)inBaseCat NS_DIRECT; // expected-note 2 {{direct method}} +@end + +@implementation Base (Cat) +-(void)inBaseCatImpl NS_DIRECT { // expected-note 2 {{direct method}} +} +-(void)inBaseCat {} +@end + +@interface Derived : Base +-(void)inDerived NS_DIRECT; // expected-note{{direct method}} ++(void)inDerivedClass NS_DIRECT; // expected-note{{direct method}} +@end + +@implementation Derived +-(void)inDerivedImpl NS_DIRECT { // expected-note{{direct method}} +} +-(void)inDerived {} ++(void)inDerivedClass {} +@end + +@interface Derived (Cat) +-(void)inDerivedCat NS_DIRECT; // expected-note{{direct method}} ++(void)inDerivedCatClass NS_DIRECT; // expected-note{{direct method}} +@end + +@implementation Derived (Cat) +-(void)inDerivedCatImpl NS_DIRECT { // expected-note{{direct method}} +} +-(void)inDerivedCat {} ++(void)inDerivedCatClass {} + +-(void)test1 { + (void)@selector(inBase); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inBaseImpl); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inBaseCat); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inBaseCatImpl); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inDerived); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inDerivedImpl); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inDerivedCat); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inDerivedCatImpl); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inDerivedClass); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inBaseClass); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inDerivedCatClass); // expected-warning{{@selector expression formed with potentially direct selector}} +} +@end + +void test2() { + (void)@selector(inBase); + (void)@selector(inBaseImpl); + (void)@selector(inBaseCat); + (void)@selector(inBaseCatImpl); + (void)@selector(inDerived); + (void)@selector(inDerivedImpl); + (void)@selector(inDerivedCat); + (void)@selector(inDerivedCatImpl); + (void)@selector(inDerivedClass); + (void)@selector(inBaseClass); + (void)@selector(inDerivedCatClass); +} + +@interface OnlyBase : Base @end +@implementation OnlyBase +-(void)test3 { + (void)@selector(inBase); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inBaseImpl); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inBaseCat); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inBaseCatImpl); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inDerived); + (void)@selector(inDerivedImpl); + (void)@selector(inDerivedCat); + (void)@selector(inDerivedCatImpl); + (void)@selector(inDerivedClass); + (void)@selector(inBaseClass); // expected-warning{{@selector expression formed with potentially direct selector}} + (void)@selector(inDerivedCatClass); +} +@end + +__attribute__((objc_root_class)) +@interface Unrelated @end +@implementation Unrelated +-(void)test3 { + (void)@selector(inBase); + (void)@selector(inBaseImpl); + (void)@selector(inBaseCat); + (void)@selector(inBaseCatImpl); + (void)@selector(inDerived); + (void)@selector(inDerivedImpl); + (void)@selector(inDerivedCat); + (void)@selector(inDerivedCatImpl); + (void)@selector(inDerivedClass); + (void)@selector(inBaseClass); + (void)@selector(inDerivedCatClass); +} +@end