Skip to content

Commit

Permalink
[Sema][ObjC] Warn when a method declared in a protocol takes a
Browse files Browse the repository at this point in the history
non-escaping parameter but the implementation's method takes an escaping
parameter.

rdar://problem/39548196

Differential Revision: https://reviews.llvm.org/D49119

llvm-svn: 338189
  • Loading branch information
ahatanaka committed Jul 28, 2018
1 parent a4005e1 commit a6b5e00
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 7 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -1733,6 +1733,8 @@ def warn_overriding_method_missing_noescape : Warning<
"__attribute__((noescape))">, InGroup<MissingNoEscape>;
def note_overridden_marked_noescape : Note<
"parameter of overridden method is annotated with __attribute__((noescape))">;
def note_cat_conform_to_noescape_prot : Note<
"%select{category|class extension}0 conforms to protocol %1 which defines method %2">;

def err_covariant_return_inaccessible_base : Error<
"invalid covariant return for virtual function: %1 is a "
Expand Down
48 changes: 41 additions & 7 deletions clang/lib/Sema/SemaDeclObjC.cpp
Expand Up @@ -109,6 +109,30 @@ bool Sema::checkInitMethod(ObjCMethodDecl *method,
return true;
}

/// Issue a warning if the parameter of the overridden method is non-escaping
/// but the parameter of the overriding method is not.
static bool diagnoseNoescape(const ParmVarDecl *NewD, const ParmVarDecl *OldD,
Sema &S) {
if (OldD->hasAttr<NoEscapeAttr>() && !NewD->hasAttr<NoEscapeAttr>()) {
S.Diag(NewD->getLocation(), diag::warn_overriding_method_missing_noescape);
S.Diag(OldD->getLocation(), diag::note_overridden_marked_noescape);
return false;
}

return true;
}

/// Produce additional diagnostics if a category conforms to a protocol that
/// defines a method taking a non-escaping parameter.
static void diagnoseNoescape(const ParmVarDecl *NewD, const ParmVarDecl *OldD,
const ObjCCategoryDecl *CD,
const ObjCProtocolDecl *PD, Sema &S) {
if (!diagnoseNoescape(NewD, OldD, S))
S.Diag(CD->getLocation(), diag::note_cat_conform_to_noescape_prot)
<< CD->IsClassExtension() << PD
<< cast<ObjCMethodDecl>(NewD->getDeclContext());
}

void Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod,
const ObjCMethodDecl *Overridden) {
if (Overridden->hasRelatedResultType() &&
Expand Down Expand Up @@ -192,13 +216,7 @@ void Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod,
Diag(oldDecl->getLocation(), diag::note_previous_decl) << "parameter";
}

// A parameter of the overriding method should be annotated with noescape
// if the corresponding parameter of the overridden method is annotated.
if (oldDecl->hasAttr<NoEscapeAttr>() && !newDecl->hasAttr<NoEscapeAttr>()) {
Diag(newDecl->getLocation(),
diag::warn_overriding_method_missing_noescape);
Diag(oldDecl->getLocation(), diag::note_overridden_marked_noescape);
}
diagnoseNoescape(newDecl, oldDecl, *this);
}
}

Expand Down Expand Up @@ -4643,6 +4661,22 @@ Decl *Sema::ActOnMethodDeclaration(
<< ObjCMethod->getDeclName();
}
}

// Warn if a method declared in a protocol to which a category or
// extension conforms is non-escaping and the implementation's method is
// escaping.
for (auto *C : IDecl->visible_categories())
for (auto &P : C->protocols())
if (auto *IMD = P->lookupMethod(ObjCMethod->getSelector(),
ObjCMethod->isInstanceMethod())) {
assert(ObjCMethod->parameters().size() ==
IMD->parameters().size() &&
"Methods have different number of parameters");
auto OI = IMD->param_begin(), OE = IMD->param_end();
auto NI = ObjCMethod->param_begin();
for (; OI != OE; ++OI, ++NI)
diagnoseNoescape(*NI, *OI, C, P, *this);
}
}
} else {
cast<DeclContext>(ClassDecl)->addDecl(ObjCMethod);
Expand Down
39 changes: 39 additions & 0 deletions clang/test/SemaObjCXX/noescape.mm
Expand Up @@ -88,3 +88,42 @@ void test0() {

S5<&noescapeFunc2> ne1;
}

@protocol NoescapeProt
-(void) m0:(int*)__attribute__((noescape)) p; // expected-note 2 {{parameter of overridden method is annotated with __attribute__((noescape))}}
+(void) m1:(int*)__attribute__((noescape)) p;
-(void) m1:(int*) p;
@end

__attribute__((objc_root_class))
@interface C3
-(void) m0:(int*) p;
+(void) m1:(int*)__attribute__((noescape)) p;
-(void) m1:(int*) p;
@end

@interface C3 () <NoescapeProt> // expected-note {{class extension conforms to protocol 'NoescapeProt' which defines method 'm0:'}}
@end

@implementation C3
-(void) m0:(int*) p { // expected-warning {{parameter of overriding method should be annotated with __attribute__((noescape))}}
}
+(void) m1:(int*)__attribute__((noescape)) p {
}
-(void) m1:(int*) p {
}
@end

__attribute__((objc_root_class))
@interface C4 <NoescapeProt>
-(void) m0:(int*) p; // expected-warning {{parameter of overriding method should be annotated with __attribute__((noescape))}}
@end

@implementation C4
-(void) m0:(int*) p {
}
+(void) m1:(int*)__attribute__((noescape)) p {
}
-(void) m1:(int*) p {
}
@end

0 comments on commit a6b5e00

Please sign in to comment.