123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- //=- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ ----*-==//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // Check that Objective C properties are set with the setter, not though a
- // direct assignment.
- //
- // Two versions of a checker exist: one that checks all methods and the other
- // that only checks the methods annotated with
- // __attribute__((annotate("objc_no_direct_instance_variable_assignment")))
- //
- // The checker does not warn about assignments to Ivars, annotated with
- // __attribute__((objc_allow_direct_instance_variable_assignment"))). This
- // annotation serves as a false positive suppression mechanism for the
- // checker. The annotation is allowed on properties and Ivars.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
- #include "clang/AST/Attr.h"
- #include "clang/AST/DeclObjC.h"
- #include "clang/AST/StmtVisitor.h"
- #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
- #include "clang/StaticAnalyzer/Core/Checker.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
- #include "llvm/ADT/DenseMap.h"
- using namespace clang;
- using namespace ento;
- namespace {
- /// The default method filter, which is used to filter out the methods on which
- /// the check should not be performed.
- ///
- /// Checks for the init, dealloc, and any other functions that might be allowed
- /// to perform direct instance variable assignment based on their name.
- static bool DefaultMethodFilter(const ObjCMethodDecl *M) {
- return M->getMethodFamily() == OMF_init ||
- M->getMethodFamily() == OMF_dealloc ||
- M->getMethodFamily() == OMF_copy ||
- M->getMethodFamily() == OMF_mutableCopy ||
- M->getSelector().getNameForSlot(0).contains("init") ||
- M->getSelector().getNameForSlot(0).contains("Init");
- }
- class DirectIvarAssignment :
- public Checker<check::ASTDecl<ObjCImplementationDecl> > {
- typedef llvm::DenseMap<const ObjCIvarDecl*,
- const ObjCPropertyDecl*> IvarToPropertyMapTy;
- /// A helper class, which walks the AST and locates all assignments to ivars
- /// in the given function.
- class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
- const IvarToPropertyMapTy &IvarToPropMap;
- const ObjCMethodDecl *MD;
- const ObjCInterfaceDecl *InterfD;
- BugReporter &BR;
- const CheckerBase *Checker;
- LocationOrAnalysisDeclContext DCtx;
- public:
- MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,
- const ObjCInterfaceDecl *InID, BugReporter &InBR,
- const CheckerBase *Checker, AnalysisDeclContext *InDCtx)
- : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR),
- Checker(Checker), DCtx(InDCtx) {}
- void VisitStmt(const Stmt *S) { VisitChildren(S); }
- void VisitBinaryOperator(const BinaryOperator *BO);
- void VisitChildren(const Stmt *S) {
- for (const Stmt *Child : S->children())
- if (Child)
- this->Visit(Child);
- }
- };
- public:
- bool (*ShouldSkipMethod)(const ObjCMethodDecl *);
- DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {}
- void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
- BugReporter &BR) const;
- };
- static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD,
- const ObjCInterfaceDecl *InterD,
- ASTContext &Ctx) {
- // Check for synthesized ivars.
- ObjCIvarDecl *ID = PD->getPropertyIvarDecl();
- if (ID)
- return ID;
- ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD);
- // Check for existing "_PropName".
- ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx));
- if (ID)
- return ID;
- // Check for existing "PropName".
- IdentifierInfo *PropIdent = PD->getIdentifier();
- ID = NonConstInterD->lookupInstanceVariable(PropIdent);
- return ID;
- }
- void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D,
- AnalysisManager& Mgr,
- BugReporter &BR) const {
- const ObjCInterfaceDecl *InterD = D->getClassInterface();
- IvarToPropertyMapTy IvarToPropMap;
- // Find all properties for this class.
- for (const auto *PD : InterD->instance_properties()) {
- // Find the corresponding IVar.
- const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD,
- Mgr.getASTContext());
- if (!ID)
- continue;
- // Store the IVar to property mapping.
- IvarToPropMap[ID] = PD;
- }
- if (IvarToPropMap.empty())
- return;
- for (const auto *M : D->instance_methods()) {
- AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);
- if ((*ShouldSkipMethod)(M))
- continue;
- const Stmt *Body = M->getBody();
- if (M->isSynthesizedAccessorStub())
- continue;
- assert(Body);
- MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,
- DCtx);
- MC.VisitStmt(Body);
- }
- }
- static bool isAnnotatedToAllowDirectAssignment(const Decl *D) {
- for (const auto *Ann : D->specific_attrs<AnnotateAttr>())
- if (Ann->getAnnotation() ==
- "objc_allow_direct_instance_variable_assignment")
- return true;
- return false;
- }
- void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(
- const BinaryOperator *BO) {
- if (!BO->isAssignmentOp())
- return;
- const ObjCIvarRefExpr *IvarRef =
- dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts());
- if (!IvarRef)
- return;
- if (const ObjCIvarDecl *D = IvarRef->getDecl()) {
- IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D);
- if (I != IvarToPropMap.end()) {
- const ObjCPropertyDecl *PD = I->second;
- // Skip warnings on Ivars, annotated with
- // objc_allow_direct_instance_variable_assignment. This annotation serves
- // as a false positive suppression mechanism for the checker. The
- // annotation is allowed on properties and ivars.
- if (isAnnotatedToAllowDirectAssignment(PD) ||
- isAnnotatedToAllowDirectAssignment(D))
- return;
- ObjCMethodDecl *GetterMethod =
- InterfD->getInstanceMethod(PD->getGetterName());
- ObjCMethodDecl *SetterMethod =
- InterfD->getInstanceMethod(PD->getSetterName());
- if (SetterMethod && SetterMethod->getCanonicalDecl() == MD)
- return;
- if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)
- return;
- BR.EmitBasicReport(
- MD, Checker, "Property access", categories::CoreFoundationObjectiveC,
- "Direct assignment to an instance variable backing a property; "
- "use the setter instead",
- PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx));
- }
- }
- }
- }
- // Register the checker that checks for direct accesses in functions annotated
- // with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))).
- static bool AttrFilter(const ObjCMethodDecl *M) {
- for (const auto *Ann : M->specific_attrs<AnnotateAttr>())
- if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment")
- return false;
- return true;
- }
- // Register the checker that checks for direct accesses in all functions,
- // except for the initialization and copy routines.
- void ento::registerDirectIvarAssignment(CheckerManager &mgr) {
- auto Chk = mgr.registerChecker<DirectIvarAssignment>();
- if (mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk,
- "AnnotatedFunctions"))
- Chk->ShouldSkipMethod = &AttrFilter;
- }
- bool ento::shouldRegisterDirectIvarAssignment(const CheckerManager &mgr) {
- return true;
- }
|