brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.0 KiB · fc174e2 Raw
231 lines · cpp
1//===- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ -*-==//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9//  Check that Objective C properties are set with the setter, not though a10//      direct assignment.11//12//  Two versions of a checker exist: one that checks all methods and the other13//      that only checks the methods annotated with14//      __attribute__((annotate("objc_no_direct_instance_variable_assignment")))15//16//  The checker does not warn about assignments to Ivars, annotated with17//       __attribute__((objc_allow_direct_instance_variable_assignment"))). This18//      annotation serves as a false positive suppression mechanism for the19//      checker. The annotation is allowed on properties and Ivars.20//21//===----------------------------------------------------------------------===//22 23#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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 32using namespace clang;33using namespace ento;34 35namespace {36 37/// The default method filter, which is used to filter out the methods on which38/// the check should not be performed.39///40/// Checks for the init, dealloc, and any other functions that might be allowed41/// to perform direct instance variable assignment based on their name.42static bool DefaultMethodFilter(const ObjCMethodDecl *M) {43  return M->getMethodFamily() == OMF_init ||44         M->getMethodFamily() == OMF_dealloc ||45         M->getMethodFamily() == OMF_copy ||46         M->getMethodFamily() == OMF_mutableCopy ||47         M->getSelector().getNameForSlot(0).contains("init") ||48         M->getSelector().getNameForSlot(0).contains("Init");49}50 51class DirectIvarAssignment :52  public Checker<check::ASTDecl<ObjCImplementationDecl> > {53 54  typedef llvm::DenseMap<const ObjCIvarDecl*,55                         const ObjCPropertyDecl*> IvarToPropertyMapTy;56 57  /// A helper class, which walks the AST and locates all assignments to ivars58  /// in the given function.59  class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {60    const IvarToPropertyMapTy &IvarToPropMap;61    const ObjCMethodDecl *MD;62    const ObjCInterfaceDecl *InterfD;63    BugReporter &BR;64    const CheckerBase *Checker;65    LocationOrAnalysisDeclContext DCtx;66 67  public:68    MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,69                  const ObjCInterfaceDecl *InID, BugReporter &InBR,70                  const CheckerBase *Checker, AnalysisDeclContext *InDCtx)71        : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR),72          Checker(Checker), DCtx(InDCtx) {}73 74    void VisitStmt(const Stmt *S) { VisitChildren(S); }75 76    void VisitBinaryOperator(const BinaryOperator *BO);77 78    void VisitChildren(const Stmt *S) {79      for (const Stmt *Child : S->children())80        if (Child)81          this->Visit(Child);82    }83  };84 85public:86  bool (*ShouldSkipMethod)(const ObjCMethodDecl *);87 88  DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {}89 90  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,91                    BugReporter &BR) const;92};93 94static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD,95                                               const ObjCInterfaceDecl *InterD,96                                               ASTContext &Ctx) {97  // Check for synthesized ivars.98  ObjCIvarDecl *ID = PD->getPropertyIvarDecl();99  if (ID)100    return ID;101 102  ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD);103 104  // Check for existing "_PropName".105  ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx));106  if (ID)107    return ID;108 109  // Check for existing "PropName".110  IdentifierInfo *PropIdent = PD->getIdentifier();111  ID = NonConstInterD->lookupInstanceVariable(PropIdent);112 113  return ID;114}115 116void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D,117                                       AnalysisManager& Mgr,118                                       BugReporter &BR) const {119  const ObjCInterfaceDecl *InterD = D->getClassInterface();120 121 122  IvarToPropertyMapTy IvarToPropMap;123 124  // Find all properties for this class.125  for (const auto *PD : InterD->instance_properties()) {126    // Find the corresponding IVar.127    const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD,128                                                     Mgr.getASTContext());129 130    if (!ID)131      continue;132 133    // Store the IVar to property mapping.134    IvarToPropMap[ID] = PD;135  }136 137  if (IvarToPropMap.empty())138    return;139 140  for (const auto *M : D->instance_methods()) {141    AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);142 143    if ((*ShouldSkipMethod)(M))144      continue;145 146    const Stmt *Body = M->getBody();147    if (M->isSynthesizedAccessorStub())148      continue;149    assert(Body);150 151    MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,152                     DCtx);153    MC.VisitStmt(Body);154  }155}156 157static bool isAnnotatedToAllowDirectAssignment(const Decl *D) {158  for (const auto *Ann : D->specific_attrs<AnnotateAttr>())159    if (Ann->getAnnotation() ==160        "objc_allow_direct_instance_variable_assignment")161      return true;162  return false;163}164 165void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(166                                                    const BinaryOperator *BO) {167  if (!BO->isAssignmentOp())168    return;169 170  const ObjCIvarRefExpr *IvarRef =171          dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts());172 173  if (!IvarRef)174    return;175 176  if (const ObjCIvarDecl *D = IvarRef->getDecl()) {177    IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D);178 179    if (I != IvarToPropMap.end()) {180      const ObjCPropertyDecl *PD = I->second;181      // Skip warnings on Ivars, annotated with182      // objc_allow_direct_instance_variable_assignment. This annotation serves183      // as a false positive suppression mechanism for the checker. The184      // annotation is allowed on properties and ivars.185      if (isAnnotatedToAllowDirectAssignment(PD) ||186          isAnnotatedToAllowDirectAssignment(D))187        return;188 189      ObjCMethodDecl *GetterMethod =190          InterfD->getInstanceMethod(PD->getGetterName());191      ObjCMethodDecl *SetterMethod =192          InterfD->getInstanceMethod(PD->getSetterName());193 194      if (SetterMethod && SetterMethod->getCanonicalDecl() == MD)195        return;196 197      if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)198        return;199 200      BR.EmitBasicReport(201          MD, Checker, "Property access", categories::CoreFoundationObjectiveC,202          "Direct assignment to an instance variable backing a property; "203          "use the setter instead",204          PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx));205    }206  }207}208}209 210// Register the checker that checks for direct accesses in functions annotated211// with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))).212static bool AttrFilter(const ObjCMethodDecl *M) {213  for (const auto *Ann : M->specific_attrs<AnnotateAttr>())214    if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment")215      return false;216  return true;217}218 219// Register the checker that checks for direct accesses in all functions,220// except for the initialization and copy routines.221void ento::registerDirectIvarAssignment(CheckerManager &mgr) {222  auto Chk = mgr.registerChecker<DirectIvarAssignment>();223  if (mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk,224                                                       "AnnotatedFunctions"))225    Chk->ShouldSkipMethod = &AttrFilter;226}227 228bool ento::shouldRegisterDirectIvarAssignment(const CheckerManager &mgr) {229  return true;230}231