DirectIvarAssignment.cpp revision 625ce084bc8de75e74b8920593ab761f20ff5971
1//=- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ ----*-==// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// Check that Objective C properties follow the following rules: 11// - The property should be set with the setter, not though a direct 12// assignment. 13// 14//===----------------------------------------------------------------------===// 15 16#include "ClangSACheckers.h" 17#include "clang/StaticAnalyzer/Core/Checker.h" 18#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 20#include "clang/AST/DeclObjC.h" 21#include "clang/AST/StmtVisitor.h" 22#include "llvm/ADT/DenseMap.h" 23 24using namespace clang; 25using namespace ento; 26 27namespace { 28 29class DirectIvarAssignment : 30 public Checker<check::ASTDecl<ObjCImplementationDecl> > { 31 32 typedef llvm::DenseMap<const ObjCIvarDecl*, 33 const ObjCPropertyDecl*> IvarToPropertyMapTy; 34 35 /// A helper class, which walks the AST and locates all assignments to ivars 36 /// in the given function. 37 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { 38 const IvarToPropertyMapTy &IvarToPropMap; 39 const ObjCMethodDecl *MD; 40 const ObjCInterfaceDecl *InterfD; 41 BugReporter &BR; 42 LocationOrAnalysisDeclContext DCtx; 43 44 public: 45 MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD, 46 const ObjCInterfaceDecl *InID, 47 BugReporter &InBR, AnalysisDeclContext *InDCtx) 48 : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR), DCtx(InDCtx) {} 49 50 void VisitStmt(const Stmt *S) { VisitChildren(S); } 51 52 void VisitBinaryOperator(const BinaryOperator *BO); 53 54 void VisitChildren(const Stmt *S) { 55 for (Stmt::const_child_range I = S->children(); I; ++I) 56 if (*I) 57 this->Visit(*I); 58 } 59 }; 60 61public: 62 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, 63 BugReporter &BR) const; 64}; 65 66static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD, 67 const ObjCInterfaceDecl *InterD, 68 ASTContext &Ctx) { 69 // Check for synthesized ivars. 70 ObjCIvarDecl *ID = PD->getPropertyIvarDecl(); 71 if (ID) 72 return ID; 73 74 ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD); 75 76 // Check for existing "_PropName". 77 ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx)); 78 if (ID) 79 return ID; 80 81 // Check for existing "PropName". 82 IdentifierInfo *PropIdent = PD->getIdentifier(); 83 ID = NonConstInterD->lookupInstanceVariable(PropIdent); 84 85 return ID; 86} 87 88void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D, 89 AnalysisManager& Mgr, 90 BugReporter &BR) const { 91 const ObjCInterfaceDecl *InterD = D->getClassInterface(); 92 93 94 IvarToPropertyMapTy IvarToPropMap; 95 96 // Find all properties for this class. 97 for (ObjCInterfaceDecl::prop_iterator I = InterD->prop_begin(), 98 E = InterD->prop_end(); I != E; ++I) { 99 ObjCPropertyDecl *PD = *I; 100 101 // Find the corresponding IVar. 102 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD, 103 Mgr.getASTContext()); 104 105 if (!ID) 106 continue; 107 108 // Store the IVar to property mapping. 109 IvarToPropMap[ID] = PD; 110 } 111 112 if (IvarToPropMap.empty()) 113 return; 114 115 for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), 116 E = D->instmeth_end(); I != E; ++I) { 117 118 ObjCMethodDecl *M = *I; 119 AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M); 120 121 // Skip the init, dealloc functions and any functions that might be doing 122 // initialization based on their name. 123 if (M->getMethodFamily() == OMF_init || 124 M->getMethodFamily() == OMF_dealloc || 125 M->getMethodFamily() == OMF_copy || 126 M->getMethodFamily() == OMF_mutableCopy || 127 M->getSelector().getNameForSlot(0).find("init") != StringRef::npos || 128 M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos) 129 continue; 130 131 const Stmt *Body = M->getBody(); 132 assert(Body); 133 134 MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, DCtx); 135 MC.VisitStmt(Body); 136 } 137} 138 139void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator( 140 const BinaryOperator *BO) { 141 if (!BO->isAssignmentOp()) 142 return; 143 144 const ObjCIvarRefExpr *IvarRef = 145 dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts()); 146 147 if (!IvarRef) 148 return; 149 150 if (const ObjCIvarDecl *D = IvarRef->getDecl()) { 151 IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D); 152 if (I != IvarToPropMap.end()) { 153 const ObjCPropertyDecl *PD = I->second; 154 155 ObjCMethodDecl *GetterMethod = 156 InterfD->getInstanceMethod(PD->getGetterName()); 157 ObjCMethodDecl *SetterMethod = 158 InterfD->getInstanceMethod(PD->getSetterName()); 159 160 if (SetterMethod && SetterMethod->getCanonicalDecl() == MD) 161 return; 162 163 if (GetterMethod && GetterMethod->getCanonicalDecl() == MD) 164 return; 165 166 BR.EmitBasicReport(MD, 167 "Property access", 168 categories::CoreFoundationObjectiveC, 169 "Direct assignment to an instance variable backing a property; " 170 "use the setter instead", PathDiagnosticLocation(IvarRef, 171 BR.getSourceManager(), 172 DCtx)); 173 } 174 } 175} 176} 177 178void ento::registerDirectIvarAssignment(CheckerManager &mgr) { 179 mgr.registerChecker<DirectIvarAssignment>(); 180} 181