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:
parent
57243da70f
commit
5e3429c395
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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">,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue