clang 17.0.0git
DirectIvarAssignment.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Check that Objective C properties are set with the setter, not though a
10// direct assignment.
11//
12// Two versions of a checker exist: one that checks all methods and the other
13// that only checks the methods annotated with
14// __attribute__((annotate("objc_no_direct_instance_variable_assignment")))
15//
16// The checker does not warn about assignments to Ivars, annotated with
17// __attribute__((objc_allow_direct_instance_variable_assignment"))). This
18// annotation serves as a false positive suppression mechanism for the
19// checker. The annotation is allowed on properties and Ivars.
20//
21//===----------------------------------------------------------------------===//
22
24#include "clang/AST/Attr.h"
25#include "clang/AST/DeclObjC.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 which
38/// the check should not be performed.
39///
40/// Checks for the init, dealloc, and any other functions that might be allowed
41/// to perform direct instance variable assignment based on their name.
42static bool DefaultMethodFilter(const ObjCMethodDecl *M) {
43 return M->getMethodFamily() == OMF_init ||
45 M->getMethodFamily() == OMF_copy ||
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 ivars
58 /// 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;
66
67 public:
68 MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,
69 const ObjCInterfaceDecl *InID, BugReporter &InBR,
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.
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()) {
142
143 if ((*ShouldSkipMethod)(M))
144 continue;
145
146 const Stmt *Body = M->getBody();
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 with
182 // objc_allow_direct_instance_variable_assignment. This annotation serves
183 // as a false positive suppression mechanism for the checker. The
184 // 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
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 annotated
211// 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>();
224 "AnnotatedFunctions"))
225 Chk->ShouldSkipMethod = &AttrFilter;
226}
227
228bool ento::shouldRegisterDirectIvarAssignment(const CheckerManager &mgr) {
229 return true;
230}
static bool AttrFilter(const ObjCMethodDecl *M)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
AnalysisDeclContext contains the context data for the function, method or block under analysis.
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3819
Expr * getLHS() const
Definition: Expr.h:3868
static bool isAssignmentOp(Opcode Opc)
Definition: Expr.h:3954
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:194
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:83
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
Definition: DeclBase.h:542
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:3060
One of these records is kept for each identifier that is lexed.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:268
instmeth_range instance_methods() const
Definition: DeclObjC.h:1026
instprop_range instance_properties() const
Definition: DeclObjC.h:975
ObjCMethodDecl * getInstanceMethod(Selector Sel, bool AllowHidden=false) const
Definition: DeclObjC.h:1059
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2472
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2584
Represents an ObjC class declaration.
Definition: DeclObjC.h:1147
ObjCIvarDecl * lookupInstanceVariable(IdentifierInfo *IVarName, ObjCInterfaceDecl *&ClassDeclared)
Definition: DeclObjC.cpp:638
ObjCIvarDecl - Represents an ObjC instance variable.
Definition: DeclObjC.h:1939
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:548
ObjCIvarDecl * getDecl()
Definition: ExprObjC.h:576
ObjCMethodDecl - Represents an instance or class method declaration.
Definition: DeclObjC.h:138
Stmt * getBody() const override
Retrieve the body of this method, if it has one.
Definition: DeclObjC.cpp:909
ObjCMethodDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition: DeclObjC.cpp:1012
bool isSynthesizedAccessorStub() const
Definition: DeclObjC.h:446
Selector getSelector() const
Definition: DeclObjC.h:329
ObjCMethodFamily getMethodFamily() const
Determines the family of this method.
Definition: DeclObjC.cpp:1053
Represents one property declaration in an Objective-C interface.
Definition: DeclObjC.h:729
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:917
Selector getSetterName() const
Definition: DeclObjC.h:886
Selector getGetterName() const
Definition: DeclObjC.h:878
IdentifierInfo * getDefaultSynthIvarName(ASTContext &Ctx) const
Get the default name of the synthesized ivar.
Definition: DeclObjC.cpp:226
StringRef getNameForSlot(unsigned argIndex) const
Retrieve the name at a given position in the selector.
Stmt - This represents one statement.
Definition: Stmt.h:72
ASTContext & getASTContext() override
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
const SourceManager & getSourceManager()
Definition: BugReporter.h:616
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=std::nullopt, ArrayRef< FixItHint > Fixits=std::nullopt)
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
const char *const CoreFoundationObjectiveC
llvm::PointerUnion< const LocationContext *, AnalysisDeclContext * > LocationOrAnalysisDeclContext
@ OMF_mutableCopy
#define bool
Definition: stdbool.h:20