Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
35266 views
//===- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ -*-==//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Check that Objective C properties are set with the setter, not though a9// direct assignment.10//11// Two versions of a checker exist: one that checks all methods and the other12// that only checks the methods annotated with13// __attribute__((annotate("objc_no_direct_instance_variable_assignment")))14//15// The checker does not warn about assignments to Ivars, annotated with16// __attribute__((objc_allow_direct_instance_variable_assignment"))). This17// annotation serves as a false positive suppression mechanism for the18// checker. The annotation is allowed on properties and Ivars.19//20//===----------------------------------------------------------------------===//2122#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"23#include "clang/AST/Attr.h"24#include "clang/AST/DeclObjC.h"25#include "clang/AST/StmtVisitor.h"26#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"27#include "clang/StaticAnalyzer/Core/Checker.h"28#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"29#include "llvm/ADT/DenseMap.h"3031using namespace clang;32using namespace ento;3334namespace {3536/// The default method filter, which is used to filter out the methods on which37/// the check should not be performed.38///39/// Checks for the init, dealloc, and any other functions that might be allowed40/// to perform direct instance variable assignment based on their name.41static bool DefaultMethodFilter(const ObjCMethodDecl *M) {42return M->getMethodFamily() == OMF_init ||43M->getMethodFamily() == OMF_dealloc ||44M->getMethodFamily() == OMF_copy ||45M->getMethodFamily() == OMF_mutableCopy ||46M->getSelector().getNameForSlot(0).contains("init") ||47M->getSelector().getNameForSlot(0).contains("Init");48}4950class DirectIvarAssignment :51public Checker<check::ASTDecl<ObjCImplementationDecl> > {5253typedef llvm::DenseMap<const ObjCIvarDecl*,54const ObjCPropertyDecl*> IvarToPropertyMapTy;5556/// A helper class, which walks the AST and locates all assignments to ivars57/// in the given function.58class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {59const IvarToPropertyMapTy &IvarToPropMap;60const ObjCMethodDecl *MD;61const ObjCInterfaceDecl *InterfD;62BugReporter &BR;63const CheckerBase *Checker;64LocationOrAnalysisDeclContext DCtx;6566public:67MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,68const ObjCInterfaceDecl *InID, BugReporter &InBR,69const CheckerBase *Checker, AnalysisDeclContext *InDCtx)70: IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR),71Checker(Checker), DCtx(InDCtx) {}7273void VisitStmt(const Stmt *S) { VisitChildren(S); }7475void VisitBinaryOperator(const BinaryOperator *BO);7677void VisitChildren(const Stmt *S) {78for (const Stmt *Child : S->children())79if (Child)80this->Visit(Child);81}82};8384public:85bool (*ShouldSkipMethod)(const ObjCMethodDecl *);8687DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {}8889void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,90BugReporter &BR) const;91};9293static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD,94const ObjCInterfaceDecl *InterD,95ASTContext &Ctx) {96// Check for synthesized ivars.97ObjCIvarDecl *ID = PD->getPropertyIvarDecl();98if (ID)99return ID;100101ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD);102103// Check for existing "_PropName".104ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx));105if (ID)106return ID;107108// Check for existing "PropName".109IdentifierInfo *PropIdent = PD->getIdentifier();110ID = NonConstInterD->lookupInstanceVariable(PropIdent);111112return ID;113}114115void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D,116AnalysisManager& Mgr,117BugReporter &BR) const {118const ObjCInterfaceDecl *InterD = D->getClassInterface();119120121IvarToPropertyMapTy IvarToPropMap;122123// Find all properties for this class.124for (const auto *PD : InterD->instance_properties()) {125// Find the corresponding IVar.126const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD,127Mgr.getASTContext());128129if (!ID)130continue;131132// Store the IVar to property mapping.133IvarToPropMap[ID] = PD;134}135136if (IvarToPropMap.empty())137return;138139for (const auto *M : D->instance_methods()) {140AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);141142if ((*ShouldSkipMethod)(M))143continue;144145const Stmt *Body = M->getBody();146if (M->isSynthesizedAccessorStub())147continue;148assert(Body);149150MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,151DCtx);152MC.VisitStmt(Body);153}154}155156static bool isAnnotatedToAllowDirectAssignment(const Decl *D) {157for (const auto *Ann : D->specific_attrs<AnnotateAttr>())158if (Ann->getAnnotation() ==159"objc_allow_direct_instance_variable_assignment")160return true;161return false;162}163164void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(165const BinaryOperator *BO) {166if (!BO->isAssignmentOp())167return;168169const ObjCIvarRefExpr *IvarRef =170dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts());171172if (!IvarRef)173return;174175if (const ObjCIvarDecl *D = IvarRef->getDecl()) {176IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D);177178if (I != IvarToPropMap.end()) {179const ObjCPropertyDecl *PD = I->second;180// Skip warnings on Ivars, annotated with181// objc_allow_direct_instance_variable_assignment. This annotation serves182// as a false positive suppression mechanism for the checker. The183// annotation is allowed on properties and ivars.184if (isAnnotatedToAllowDirectAssignment(PD) ||185isAnnotatedToAllowDirectAssignment(D))186return;187188ObjCMethodDecl *GetterMethod =189InterfD->getInstanceMethod(PD->getGetterName());190ObjCMethodDecl *SetterMethod =191InterfD->getInstanceMethod(PD->getSetterName());192193if (SetterMethod && SetterMethod->getCanonicalDecl() == MD)194return;195196if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)197return;198199BR.EmitBasicReport(200MD, Checker, "Property access", categories::CoreFoundationObjectiveC,201"Direct assignment to an instance variable backing a property; "202"use the setter instead",203PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx));204}205}206}207}208209// Register the checker that checks for direct accesses in functions annotated210// with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))).211static bool AttrFilter(const ObjCMethodDecl *M) {212for (const auto *Ann : M->specific_attrs<AnnotateAttr>())213if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment")214return false;215return true;216}217218// Register the checker that checks for direct accesses in all functions,219// except for the initialization and copy routines.220void ento::registerDirectIvarAssignment(CheckerManager &mgr) {221auto Chk = mgr.registerChecker<DirectIvarAssignment>();222if (mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk,223"AnnotatedFunctions"))224Chk->ShouldSkipMethod = &AttrFilter;225}226227bool ento::shouldRegisterDirectIvarAssignment(const CheckerManager &mgr) {228return true;229}230231232