DirectIvarAssignment.cpp revision 249423
1243791Sdim//=- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ ----*-==// 2243791Sdim// 3243791Sdim// The LLVM Compiler Infrastructure 4243791Sdim// 5243791Sdim// This file is distributed under the University of Illinois Open Source 6243791Sdim// License. See LICENSE.TXT for details. 7243791Sdim// 8243791Sdim//===----------------------------------------------------------------------===// 9243791Sdim// 10249423Sdim// Check that Objective C properties are set with the setter, not though a 11249423Sdim// direct assignment. 12243791Sdim// 13249423Sdim// Two versions of a checker exist: one that checks all methods and the other 14249423Sdim// that only checks the methods annotated with 15249423Sdim// __attribute__((annotate("objc_no_direct_instance_variable_assignment"))) 16249423Sdim// 17249423Sdim// The checker does not warn about assignments to Ivars, annotated with 18249423Sdim// __attribute__((objc_allow_direct_instance_variable_assignment"))). This 19249423Sdim// annotation serves as a false positive suppression mechanism for the 20249423Sdim// checker. The annotation is allowed on properties and Ivars. 21249423Sdim// 22243791Sdim//===----------------------------------------------------------------------===// 23243791Sdim 24243791Sdim#include "ClangSACheckers.h" 25249423Sdim#include "clang/AST/Attr.h" 26249423Sdim#include "clang/AST/DeclObjC.h" 27249423Sdim#include "clang/AST/StmtVisitor.h" 28249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 29243791Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 30243791Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 31243791Sdim#include "llvm/ADT/DenseMap.h" 32243791Sdim 33243791Sdimusing namespace clang; 34243791Sdimusing namespace ento; 35243791Sdim 36243791Sdimnamespace { 37243791Sdim 38249423Sdim/// The default method filter, which is used to filter out the methods on which 39249423Sdim/// the check should not be performed. 40249423Sdim/// 41249423Sdim/// Checks for the init, dealloc, and any other functions that might be allowed 42249423Sdim/// to perform direct instance variable assignment based on their name. 43249423Sdimstruct MethodFilter { 44249423Sdim virtual ~MethodFilter() {} 45249423Sdim virtual bool operator()(ObjCMethodDecl *M) { 46249423Sdim if (M->getMethodFamily() == OMF_init || 47249423Sdim M->getMethodFamily() == OMF_dealloc || 48249423Sdim M->getMethodFamily() == OMF_copy || 49249423Sdim M->getMethodFamily() == OMF_mutableCopy || 50249423Sdim M->getSelector().getNameForSlot(0).find("init") != StringRef::npos || 51249423Sdim M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos) 52249423Sdim return true; 53249423Sdim return false; 54249423Sdim } 55249423Sdim}; 56249423Sdim 57249423Sdimstatic MethodFilter DefaultMethodFilter; 58249423Sdim 59243791Sdimclass DirectIvarAssignment : 60243791Sdim public Checker<check::ASTDecl<ObjCImplementationDecl> > { 61243791Sdim 62243791Sdim typedef llvm::DenseMap<const ObjCIvarDecl*, 63243791Sdim const ObjCPropertyDecl*> IvarToPropertyMapTy; 64243791Sdim 65243791Sdim /// A helper class, which walks the AST and locates all assignments to ivars 66243791Sdim /// in the given function. 67243791Sdim class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { 68243791Sdim const IvarToPropertyMapTy &IvarToPropMap; 69243791Sdim const ObjCMethodDecl *MD; 70243791Sdim const ObjCInterfaceDecl *InterfD; 71243791Sdim BugReporter &BR; 72243791Sdim LocationOrAnalysisDeclContext DCtx; 73243791Sdim 74243791Sdim public: 75243791Sdim MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD, 76243791Sdim const ObjCInterfaceDecl *InID, 77243791Sdim BugReporter &InBR, AnalysisDeclContext *InDCtx) 78243791Sdim : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR), DCtx(InDCtx) {} 79243791Sdim 80243791Sdim void VisitStmt(const Stmt *S) { VisitChildren(S); } 81243791Sdim 82243791Sdim void VisitBinaryOperator(const BinaryOperator *BO); 83243791Sdim 84243791Sdim void VisitChildren(const Stmt *S) { 85243791Sdim for (Stmt::const_child_range I = S->children(); I; ++I) 86243791Sdim if (*I) 87243791Sdim this->Visit(*I); 88243791Sdim } 89243791Sdim }; 90243791Sdim 91243791Sdimpublic: 92249423Sdim MethodFilter *ShouldSkipMethod; 93249423Sdim 94249423Sdim DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {} 95249423Sdim 96243791Sdim void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, 97243791Sdim BugReporter &BR) const; 98243791Sdim}; 99243791Sdim 100243791Sdimstatic const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD, 101243791Sdim const ObjCInterfaceDecl *InterD, 102243791Sdim ASTContext &Ctx) { 103243791Sdim // Check for synthesized ivars. 104243791Sdim ObjCIvarDecl *ID = PD->getPropertyIvarDecl(); 105243791Sdim if (ID) 106243791Sdim return ID; 107243791Sdim 108243791Sdim ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD); 109243791Sdim 110243791Sdim // Check for existing "_PropName". 111243791Sdim ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx)); 112243791Sdim if (ID) 113243791Sdim return ID; 114243791Sdim 115243791Sdim // Check for existing "PropName". 116243791Sdim IdentifierInfo *PropIdent = PD->getIdentifier(); 117243791Sdim ID = NonConstInterD->lookupInstanceVariable(PropIdent); 118243791Sdim 119243791Sdim return ID; 120243791Sdim} 121243791Sdim 122243791Sdimvoid DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D, 123243791Sdim AnalysisManager& Mgr, 124243791Sdim BugReporter &BR) const { 125243791Sdim const ObjCInterfaceDecl *InterD = D->getClassInterface(); 126243791Sdim 127243791Sdim 128243791Sdim IvarToPropertyMapTy IvarToPropMap; 129243791Sdim 130243791Sdim // Find all properties for this class. 131243791Sdim for (ObjCInterfaceDecl::prop_iterator I = InterD->prop_begin(), 132243791Sdim E = InterD->prop_end(); I != E; ++I) { 133243791Sdim ObjCPropertyDecl *PD = *I; 134243791Sdim 135243791Sdim // Find the corresponding IVar. 136243791Sdim const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD, 137243791Sdim Mgr.getASTContext()); 138243791Sdim 139243791Sdim if (!ID) 140243791Sdim continue; 141243791Sdim 142243791Sdim // Store the IVar to property mapping. 143243791Sdim IvarToPropMap[ID] = PD; 144243791Sdim } 145243791Sdim 146243791Sdim if (IvarToPropMap.empty()) 147243791Sdim return; 148243791Sdim 149243791Sdim for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), 150243791Sdim E = D->instmeth_end(); I != E; ++I) { 151243791Sdim 152243791Sdim ObjCMethodDecl *M = *I; 153243791Sdim AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M); 154243791Sdim 155249423Sdim if ((*ShouldSkipMethod)(M)) 156243791Sdim continue; 157243791Sdim 158243791Sdim const Stmt *Body = M->getBody(); 159243791Sdim assert(Body); 160243791Sdim 161243791Sdim MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, DCtx); 162243791Sdim MC.VisitStmt(Body); 163243791Sdim } 164243791Sdim} 165243791Sdim 166249423Sdimstatic bool isAnnotatedToAllowDirectAssignment(const Decl *D) { 167249423Sdim for (specific_attr_iterator<AnnotateAttr> 168249423Sdim AI = D->specific_attr_begin<AnnotateAttr>(), 169249423Sdim AE = D->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) { 170249423Sdim const AnnotateAttr *Ann = *AI; 171249423Sdim if (Ann->getAnnotation() == 172249423Sdim "objc_allow_direct_instance_variable_assignment") 173249423Sdim return true; 174249423Sdim } 175249423Sdim return false; 176249423Sdim} 177249423Sdim 178243791Sdimvoid DirectIvarAssignment::MethodCrawler::VisitBinaryOperator( 179243791Sdim const BinaryOperator *BO) { 180243791Sdim if (!BO->isAssignmentOp()) 181243791Sdim return; 182243791Sdim 183243791Sdim const ObjCIvarRefExpr *IvarRef = 184243791Sdim dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts()); 185243791Sdim 186243791Sdim if (!IvarRef) 187243791Sdim return; 188243791Sdim 189243791Sdim if (const ObjCIvarDecl *D = IvarRef->getDecl()) { 190243791Sdim IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D); 191249423Sdim 192243791Sdim if (I != IvarToPropMap.end()) { 193243791Sdim const ObjCPropertyDecl *PD = I->second; 194249423Sdim // Skip warnings on Ivars, annotated with 195249423Sdim // objc_allow_direct_instance_variable_assignment. This annotation serves 196249423Sdim // as a false positive suppression mechanism for the checker. The 197249423Sdim // annotation is allowed on properties and ivars. 198249423Sdim if (isAnnotatedToAllowDirectAssignment(PD) || 199249423Sdim isAnnotatedToAllowDirectAssignment(D)) 200249423Sdim return; 201243791Sdim 202243791Sdim ObjCMethodDecl *GetterMethod = 203243791Sdim InterfD->getInstanceMethod(PD->getGetterName()); 204243791Sdim ObjCMethodDecl *SetterMethod = 205243791Sdim InterfD->getInstanceMethod(PD->getSetterName()); 206243791Sdim 207243791Sdim if (SetterMethod && SetterMethod->getCanonicalDecl() == MD) 208243791Sdim return; 209243791Sdim 210243791Sdim if (GetterMethod && GetterMethod->getCanonicalDecl() == MD) 211243791Sdim return; 212243791Sdim 213243791Sdim BR.EmitBasicReport(MD, 214243791Sdim "Property access", 215243791Sdim categories::CoreFoundationObjectiveC, 216243791Sdim "Direct assignment to an instance variable backing a property; " 217243791Sdim "use the setter instead", PathDiagnosticLocation(IvarRef, 218243791Sdim BR.getSourceManager(), 219243791Sdim DCtx)); 220243791Sdim } 221243791Sdim } 222243791Sdim} 223243791Sdim} 224243791Sdim 225249423Sdim// Register the checker that checks for direct accesses in all functions, 226249423Sdim// except for the initialization and copy routines. 227243791Sdimvoid ento::registerDirectIvarAssignment(CheckerManager &mgr) { 228243791Sdim mgr.registerChecker<DirectIvarAssignment>(); 229243791Sdim} 230249423Sdim 231249423Sdim// Register the checker that checks for direct accesses in functions annotated 232249423Sdim// with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))). 233249423Sdimnamespace { 234249423Sdimstruct InvalidatorMethodFilter : MethodFilter { 235249423Sdim virtual ~InvalidatorMethodFilter() {} 236249423Sdim virtual bool operator()(ObjCMethodDecl *M) { 237249423Sdim for (specific_attr_iterator<AnnotateAttr> 238249423Sdim AI = M->specific_attr_begin<AnnotateAttr>(), 239249423Sdim AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) { 240249423Sdim const AnnotateAttr *Ann = *AI; 241249423Sdim if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment") 242249423Sdim return false; 243249423Sdim } 244249423Sdim return true; 245249423Sdim } 246249423Sdim}; 247249423Sdim 248249423SdimInvalidatorMethodFilter AttrFilter; 249249423Sdim} 250249423Sdim 251249423Sdimvoid ento::registerDirectIvarAssignmentForAnnotatedFunctions( 252249423Sdim CheckerManager &mgr) { 253249423Sdim mgr.registerChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter; 254249423Sdim} 255