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 24 using namespace clang; 25 using namespace ento; 26 27 namespace { 28 29 class 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 61 public: 62 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, 63 BugReporter &BR) const; 64 }; 65 66 static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD, 67 ObjCInterfaceDecl *InterD, 68 ASTContext &Ctx) { 69 // Check for synthesized ivars. 70 ObjCIvarDecl *ID = PD->getPropertyIvarDecl(); 71 if (ID) 72 return ID; 73 74 // Check for existing "_PropName". 75 ID = InterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx)); 76 if (ID) 77 return ID; 78 79 // Check for existing "PropName". 80 IdentifierInfo *PropIdent = PD->getIdentifier(); 81 ID = InterD->lookupInstanceVariable(PropIdent); 82 83 return ID; 84 } 85 86 void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D, 87 AnalysisManager& Mgr, 88 BugReporter &BR) const { 89 const ObjCInterfaceDecl *InterD = D->getClassInterface(); 90 91 92 IvarToPropertyMapTy IvarToPropMap; 93 94 // Find all properties for this class. 95 for (ObjCInterfaceDecl::prop_iterator I = InterD->prop_begin(), 96 E = InterD->prop_end(); I != E; ++I) { 97 ObjCPropertyDecl *PD = *I; 98 99 // Find the corresponding IVar. 100 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, 101 const_cast<ObjCInterfaceDecl*>(InterD), 102 Mgr.getASTContext()); 103 104 if (!ID) 105 continue; 106 107 // Store the IVar to property mapping. 108 IvarToPropMap[ID] = PD; 109 } 110 111 if (IvarToPropMap.empty()) 112 return; 113 114 for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), 115 E = D->instmeth_end(); I != E; ++I) { 116 117 ObjCMethodDecl *M = *I; 118 AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M); 119 120 if (M->getMethodFamily() == OMF_init || 121 M->getMethodFamily() == OMF_dealloc || 122 M->getSelector().getAsString().find("init") != StringRef::npos || 123 M->getSelector().getAsString().find("Init") != StringRef::npos) 124 continue; 125 126 const Stmt *Body = M->getBody(); 127 if (!Body) 128 continue; 129 130 MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, DCtx); 131 MC.VisitStmt(Body); 132 } 133 } 134 135 void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator( 136 const BinaryOperator *BO) { 137 if (!BO->isAssignmentOp()) 138 return; 139 140 const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(BO->getLHS()); 141 142 if (!IvarRef) 143 return; 144 145 if (const ObjCIvarDecl *D = IvarRef->getDecl()) { 146 IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D); 147 if (I != IvarToPropMap.end()) { 148 const ObjCPropertyDecl *PD = I->second; 149 150 ObjCMethodDecl *GetterMethod = 151 InterfD->getInstanceMethod(PD->getGetterName()); 152 ObjCMethodDecl *SetterMethod = 153 InterfD->getInstanceMethod(PD->getSetterName()); 154 155 if (SetterMethod && SetterMethod->getCanonicalDecl() == MD) 156 return; 157 158 if (GetterMethod && GetterMethod->getCanonicalDecl() == MD) 159 return; 160 161 162 PathDiagnosticLocation IvarRefLocation = 163 PathDiagnosticLocation::createBegin(IvarRef, 164 BR.getSourceManager(), DCtx); 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", IvarRefLocation); 171 } 172 } 173 } 174 } 175 176 void ento::registerDirectIvarAssignment(CheckerManager &mgr) { 177 mgr.registerChecker<DirectIvarAssignment>(); 178 } 179