/
ObjCAssignIvarOutsideAccessorsRule.cpp
161 lines (133 loc) · 4.17 KB
/
ObjCAssignIvarOutsideAccessorsRule.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include "oclint/AbstractASTVisitorRule.h"
#include "oclint/RuleSet.h"
#include "oclint/util/StdUtil.h"
using namespace std;
using namespace clang;
using namespace oclint;
// Traversal for ivar accesses
class ContainsIvarFetch : public RecursiveASTVisitor<ContainsIvarFetch>
{
public:
// Location to save found ivar accesses
vector<ObjCIvarRefExpr*> _instances;
bool VisitObjCIvarRefExpr(ObjCIvarRefExpr* ivarRef) {
_instances.push_back(ivarRef);
return true;
}
vector<ObjCIvarRefExpr*>& getInstances() {
return _instances;
}
};
// Look for assignment operators. Then collect ivar accesses inside
class ContainsBinaryOperatorWithIvarAssignment :
public RecursiveASTVisitor<ContainsBinaryOperatorWithIvarAssignment>
{
private:
// Location to save found ivar accesses
vector<ObjCIvarRefExpr*> _instances;
public:
bool VisitBinaryOperator(BinaryOperator *binaryOperator)
{
// Found an operator
Expr *leftExpr = binaryOperator->getLHS();
if (binaryOperator->isAssignmentOp())
{
// So check for ivar references in the assignment part
ContainsIvarFetch checker;
checker.TraverseStmt(leftExpr);
_instances.insert(
_instances.end(),
checker.getInstances().begin(),
checker.getInstances().end()
);
}
return true;
}
vector<ObjCIvarRefExpr*>& getInstances() {
return _instances;
}
};
// Main rule implementation.
// Looks for methods then finds accesses in each method and sees if they're okay
class ObjCAssignIvarOutsideAccessorsRule:
public AbstractASTVisitorRule<ObjCAssignIvarOutsideAccessorsRule>
{
public:
virtual const string name() const override
{
return "ivar assignment outside accessors or init";
}
virtual const string identifier() const override
{
return "AssignIvarOutsideAccessors";
}
virtual int priority() const override
{
return 2;
}
virtual const string category() const override
{
return "convention";
}
#ifdef DOCGEN
virtual const std::string since() const override
{
return "0.8";
}
virtual const std::string description() const override
{
return "This rule prevents assigning an ivar outside of "
"getters, setters, and ``init`` method.";
}
virtual const std::string fileName() const override
{
return "ObjCAssignIvarOutsideAccessorsRule.cpp";
}
virtual const std::string example() const override
{
return R"rst(
.. code-block:: objective-c
@interface Foo : NSObject
{
int _bar;
}
@property (assign, nonatomic) int bar;
@end
@implementation Foo
@synthesize bar = _bar;
- (void)doSomething {
_bar = 3; // access _bar outside its getter, setter or init
}
@end
)rst";
}
#endif
bool VisitObjCMethodDecl(ObjCMethodDecl* decl) {
// Save the method name
string selectorName = decl->getSelector().getAsString();
// Then traverse down with a separate checker
// to find the actual accesses
ContainsBinaryOperatorWithIvarAssignment checker;
checker.TraverseDecl(decl);
// Now go through all the ivar accesses and see
// if they match the method name as a getter, setter, or init method
for (auto& access : checker.getInstances())
{
// ivarName is _foo or foo or foo_
string ivarName = access->getDecl()->getNameAsString();
// getterName is foo. Note this won't work for
// properties that reassign getter= or setter=
string getterName = removeUnderscores(ivarName);
// setterName is setFoo
string setterName = "set" + capitalizeFirstLetter(getterName) + ":";
if((selectorName != getterName
&& selectorName != setterName)
&& selectorName.substr(0, 4) != "init") {
addViolation(access, this);
}
}
return true;
}
};
// Instantiate the rule
static RuleSet rules(new ObjCAssignIvarOutsideAccessorsRule());