DirectIvarAssignment.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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 are set with the setter, not though a
  11. // direct assignment.
  12. //
  13. // Two versions of a checker exist: one that checks all methods and the other
  14. // that only checks the methods annotated with
  15. // __attribute__((annotate("objc_no_direct_instance_variable_assignment")))
  16. //
  17. // The checker does not warn about assignments to Ivars, annotated with
  18. // __attribute__((objc_allow_direct_instance_variable_assignment"))). This
  19. // annotation serves as a false positive suppression mechanism for the
  20. // checker. The annotation is allowed on properties and Ivars.
  21. //
  22. //===----------------------------------------------------------------------===//
  23. #include "ClangSACheckers.h"
  24. #include "clang/AST/Attr.h"
  25. #include "clang/AST/DeclObjC.h"
  26. #include "clang/AST/StmtVisitor.h"
  27. #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
  28. #include "clang/StaticAnalyzer/Core/Checker.h"
  29. #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
  30. #include "llvm/ADT/DenseMap.h"
  31. using namespace clang;
  32. using namespace ento;
  33. namespace {
  34. /// The default method filter, which is used to filter out the methods on which
  35. /// the check should not be performed.
  36. ///
  37. /// Checks for the init, dealloc, and any other functions that might be allowed
  38. /// to perform direct instance variable assignment based on their name.
  39. static bool DefaultMethodFilter(const ObjCMethodDecl *M) {
  40. if (M->getMethodFamily() == OMF_init || M->getMethodFamily() == OMF_dealloc ||
  41. M->getMethodFamily() == OMF_copy ||
  42. M->getMethodFamily() == OMF_mutableCopy ||
  43. M->getSelector().getNameForSlot(0).find("init") != StringRef::npos ||
  44. M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos)
  45. return true;
  46. return false;
  47. }
  48. class DirectIvarAssignment :
  49. public Checker<check::ASTDecl<ObjCImplementationDecl> > {
  50. typedef llvm::DenseMap<const ObjCIvarDecl*,
  51. const ObjCPropertyDecl*> IvarToPropertyMapTy;
  52. /// A helper class, which walks the AST and locates all assignments to ivars
  53. /// in the given function.
  54. class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
  55. const IvarToPropertyMapTy &IvarToPropMap;
  56. const ObjCMethodDecl *MD;
  57. const ObjCInterfaceDecl *InterfD;
  58. BugReporter &BR;
  59. const CheckerBase *Checker;
  60. LocationOrAnalysisDeclContext DCtx;
  61. public:
  62. MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,
  63. const ObjCInterfaceDecl *InID, BugReporter &InBR,
  64. const CheckerBase *Checker, AnalysisDeclContext *InDCtx)
  65. : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR),
  66. Checker(Checker), DCtx(InDCtx) {}
  67. void VisitStmt(const Stmt *S) { VisitChildren(S); }
  68. void VisitBinaryOperator(const BinaryOperator *BO);
  69. void VisitChildren(const Stmt *S) {
  70. for (const Stmt *Child : S->children())
  71. if (Child)
  72. this->Visit(Child);
  73. }
  74. };
  75. public:
  76. bool (*ShouldSkipMethod)(const ObjCMethodDecl *);
  77. DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {}
  78. void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
  79. BugReporter &BR) const;
  80. };
  81. static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD,
  82. const ObjCInterfaceDecl *InterD,
  83. ASTContext &Ctx) {
  84. // Check for synthesized ivars.
  85. ObjCIvarDecl *ID = PD->getPropertyIvarDecl();
  86. if (ID)
  87. return ID;
  88. ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD);
  89. // Check for existing "_PropName".
  90. ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx));
  91. if (ID)
  92. return ID;
  93. // Check for existing "PropName".
  94. IdentifierInfo *PropIdent = PD->getIdentifier();
  95. ID = NonConstInterD->lookupInstanceVariable(PropIdent);
  96. return ID;
  97. }
  98. void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D,
  99. AnalysisManager& Mgr,
  100. BugReporter &BR) const {
  101. const ObjCInterfaceDecl *InterD = D->getClassInterface();
  102. IvarToPropertyMapTy IvarToPropMap;
  103. // Find all properties for this class.
  104. for (const auto *PD : InterD->properties()) {
  105. // Find the corresponding IVar.
  106. const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD,
  107. Mgr.getASTContext());
  108. if (!ID)
  109. continue;
  110. // Store the IVar to property mapping.
  111. IvarToPropMap[ID] = PD;
  112. }
  113. if (IvarToPropMap.empty())
  114. return;
  115. for (const auto *M : D->instance_methods()) {
  116. AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);
  117. if ((*ShouldSkipMethod)(M))
  118. continue;
  119. const Stmt *Body = M->getBody();
  120. assert(Body);
  121. MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,
  122. DCtx);
  123. MC.VisitStmt(Body);
  124. }
  125. }
  126. static bool isAnnotatedToAllowDirectAssignment(const Decl *D) {
  127. for (const auto *Ann : D->specific_attrs<AnnotateAttr>())
  128. if (Ann->getAnnotation() ==
  129. "objc_allow_direct_instance_variable_assignment")
  130. return true;
  131. return false;
  132. }
  133. void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(
  134. const BinaryOperator *BO) {
  135. if (!BO->isAssignmentOp())
  136. return;
  137. const ObjCIvarRefExpr *IvarRef =
  138. dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts());
  139. if (!IvarRef)
  140. return;
  141. if (const ObjCIvarDecl *D = IvarRef->getDecl()) {
  142. IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D);
  143. if (I != IvarToPropMap.end()) {
  144. const ObjCPropertyDecl *PD = I->second;
  145. // Skip warnings on Ivars, annotated with
  146. // objc_allow_direct_instance_variable_assignment. This annotation serves
  147. // as a false positive suppression mechanism for the checker. The
  148. // annotation is allowed on properties and ivars.
  149. if (isAnnotatedToAllowDirectAssignment(PD) ||
  150. isAnnotatedToAllowDirectAssignment(D))
  151. return;
  152. ObjCMethodDecl *GetterMethod =
  153. InterfD->getInstanceMethod(PD->getGetterName());
  154. ObjCMethodDecl *SetterMethod =
  155. InterfD->getInstanceMethod(PD->getSetterName());
  156. if (SetterMethod && SetterMethod->getCanonicalDecl() == MD)
  157. return;
  158. if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)
  159. return;
  160. BR.EmitBasicReport(
  161. MD, Checker, "Property access", categories::CoreFoundationObjectiveC,
  162. "Direct assignment to an instance variable backing a property; "
  163. "use the setter instead",
  164. PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx));
  165. }
  166. }
  167. }
  168. }
  169. // Register the checker that checks for direct accesses in all functions,
  170. // except for the initialization and copy routines.
  171. void ento::registerDirectIvarAssignment(CheckerManager &mgr) {
  172. mgr.registerChecker<DirectIvarAssignment>();
  173. }
  174. // Register the checker that checks for direct accesses in functions annotated
  175. // with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))).
  176. static bool AttrFilter(const ObjCMethodDecl *M) {
  177. for (const auto *Ann : M->specific_attrs<AnnotateAttr>())
  178. if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment")
  179. return false;
  180. return true;
  181. }
  182. void ento::registerDirectIvarAssignmentForAnnotatedFunctions(
  183. CheckerManager &mgr) {
  184. mgr.registerChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter;
  185. }