clang API Documentation
00001 //=- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ ----*-==// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // Check that Objective C properties are set with the setter, not though a 00011 // direct assignment. 00012 // 00013 // Two versions of a checker exist: one that checks all methods and the other 00014 // that only checks the methods annotated with 00015 // __attribute__((annotate("objc_no_direct_instance_variable_assignment"))) 00016 // 00017 // The checker does not warn about assignments to Ivars, annotated with 00018 // __attribute__((objc_allow_direct_instance_variable_assignment"))). This 00019 // annotation serves as a false positive suppression mechanism for the 00020 // checker. The annotation is allowed on properties and Ivars. 00021 // 00022 //===----------------------------------------------------------------------===// 00023 00024 #include "ClangSACheckers.h" 00025 #include "clang/AST/Attr.h" 00026 #include "clang/AST/DeclObjC.h" 00027 #include "clang/AST/StmtVisitor.h" 00028 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 00029 #include "clang/StaticAnalyzer/Core/Checker.h" 00030 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 00031 #include "llvm/ADT/DenseMap.h" 00032 00033 using namespace clang; 00034 using namespace ento; 00035 00036 namespace { 00037 00038 /// The default method filter, which is used to filter out the methods on which 00039 /// the check should not be performed. 00040 /// 00041 /// Checks for the init, dealloc, and any other functions that might be allowed 00042 /// to perform direct instance variable assignment based on their name. 00043 static bool DefaultMethodFilter(const ObjCMethodDecl *M) { 00044 if (M->getMethodFamily() == OMF_init || M->getMethodFamily() == OMF_dealloc || 00045 M->getMethodFamily() == OMF_copy || 00046 M->getMethodFamily() == OMF_mutableCopy || 00047 M->getSelector().getNameForSlot(0).find("init") != StringRef::npos || 00048 M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos) 00049 return true; 00050 return false; 00051 } 00052 00053 class DirectIvarAssignment : 00054 public Checker<check::ASTDecl<ObjCImplementationDecl> > { 00055 00056 typedef llvm::DenseMap<const ObjCIvarDecl*, 00057 const ObjCPropertyDecl*> IvarToPropertyMapTy; 00058 00059 /// A helper class, which walks the AST and locates all assignments to ivars 00060 /// in the given function. 00061 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { 00062 const IvarToPropertyMapTy &IvarToPropMap; 00063 const ObjCMethodDecl *MD; 00064 const ObjCInterfaceDecl *InterfD; 00065 BugReporter &BR; 00066 const CheckerBase *Checker; 00067 LocationOrAnalysisDeclContext DCtx; 00068 00069 public: 00070 MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD, 00071 const ObjCInterfaceDecl *InID, BugReporter &InBR, 00072 const CheckerBase *Checker, AnalysisDeclContext *InDCtx) 00073 : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR), 00074 Checker(Checker), DCtx(InDCtx) {} 00075 00076 void VisitStmt(const Stmt *S) { VisitChildren(S); } 00077 00078 void VisitBinaryOperator(const BinaryOperator *BO); 00079 00080 void VisitChildren(const Stmt *S) { 00081 for (Stmt::const_child_range I = S->children(); I; ++I) 00082 if (*I) 00083 this->Visit(*I); 00084 } 00085 }; 00086 00087 public: 00088 bool (*ShouldSkipMethod)(const ObjCMethodDecl *); 00089 00090 DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {} 00091 00092 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, 00093 BugReporter &BR) const; 00094 }; 00095 00096 static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD, 00097 const ObjCInterfaceDecl *InterD, 00098 ASTContext &Ctx) { 00099 // Check for synthesized ivars. 00100 ObjCIvarDecl *ID = PD->getPropertyIvarDecl(); 00101 if (ID) 00102 return ID; 00103 00104 ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD); 00105 00106 // Check for existing "_PropName". 00107 ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx)); 00108 if (ID) 00109 return ID; 00110 00111 // Check for existing "PropName". 00112 IdentifierInfo *PropIdent = PD->getIdentifier(); 00113 ID = NonConstInterD->lookupInstanceVariable(PropIdent); 00114 00115 return ID; 00116 } 00117 00118 void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D, 00119 AnalysisManager& Mgr, 00120 BugReporter &BR) const { 00121 const ObjCInterfaceDecl *InterD = D->getClassInterface(); 00122 00123 00124 IvarToPropertyMapTy IvarToPropMap; 00125 00126 // Find all properties for this class. 00127 for (const auto *PD : InterD->properties()) { 00128 // Find the corresponding IVar. 00129 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD, 00130 Mgr.getASTContext()); 00131 00132 if (!ID) 00133 continue; 00134 00135 // Store the IVar to property mapping. 00136 IvarToPropMap[ID] = PD; 00137 } 00138 00139 if (IvarToPropMap.empty()) 00140 return; 00141 00142 for (const auto *M : D->instance_methods()) { 00143 AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M); 00144 00145 if ((*ShouldSkipMethod)(M)) 00146 continue; 00147 00148 const Stmt *Body = M->getBody(); 00149 assert(Body); 00150 00151 MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this, 00152 DCtx); 00153 MC.VisitStmt(Body); 00154 } 00155 } 00156 00157 static bool isAnnotatedToAllowDirectAssignment(const Decl *D) { 00158 for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) 00159 if (Ann->getAnnotation() == 00160 "objc_allow_direct_instance_variable_assignment") 00161 return true; 00162 return false; 00163 } 00164 00165 void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator( 00166 const BinaryOperator *BO) { 00167 if (!BO->isAssignmentOp()) 00168 return; 00169 00170 const ObjCIvarRefExpr *IvarRef = 00171 dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts()); 00172 00173 if (!IvarRef) 00174 return; 00175 00176 if (const ObjCIvarDecl *D = IvarRef->getDecl()) { 00177 IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D); 00178 00179 if (I != IvarToPropMap.end()) { 00180 const ObjCPropertyDecl *PD = I->second; 00181 // Skip warnings on Ivars, annotated with 00182 // objc_allow_direct_instance_variable_assignment. This annotation serves 00183 // as a false positive suppression mechanism for the checker. The 00184 // annotation is allowed on properties and ivars. 00185 if (isAnnotatedToAllowDirectAssignment(PD) || 00186 isAnnotatedToAllowDirectAssignment(D)) 00187 return; 00188 00189 ObjCMethodDecl *GetterMethod = 00190 InterfD->getInstanceMethod(PD->getGetterName()); 00191 ObjCMethodDecl *SetterMethod = 00192 InterfD->getInstanceMethod(PD->getSetterName()); 00193 00194 if (SetterMethod && SetterMethod->getCanonicalDecl() == MD) 00195 return; 00196 00197 if (GetterMethod && GetterMethod->getCanonicalDecl() == MD) 00198 return; 00199 00200 BR.EmitBasicReport( 00201 MD, Checker, "Property access", categories::CoreFoundationObjectiveC, 00202 "Direct assignment to an instance variable backing a property; " 00203 "use the setter instead", 00204 PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx)); 00205 } 00206 } 00207 } 00208 } 00209 00210 // Register the checker that checks for direct accesses in all functions, 00211 // except for the initialization and copy routines. 00212 void ento::registerDirectIvarAssignment(CheckerManager &mgr) { 00213 mgr.registerChecker<DirectIvarAssignment>(); 00214 } 00215 00216 // Register the checker that checks for direct accesses in functions annotated 00217 // with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))). 00218 static bool AttrFilter(const ObjCMethodDecl *M) { 00219 for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) 00220 if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment") 00221 return false; 00222 return true; 00223 } 00224 00225 void ento::registerDirectIvarAssignmentForAnnotatedFunctions( 00226 CheckerManager &mgr) { 00227 mgr.registerChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter; 00228 }