1 | #include "../clang-tidy/utils/DeclRefExprUtils.h" |
2 | #include "ClangTidyDiagnosticConsumer.h" |
3 | #include "ClangTidyTest.h" |
4 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
5 | #include "clang/ASTMatchers/ASTMatchers.h" |
6 | #include "clang/Tooling/Tooling.h" |
7 | #include "gtest/gtest.h" |
8 | |
9 | namespace clang { |
10 | namespace tidy { |
11 | |
12 | namespace { |
13 | using namespace clang::ast_matchers; |
14 | |
15 | template <int Indirections> |
16 | class ConstReferenceDeclRefExprsTransform : public ClangTidyCheck { |
17 | public: |
18 | ConstReferenceDeclRefExprsTransform(StringRef CheckName, |
19 | ClangTidyContext *Context) |
20 | : ClangTidyCheck(CheckName, Context) {} |
21 | |
22 | void registerMatchers(MatchFinder *Finder) override { |
23 | Finder->addMatcher(varDecl(hasName(Name: "target" )).bind(ID: "var" ), this); |
24 | } |
25 | |
26 | void check(const MatchFinder::MatchResult &Result) override { |
27 | const auto *D = Result.Nodes.getNodeAs<VarDecl>(ID: "var" ); |
28 | using utils::decl_ref_expr::constReferenceDeclRefExprs; |
29 | const auto const_decrefexprs = constReferenceDeclRefExprs( |
30 | *D, *cast<FunctionDecl>(D->getDeclContext())->getBody(), |
31 | *Result.Context, Indirections); |
32 | |
33 | for (const DeclRefExpr *const Expr : const_decrefexprs) { |
34 | assert(Expr); |
35 | diag(Expr->getBeginLoc(), "const usage" ) |
36 | << FixItHint::CreateInsertion(Expr->getBeginLoc(), "/*const*/" ); |
37 | } |
38 | } |
39 | }; |
40 | } // namespace |
41 | |
42 | namespace test { |
43 | |
44 | template <int Indirections> void RunTest(StringRef Snippet) { |
45 | |
46 | StringRef CommonCode = R"( |
47 | struct ConstTag{}; |
48 | struct NonConstTag{}; |
49 | |
50 | struct S { |
51 | void constMethod() const; |
52 | void nonConstMethod(); |
53 | |
54 | static void staticMethod(); |
55 | |
56 | void operator()(ConstTag) const; |
57 | void operator()(NonConstTag); |
58 | |
59 | void operator[](int); |
60 | void operator[](int) const; |
61 | |
62 | bool operator==(const S&) const; |
63 | |
64 | int int_member; |
65 | // We consider a mutation of the `*ptr_member` to be a const use of |
66 | // `*this`. This is consistent with the semantics of `const`-qualified |
67 | // methods, which prevent modifying `ptr_member` but not `*ptr_member`. |
68 | int* ptr_member; |
69 | |
70 | }; |
71 | |
72 | struct Derived : public S { |
73 | |
74 | }; |
75 | |
76 | void useVal(S); |
77 | void useRef(S&); |
78 | void usePtr(S*); |
79 | void usePtrPtr(S**); |
80 | void usePtrConstPtr(S* const*); |
81 | void useConstRef(const S&); |
82 | void useConstPtr(const S*); |
83 | void useConstPtrRef(const S*&); |
84 | void useConstPtrPtr(const S**); |
85 | void useConstPtrConstRef(const S* const&); |
86 | void useConstPtrConstPtr(const S* const*); |
87 | |
88 | void useInt(int); |
89 | void useIntRef(int&); |
90 | void useIntConstRef(const int&); |
91 | void useIntPtr(int*); |
92 | void useIntConstPtr(const int*); |
93 | |
94 | )" ; |
95 | |
96 | std::string Code = (CommonCode + Snippet).str(); |
97 | |
98 | llvm::SmallVector<StringRef, 1> Parts; |
99 | StringRef(Code).split(A&: Parts, Separator: "/*const*/" ); |
100 | |
101 | EXPECT_EQ(Code, |
102 | runCheckOnCode<ConstReferenceDeclRefExprsTransform<Indirections>>( |
103 | join(Parts, "" ))); |
104 | } |
105 | |
106 | TEST(ConstReferenceDeclRefExprsTest, ConstValueVar) { |
107 | RunTest<0>(Snippet: R"( |
108 | void f(const S target) { |
109 | useVal(/*const*/target); |
110 | useConstRef(/*const*/target); |
111 | useConstPtr(&/*const*/target); |
112 | useConstPtrConstRef(&/*const*/target); |
113 | /*const*/target.constMethod(); |
114 | /*const*/target.staticMethod(); |
115 | /*const*/target(ConstTag{}); |
116 | /*const*/target[42]; |
117 | useConstRef((/*const*/target)); |
118 | (/*const*/target).constMethod(); |
119 | /*const*/target.staticMethod(); |
120 | (void)(/*const*/target == /*const*/target); |
121 | (void)/*const*/target; |
122 | (void)&/*const*/target; |
123 | (void)*&/*const*/target; |
124 | /*const*/target; |
125 | S copy1 = /*const*/target; |
126 | S copy2(/*const*/target); |
127 | /*const*/target.int_member; |
128 | useInt(/*const*/target.int_member); |
129 | useIntConstRef(/*const*/target.int_member); |
130 | useIntPtr(/*const*/target.ptr_member); |
131 | useIntConstPtr(&/*const*/target.int_member); |
132 | |
133 | const S& const_target_ref = /*const*/target; |
134 | const S* const_target_ptr = &/*const*/target; |
135 | } |
136 | )" ); |
137 | } |
138 | |
139 | TEST(ConstReferenceDeclRefExprsTest, ConstRefVar) { |
140 | RunTest<0>(Snippet: R"( |
141 | void f(const S& target) { |
142 | useVal(/*const*/target); |
143 | useConstRef(/*const*/target); |
144 | useConstPtr(&/*const*/target); |
145 | useConstPtrConstRef(&/*const*/target); |
146 | /*const*/target.constMethod(); |
147 | /*const*/target.staticMethod(); |
148 | /*const*/target(ConstTag{}); |
149 | /*const*/target[42]; |
150 | useConstRef((/*const*/target)); |
151 | (/*const*/target).constMethod(); |
152 | (void)(/*const*/target == /*const*/target); |
153 | (void)/*const*/target; |
154 | (void)&/*const*/target; |
155 | (void)*&/*const*/target; |
156 | /*const*/target; |
157 | S copy1 = /*const*/target; |
158 | S copy2(/*const*/target); |
159 | /*const*/target.int_member; |
160 | useInt(/*const*/target.int_member); |
161 | useIntConstRef(/*const*/target.int_member); |
162 | useIntPtr(/*const*/target.ptr_member); |
163 | useIntConstPtr(&/*const*/target.int_member); |
164 | |
165 | const S& const_target_ref = /*const*/target; |
166 | const S* const_target_ptr = &/*const*/target; |
167 | } |
168 | )" ); |
169 | } |
170 | |
171 | TEST(ConstReferenceDeclRefExprsTest, DEBUGREMOVEME) { |
172 | RunTest<0>(Snippet: R"( |
173 | void f(S target, const S& other) { |
174 | S* target_ptr = ⌖ |
175 | } |
176 | )" ); |
177 | } |
178 | |
179 | TEST(ConstReferenceDeclRefExprsTest, ValueVar) { |
180 | RunTest<0>(Snippet: R"( |
181 | void f(S target, const S& other) { |
182 | useConstRef(/*const*/target); |
183 | useVal(/*const*/target); |
184 | useConstPtr(&/*const*/target); |
185 | useConstPtrConstRef(&/*const*/target); |
186 | /*const*/target.constMethod(); |
187 | /*const*/target.staticMethod(); |
188 | target.nonConstMethod(); |
189 | /*const*/target(ConstTag{}); |
190 | target[42]; |
191 | /*const*/target(ConstTag{}); |
192 | target(NonConstTag{}); |
193 | useRef(target); |
194 | usePtr(&target); |
195 | useConstRef((/*const*/target)); |
196 | (/*const*/target).constMethod(); |
197 | (void)(/*const*/target == /*const*/target); |
198 | (void)(/*const*/target == other); |
199 | (void)/*const*/target; |
200 | (void)&/*const*/target; |
201 | (void)*&/*const*/target; |
202 | /*const*/target; |
203 | S copy1 = /*const*/target; |
204 | S copy2(/*const*/target); |
205 | /*const*/target.int_member; |
206 | useInt(/*const*/target.int_member); |
207 | useIntConstRef(/*const*/target.int_member); |
208 | useIntPtr(/*const*/target.ptr_member); |
209 | useIntConstPtr(&/*const*/target.int_member); |
210 | |
211 | const S& const_target_ref = /*const*/target; |
212 | const S* const_target_ptr = &/*const*/target; |
213 | S* target_ptr = ⌖ |
214 | } |
215 | )" ); |
216 | } |
217 | |
218 | TEST(ConstReferenceDeclRefExprsTest, RefVar) { |
219 | RunTest<0>(Snippet: R"( |
220 | void f(S& target) { |
221 | useVal(/*const*/target); |
222 | usePtr(&target); |
223 | useConstRef(/*const*/target); |
224 | useConstPtr(&/*const*/target); |
225 | useConstPtrConstRef(&/*const*/target); |
226 | /*const*/target.constMethod(); |
227 | /*const*/target.staticMethod(); |
228 | target.nonConstMethod(); |
229 | /*const*/target(ConstTag{}); |
230 | target[42]; |
231 | useConstRef((/*const*/target)); |
232 | (/*const*/target).constMethod(); |
233 | (void)(/*const*/target == /*const*/target); |
234 | (void)/*const*/target; |
235 | (void)&/*const*/target; |
236 | (void)*&/*const*/target; |
237 | /*const*/target; |
238 | S copy1 = /*const*/target; |
239 | S copy2(/*const*/target); |
240 | /*const*/target.int_member; |
241 | useInt(/*const*/target.int_member); |
242 | useIntConstRef(/*const*/target.int_member); |
243 | useIntPtr(/*const*/target.ptr_member); |
244 | useIntConstPtr(&/*const*/target.int_member); |
245 | |
246 | (void)(&/*const*/target)->int_member; |
247 | useIntRef((&target)->int_member); |
248 | |
249 | const S& const_target_ref = /*const*/target; |
250 | const S* const_target_ptr = &/*const*/target; |
251 | S* target_ptr = ⌖ |
252 | } |
253 | )" ); |
254 | } |
255 | |
256 | TEST(ConstReferenceDeclRefExprsTest, PtrVar) { |
257 | RunTest<1>(Snippet: R"( |
258 | void f(S* target) { |
259 | useVal(*/*const*/target); |
260 | usePtr(target); |
261 | useConstRef(*/*const*/target); |
262 | useConstPtr(/*const*/target); |
263 | useConstPtrConstRef(/*const*/target); |
264 | usePtrConstPtr(&target); |
265 | /*const*/target->constMethod(); |
266 | /*const*/target->staticMethod(); |
267 | target->nonConstMethod(); |
268 | (*/*const*/target)(ConstTag{}); |
269 | (*target)[42]; |
270 | target->operator[](42); |
271 | useConstRef((*/*const*/target)); |
272 | (/*const*/target)->constMethod(); |
273 | (void)(*/*const*/target == */*const*/target); |
274 | (void)*/*const*/target; |
275 | (void)/*const*/target; |
276 | /*const*/target; |
277 | S copy1 = */*const*/target; |
278 | S copy2(*/*const*/target); |
279 | /*const*/target->int_member; |
280 | useInt(/*const*/target->int_member); |
281 | useIntConstRef(/*const*/target->int_member); |
282 | useIntPtr(/*const*/target->ptr_member); |
283 | useIntConstPtr(&/*const*/target->int_member); |
284 | |
285 | const S& const_target_ref = */*const*/target; |
286 | const S* const_target_ptr = /*const*/target; |
287 | S* target_ptr = target; // FIXME: we could chect const usage of `target_ptr`. |
288 | } |
289 | )" ); |
290 | } |
291 | |
292 | TEST(ConstReferenceDeclRefExprsTest, ConstPtrVar) { |
293 | RunTest<1>(Snippet: R"( |
294 | void f(const S* target) { |
295 | useVal(*/*const*/target); |
296 | useConstRef(*/*const*/target); |
297 | useConstPtr(/*const*/target); |
298 | useConstPtrRef(/*const*/target); |
299 | useConstPtrPtr(&/*const*/target); |
300 | useConstPtrConstPtr(&/*const*/target); |
301 | useConstPtrConstRef(/*const*/target); |
302 | /*const*/target->constMethod(); |
303 | /*const*/target->staticMethod(); |
304 | (*/*const*/target)(ConstTag{}); |
305 | (*/*const*/target)[42]; |
306 | /*const*/target->operator[](42); |
307 | (void)(*/*const*/target == */*const*/target); |
308 | (void)/*const*/target; |
309 | (void)*/*const*/target; |
310 | /*const*/target; |
311 | if(/*const*/target) {} |
312 | S copy1 = */*const*/target; |
313 | S copy2(*/*const*/target); |
314 | /*const*/target->int_member; |
315 | useInt(/*const*/target->int_member); |
316 | useIntConstRef(/*const*/target->int_member); |
317 | useIntPtr(/*const*/target->ptr_member); |
318 | useIntConstPtr(&/*const*/target->int_member); |
319 | |
320 | const S& const_target_ref = */*const*/target; |
321 | const S* const_target_ptr = /*const*/target; |
322 | } |
323 | )" ); |
324 | } |
325 | |
326 | TEST(ConstReferenceDeclRefExprsTest, ConstPtrPtrVar) { |
327 | RunTest<2>(Snippet: R"( |
328 | void f(const S** target) { |
329 | useVal(**/*const*/target); |
330 | useConstRef(**/*const*/target); |
331 | useConstPtr(*/*const*/target); |
332 | useConstPtrRef(*/*const*/target); |
333 | useConstPtrPtr(/*const*/target); |
334 | useConstPtrConstPtr(/*const*/target); |
335 | useConstPtrConstRef(*/*const*/target); |
336 | (void)/*const*/target; |
337 | (void)*/*const*/target; |
338 | (void)**/*const*/target; |
339 | /*const*/target; |
340 | if(/*const*/target) {} |
341 | if(*/*const*/target) {} |
342 | S copy1 = **/*const*/target; |
343 | S copy2(**/*const*/target); |
344 | (*/*const*/target)->int_member; |
345 | useInt((*/*const*/target)->int_member); |
346 | useIntConstRef((*/*const*/target)->int_member); |
347 | useIntPtr((*/*const*/target)->ptr_member); |
348 | useIntConstPtr(&(*/*const*/target)->int_member); |
349 | |
350 | const S& const_target_ref = **/*const*/target; |
351 | const S* const_target_ptr = */*const*/target; |
352 | } |
353 | )" ); |
354 | } |
355 | |
356 | TEST(ConstReferenceDeclRefExprsTest, ConstPtrConstPtrVar) { |
357 | RunTest<2>(Snippet: R"( |
358 | void f(const S* const* target) { |
359 | useVal(**/*const*/target); |
360 | useConstRef(**/*const*/target); |
361 | useConstPtr(*/*const*/target); |
362 | useConstPtrConstPtr(/*const*/target); |
363 | useConstPtrConstRef(*/*const*/target); |
364 | (void)/*const*/target; |
365 | (void)*/*const*/target; |
366 | (void)**/*const*/target; |
367 | /*const*/target; |
368 | if(/*const*/target) {} |
369 | if(*/*const*/target) {} |
370 | S copy1 = **/*const*/target; |
371 | S copy2(**/*const*/target); |
372 | (*/*const*/target)->int_member; |
373 | useInt((*/*const*/target)->int_member); |
374 | useIntConstRef((*/*const*/target)->int_member); |
375 | useIntPtr((*/*const*/target)->ptr_member); |
376 | useIntConstPtr(&(*/*const*/target)->int_member); |
377 | |
378 | const S& const_target_ref = **/*const*/target; |
379 | const S* const_target_ptr = */*const*/target; |
380 | } |
381 | )" ); |
382 | } |
383 | |
384 | } // namespace test |
385 | } // namespace tidy |
386 | } // namespace clang |
387 | |