DirectIvarAssignment.cpp revision bf24792e00a47fd9d74ff21e21d2cbffc6d62818
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->getSelector().getNameForSlot(0).find("init") != StringRef::npos || 126 M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos) 127 continue; 128 129 const Stmt *Body = M->getBody(); 130 assert(Body); 131 132 MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, DCtx); 133 MC.VisitStmt(Body); 134 } 135} 136 137void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator( 138 const BinaryOperator *BO) { 139 if (!BO->isAssignmentOp()) 140 return; 141 142 const ObjCIvarRefExpr *IvarRef = 143 dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts()); 144 145 if (!IvarRef) 146 return; 147 148 if (const ObjCIvarDecl *D = IvarRef->getDecl()) { 149 IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D); 150 if (I != IvarToPropMap.end()) { 151 const ObjCPropertyDecl *PD = I->second; 152 153 ObjCMethodDecl *GetterMethod = 154 InterfD->getInstanceMethod(PD->getGetterName()); 155 ObjCMethodDecl *SetterMethod = 156 InterfD->getInstanceMethod(PD->getSetterName()); 157 158 if (SetterMethod && SetterMethod->getCanonicalDecl() == MD) 159 return; 160 161 if (GetterMethod && GetterMethod->getCanonicalDecl() == MD) 162 return; 163 164 BR.EmitBasicReport(MD, 165 "Property access", 166 categories::CoreFoundationObjectiveC, 167 "Direct assignment to an instance variable backing a property; " 168 "use the setter instead", PathDiagnosticLocation(IvarRef, 169 BR.getSourceManager(), 170 DCtx)); 171 } 172 } 173} 174} 175 176void ento::registerDirectIvarAssignment(CheckerManager &mgr) { 177 mgr.registerChecker<DirectIvarAssignment>(); 178} 179