ObjectiveC: under -Wunused-property-ivar warn if property's

backing warning is not used in one of its accessor methods.
// rdar://14989999

llvm-svn: 193439
This commit is contained in:
Fariborz Jahanian 2013-10-25 21:44:50 +00:00
parent 57243da70f
commit 5e3429c395
13 changed files with 132 additions and 8 deletions

View File

@ -1246,10 +1246,12 @@ private:
ObjCIvarDecl(ObjCContainerDecl *DC, SourceLocation StartLoc,
SourceLocation IdLoc, IdentifierInfo *Id,
QualType T, TypeSourceInfo *TInfo, AccessControl ac, Expr *BW,
bool synthesized)
bool synthesized,
bool backingIvarReferencedInAccessor)
: FieldDecl(ObjCIvar, DC, StartLoc, IdLoc, Id, T, TInfo, BW,
/*Mutable=*/false, /*HasInit=*/ICIS_NoInit),
NextIvar(0), DeclAccess(ac), Synthesized(synthesized) {}
NextIvar(0), DeclAccess(ac), Synthesized(synthesized),
BackingIvarReferencedInAccessor(backingIvarReferencedInAccessor) {}
public:
static ObjCIvarDecl *Create(ASTContext &C, ObjCContainerDecl *DC,
@ -1257,7 +1259,8 @@ public:
IdentifierInfo *Id, QualType T,
TypeSourceInfo *TInfo,
AccessControl ac, Expr *BW = NULL,
bool synthesized=false);
bool synthesized=false,
bool backingIvarReferencedInAccessor=false);
static ObjCIvarDecl *CreateDeserialized(ASTContext &C, unsigned ID);
@ -1279,6 +1282,13 @@ public:
return DeclAccess == None ? Protected : AccessControl(DeclAccess);
}
void setBackingIvarReferencedInAccessor(bool val) {
BackingIvarReferencedInAccessor = val;
}
bool getBackingIvarReferencedInAccessor() const {
return BackingIvarReferencedInAccessor;
}
void setSynthesize(bool synth) { Synthesized = synth; }
bool getSynthesize() const { return Synthesized; }
@ -1293,6 +1303,7 @@ private:
// NOTE: VC++ treats enums as signed, avoid using the AccessControl enum
unsigned DeclAccess : 3;
unsigned Synthesized : 1;
unsigned BackingIvarReferencedInAccessor : 1;
};

View File

@ -352,6 +352,7 @@ def UnusedValue : DiagGroup<"unused-value", [UnusedComparison, UnusedResult]>;
def UnusedConstVariable : DiagGroup<"unused-const-variable">;
def UnusedVariable : DiagGroup<"unused-variable",
[UnusedConstVariable]>;
def UnusedPropertyIvar : DiagGroup<"unused-property-ivar">;
def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">;
def UserDefinedLiterals : DiagGroup<"user-defined-literals">;
def ReadOnlySetterAttrs : DiagGroup<"readonly-setter-attrs">;
@ -437,7 +438,7 @@ def Unused : DiagGroup<"unused",
// UnusedParameter, (matches GCC's behavior)
// UnusedMemberFunction, (clean-up llvm before enabling)
UnusedPrivateField,
UnusedValue, UnusedVariable]>,
UnusedValue, UnusedVariable, UnusedPropertyIvar]>,
DiagCategory<"Unused Entity Issue">;
// Format settings.

View File

@ -152,6 +152,10 @@ def warn_unused_parameter : Warning<"unused parameter %0">,
InGroup<UnusedParameter>, DefaultIgnore;
def warn_unused_variable : Warning<"unused variable %0">,
InGroup<UnusedVariable>, DefaultIgnore;
def warn_unused_property_backing_ivar :
Warning<"ivar %0 which backs the property is not "
"referenced in this property's accessor">,
InGroup<UnusedPropertyIvar>, DefaultIgnore;
def warn_unused_const_variable : Warning<"unused variable %0">,
InGroup<UnusedConstVariable>, DefaultIgnore;
def warn_unused_exception_param : Warning<"unused exception parameter %0">,

View File

@ -2629,6 +2629,16 @@ public:
bool IvarBacksCurrentMethodAccessor(ObjCInterfaceDecl *IFace,
ObjCMethodDecl *Method, ObjCIvarDecl *IV);
/// DiagnoseUnusedBackingIvarInAccessor - Issue an 'unused' warning if ivar which
/// backs the property is not used in the property's accessor.
void DiagnoseUnusedBackingIvarInAccessor(Scope *S);
/// GetIvarBackingPropertyAccessor - If method is a property setter/getter and
/// it property has a backing ivar, returns this ivar; otherwise, returns NULL.
/// It also returns ivar's property on success.
ObjCIvarDecl *GetIvarBackingPropertyAccessor(const ObjCMethodDecl *Method,
const ObjCPropertyDecl *&PDecl) const;
/// Called by ActOnProperty to handle \@property declarations in
/// class extensions.
ObjCPropertyDecl *HandlePropertyInClassExtension(Scope *S,

View File

@ -1241,6 +1241,8 @@ void ASTDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
dumpType(D->getType());
if (D->getSynthesize())
OS << " synthesize";
if (D->getBackingIvarReferencedInAccessor())
OS << " BackingIvarReferencedInAccessor";
switch (D->getAccessControl()) {
case ObjCIvarDecl::None:

View File

@ -3017,7 +3017,8 @@ Decl *ASTNodeImporter::VisitObjCIvarDecl(ObjCIvarDecl *D) {
Importer.Import(D->getInnerLocStart()),
Loc, Name.getAsIdentifierInfo(),
T, TInfo, D->getAccessControl(),
BitWidth, D->getSynthesize());
BitWidth, D->getSynthesize(),
D->getBackingIvarReferencedInAccessor());
ToIvar->setLexicalDeclContext(LexicalDC);
Importer.Imported(D, ToIvar);
LexicalDC->addDeclInternal(ToIvar);

View File

@ -1325,7 +1325,8 @@ ObjCIvarDecl *ObjCIvarDecl::Create(ASTContext &C, ObjCContainerDecl *DC,
SourceLocation IdLoc, IdentifierInfo *Id,
QualType T, TypeSourceInfo *TInfo,
AccessControl ac, Expr *BW,
bool synthesized) {
bool synthesized,
bool backingIvarReferencedInAccessor) {
if (DC) {
// Ivar's can only appear in interfaces, implementations (via synthesized
// properties), and class extensions (via direct declaration, or synthesized
@ -1353,13 +1354,13 @@ ObjCIvarDecl *ObjCIvarDecl::Create(ASTContext &C, ObjCContainerDecl *DC,
}
return new (C) ObjCIvarDecl(DC, StartLoc, IdLoc, Id, T, TInfo,
ac, BW, synthesized);
ac, BW, synthesized, backingIvarReferencedInAccessor);
}
ObjCIvarDecl *ObjCIvarDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
void *Mem = AllocateDeserializedDecl(C, ID, sizeof(ObjCIvarDecl));
return new (Mem) ObjCIvarDecl(0, SourceLocation(), SourceLocation(), 0,
QualType(), 0, ObjCIvarDecl::None, 0, false);
QualType(), 0, ObjCIvarDecl::None, 0, false, false);
}
const ObjCInterfaceDecl *ObjCIvarDecl::getContainingInterface() const {

View File

@ -1388,6 +1388,7 @@ void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) {
// Remove this name from our lexical scope.
IdResolver.RemoveDecl(D);
}
DiagnoseUnusedBackingIvarInAccessor(S);
}
void Sema::ActOnStartFunctionDeclarator() {

View File

@ -3501,3 +3501,34 @@ void Sema::DiagnoseUseOfUnimplementedSelectors() {
}
return;
}
ObjCIvarDecl *
Sema::GetIvarBackingPropertyAccessor(const ObjCMethodDecl *Method,
const ObjCPropertyDecl *&PDecl) const {
const ObjCInterfaceDecl *IDecl = Method->getClassInterface();
if (!IDecl)
return 0;
Method = IDecl->lookupMethod(Method->getSelector(), true);
if (!Method || !Method->isPropertyAccessor())
return 0;
if ((PDecl = Method->findPropertyDecl()))
return PDecl->getPropertyIvarDecl();
return 0;
}
void Sema::DiagnoseUnusedBackingIvarInAccessor(Scope *S) {
if (S->hasUnrecoverableErrorOccurred() || !S->isInObjcMethodScope())
return;
const ObjCMethodDecl *CurMethod = getCurMethodDecl();
if (!CurMethod)
return;
const ObjCPropertyDecl *PDecl;
const ObjCIvarDecl *IV = GetIvarBackingPropertyAccessor(CurMethod, PDecl);
if (IV && !IV->getBackingIvarReferencedInAccessor()) {
Diag(getCurMethodDecl()->getLocation(), diag::warn_unused_property_backing_ivar)
<< IV->getDeclName();
Diag(PDecl->getLocation(), diag::note_property_declare);
}
}

View File

@ -2257,6 +2257,12 @@ Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S,
return ExprError();
MarkAnyDeclReferenced(Loc, IV, true);
// Mark this ivar 'referenced' in this method, if it is a backing ivar
// of a property and current method is one of its property accessor.
const ObjCPropertyDecl *PDecl;
const ObjCIvarDecl *BIV = GetIvarBackingPropertyAccessor(CurMethod, PDecl);
if (BIV && BIV == IV)
IV->setBackingIvarReferencedInAccessor(true);
ObjCMethodFamily MF = CurMethod->getMethodFamily();
if (MF != OMF_init && MF != OMF_dealloc && MF != OMF_finalize &&

View File

@ -798,6 +798,8 @@ void ASTDeclReader::VisitObjCIvarDecl(ObjCIvarDecl *IVD) {
IVD->setNextIvar(0);
bool synth = Record[Idx++];
IVD->setSynthesize(synth);
bool backingIvarReferencedInAccessor = Record[Idx++];
IVD->setBackingIvarReferencedInAccessor(backingIvarReferencedInAccessor);
}
void ASTDeclReader::VisitObjCProtocolDecl(ObjCProtocolDecl *PD) {

View File

@ -528,6 +528,7 @@ void ASTDeclWriter::VisitObjCIvarDecl(ObjCIvarDecl *D) {
// FIXME: stable encoding for @public/@private/@protected/@package
Record.push_back(D->getAccessControl());
Record.push_back(D->getSynthesize());
Record.push_back(D->getBackingIvarReferencedInAccessor());
if (!D->hasAttrs() &&
!D->isImplicit() &&
@ -1505,6 +1506,8 @@ void ASTWriter::WriteDeclsBlockAbbrevs() {
// ObjC Ivar
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // getAccessControl
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // getSynthesize
// getBackingIvarReferencedInAccessor
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6));
// Type Source Info
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6));
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));

View File

@ -0,0 +1,51 @@
// RUN: %clang_cc1 -fsyntax-only -Wunused-property-ivar -verify -Wno-objc-root-class %s
// rdar://14989999
@interface NSObject @end
@interface Example : NSObject
@property (nonatomic, copy) id t; // expected-note {{property declared here}}
@property (nonatomic, copy) id u; // expected-note {{property declared here}}
@property (nonatomic, copy) id v; // expected-note {{property declared here}}
@property (nonatomic, copy) id w;
@property (nonatomic, copy) id x; // expected-note {{property declared here}}
@property (nonatomic, copy) id y; // expected-note {{property declared here}}
@property (nonatomic, copy) id z;
@property (nonatomic, copy) id ok;
@end
@implementation Example
- (void) setX:(id)newX { // expected-warning {{ivar '_x' which backs the property is not referenced in this property's accessor}}
_y = newX;
}
- (id) y { // expected-warning {{ivar '_y' which backs the property is not referenced in this property's accessor}}
return _v;
}
- (void) setV:(id)newV { // expected-warning {{ivar '_v' which backs the property is not referenced in this property's accessor}}
_y = newV;
}
// No warning here because there is no backing ivar.
// both setter/getter are user defined.
- (void) setW:(id)newW {
_y = newW;
}
- (id) w {
return _v;
}
- (id) u { // expected-warning {{ivar '_u' which backs the property is not referenced in this property's accessor}}
return _v;
}
@synthesize ok = okIvar;
- (void) setOk:(id)newOk {
okIvar = newOk;
}
@synthesize t = tIvar;
- (void) setT:(id)newT { // expected-warning {{ivar 'tIvar' which backs the property is not referenced in this property's accessor}}
okIvar = newT;
}
@end